aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authoramankumarsingh77 <amankumarsingh7702@gmal.com>2026-05-02 16:20:09 +0530
committeramankumarsingh77 <amankumarsingh7702@gmal.com>2026-05-02 16:20:09 +0530
commit6708da54b1805756908dea84bf65fe73ab03426a (patch)
tree95e144b83d69af9918321f2e3faf29c5a8bfad17 /src/util
parent9ead6a0577b750bc3ae3f36a7e8bfe108e979925 (diff)
feat(domains): migrate screenshot, add typed sharp shim, restore click crosshair overlay
Diffstat (limited to 'src/util')
-rw-r--r--src/util/sharp-shim.ts49
1 files changed, 49 insertions, 0 deletions
diff --git a/src/util/sharp-shim.ts b/src/util/sharp-shim.ts
new file mode 100644
index 0000000..7ea409d
--- /dev/null
+++ b/src/util/sharp-shim.ts
@@ -0,0 +1,49 @@
+// Typed minimal shim for the optional `sharp` dependency. Sharp is a heavy
+// native module not installed in every workspace; we import dynamically and
+// distinguish "missing" (no install) from "error" (installed but threw).
+//
+// We declare only the surface we use. The dynamic import returns `unknown`
+// at the boundary; the cast inside loadSharp() is the documented escape
+// hatch — every other consumer sees the typed SharpFactory.
+
+export type SharpInstance = {
+ metadata(): Promise<{ width?: number; height?: number }>;
+ composite(layers: ReadonlyArray<{ input: Buffer; top: number; left: number }>): SharpInstance;
+ resize(width: number, height: number, opts?: { fit?: "inside" }): SharpInstance;
+ toFile(path: string): Promise<unknown>;
+};
+
+export type SharpFactory = (input: string | Buffer) => SharpInstance;
+
+export type SharpLoad =
+ | { readonly kind: "ok"; readonly sharp: SharpFactory }
+ | { readonly kind: "missing" }
+ | { readonly kind: "error"; readonly message: string };
+
+export const loadSharp = async (): Promise<SharpLoad> => {
+ let mod: unknown;
+ try {
+ // sharp is an optional peer dependency; it may not be installed.
+ // @ts-expect-error sharp is an optional peer dependency not required at compile time
+ mod = await import("sharp");
+ } catch (e) {
+ const msg = e instanceof Error ? e.message : String(e);
+ if (
+ msg.includes("Cannot find module") ||
+ msg.includes("MODULE_NOT_FOUND") ||
+ msg.includes("ERR_MODULE_NOT_FOUND")
+ ) {
+ return { kind: "missing" };
+ }
+ return { kind: "error", message: msg };
+ }
+ if (mod === null || mod === undefined) return { kind: "missing" };
+ // The CommonJS sharp module's default export IS the factory. ESM-wrapped
+ // sharp puts it on .default. Probe both.
+ const m = mod as { default?: unknown };
+ const candidate: unknown = typeof m.default === "function" ? m.default : mod;
+ if (typeof candidate !== "function") {
+ return { kind: "error", message: "sharp module did not expose a callable factory" };
+ }
+ return { kind: "ok", sharp: candidate as SharpFactory };
+};