@nifrajs/image 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The pure attribute builder shared by every adapter `<Image>` — turns image props into CLS-safe,
3
+ * responsive `<img>` attributes (`src`/`srcSet`/`sizes`/`width`/`height`/`loading`/`decoding`/
4
+ * `fetchpriority`). The actual resize is delegated to an {@link ImageLoader} (a URL builder) — nifra
5
+ * bundles no codec; point the loader at your image CDN.
6
+ */
7
+ /** Builds a variant URL for `src` at a target pixel `width` (and optional `quality`). */
8
+ export type ImageLoader = (args: {
9
+ src: string;
10
+ width: number;
11
+ quality?: number;
12
+ }) => string;
13
+ /** Default loader: return the source unchanged (no transform). Use when there's no image CDN — you
14
+ * still get CLS-safe sizing + lazy loading, just no responsive variants. */
15
+ export declare const identityLoader: ImageLoader;
16
+ export interface CloudflareLoaderOptions {
17
+ /** Prefix for the source (e.g. an absolute origin) when `src` is a bare path. Default: none. */
18
+ readonly base?: string;
19
+ }
20
+ /**
21
+ * Cloudflare Images loader — builds `/cdn-cgi/image/<options>/<source>` URLs that the Cloudflare edge
22
+ * resizes on the fly (also emits `format=auto` for webp/avif negotiation). Works on Cloudflare Pages /
23
+ * Workers with Images enabled.
24
+ */
25
+ export declare function cloudflareLoader(options?: CloudflareLoaderOptions): ImageLoader;
26
+ export interface SelfHostedLoaderOptions {
27
+ /** Path/URL where `createImageHandler` (`@nifrajs/image/server`) is mounted, e.g. `"/_image"`. */
28
+ readonly endpoint: string;
29
+ /**
30
+ * HMAC secret for **signed URLs**. When set, each URL gets a stable `&s=` signature and the handler
31
+ * must be configured with the SAME `signing.secret` — it then rejects any unsigned/forged `(src, w, q)`,
32
+ * shutting down resize-bombing. ⚠️ The signer holds the secret, so a loader created with it is
33
+ * **server-only** — inject it like a session secret (from `env`), never import this config into a
34
+ * route/client module. Signatures are stable (no expiry), so SSR-signed URLs hydrate + cache identically.
35
+ */
36
+ readonly secret?: string;
37
+ }
38
+ /**
39
+ * Loader for nifra's **self-hosted** resize endpoint (`createImageHandler` from `@nifrajs/image/server`,
40
+ * backed by `Bun.Image`/sharp/WASM). Builds `<endpoint>?src=…&w=…[&q=…][&s=…]` (the endpoint negotiates
41
+ * the output format). Pure + dependency-free. For runtimes without a native codec, pair the endpoint
42
+ * with `wasmImageBackend`, or use the CDN `cloudflareLoader` instead.
43
+ */
44
+ export declare function selfHostedLoader(options: SelfHostedLoaderOptions): ImageLoader;
45
+ export interface SignImageUrlOptions {
46
+ /** HMAC secret — must match the handler's `signing.secret`. */
47
+ readonly secret: string;
48
+ /** Seconds until the URL expires (adds `&exp=`). Omit for a stable, cacheable-forever signed URL. */
49
+ readonly expiresIn?: number;
50
+ }
51
+ /**
52
+ * Mint a **signed** self-hosted image URL on the server — for cases the (stable) `selfHostedLoader`
53
+ * doesn't cover, chiefly **time-limited** access (`expiresIn`) to private images. Server-only (it holds
54
+ * the secret). Pair with a passthrough loader, or use the signed string as a plain `src`.
55
+ *
56
+ * ```ts
57
+ * const url = signImageUrl("/_image", { src: "/private/a.jpg", width: 800 }, { secret, expiresIn: 300 })
58
+ * ```
59
+ */
60
+ export declare function signImageUrl(endpoint: string, image: {
61
+ src: string;
62
+ width: number;
63
+ quality?: number;
64
+ }, options: SignImageUrlOptions): string;
65
+ export interface ImageProps {
66
+ readonly src: string;
67
+ /** Intrinsic width (px) — **required**, reserves layout space (no CLS). */
68
+ readonly width: number;
69
+ /** Intrinsic height (px) — **required** (no CLS). */
70
+ readonly height: number;
71
+ /** Alt text — **required** for accessibility (use `alt=""` for decorative images). */
72
+ readonly alt: string;
73
+ /** `sizes` attribute for responsive selection (e.g. `"(max-width: 600px) 100vw, 600px"`). */
74
+ readonly sizes?: string;
75
+ /** Widths to generate `srcSet` entries for. Default: `[width, width*2]` (1×/2× retina). */
76
+ readonly widths?: readonly number[];
77
+ /** Quality passed to the loader. */
78
+ readonly quality?: number;
79
+ /** `lazy` (default) or `eager`. `priority` overrides this to eager. */
80
+ readonly loading?: "lazy" | "eager";
81
+ /** Mark the LCP image: `loading="eager"` + `fetchpriority="high"`. */
82
+ readonly priority?: boolean;
83
+ }
84
+ export interface ResolvedImage {
85
+ readonly src: string;
86
+ readonly srcSet?: string;
87
+ readonly sizes?: string;
88
+ readonly width: number;
89
+ readonly height: number;
90
+ readonly alt: string;
91
+ readonly loading: "lazy" | "eager";
92
+ readonly decoding: "async";
93
+ readonly fetchPriority?: "high";
94
+ }
95
+ /**
96
+ * Resolve {@link ImageProps} + an {@link ImageLoader} into `<img>` attributes. CLS-safe (`width`/
97
+ * `height` required + > 0, else a dev error), lazy + async-decoding by default, with a responsive
98
+ * `srcSet` built from `widths` via the loader. If every width produces the same URL (e.g.
99
+ * {@link identityLoader}), `srcSet` is omitted (it'd be redundant).
100
+ */
101
+ export declare function resolveImage(props: ImageProps, loader?: ImageLoader): ResolvedImage;
102
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,yFAAyF;AACzF,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CAAA;AAE5F;4EAC4E;AAC5E,eAAO,MAAM,cAAc,EAAE,WAA8B,CAAA;AAE3D,MAAM,WAAW,uBAAuB;IACtC,gGAAgG;IAChG,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,WAAW,CAOnF;AAED,MAAM,WAAW,uBAAuB;IACtC,kGAAkG;IAClG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,WAAW,CAU9E;AAED,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,qGAAqG;IACrG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EACvD,OAAO,EAAE,mBAAmB,GAC3B,MAAM,CAYR;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,sFAAsF;IACtF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,oCAAoC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,uEAAuE;IACvE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACnC,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,UAAU,EACjB,MAAM,GAAE,WAA4B,GACnC,aAAa,CAsCf"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The pure attribute builder shared by every adapter `<Image>` — turns image props into CLS-safe,
3
+ * responsive `<img>` attributes (`src`/`srcSet`/`sizes`/`width`/`height`/`loading`/`decoding`/
4
+ * `fetchpriority`). The actual resize is delegated to an {@link ImageLoader} (a URL builder) — nifra
5
+ * bundles no codec; point the loader at your image CDN.
6
+ */
7
+ import { signImageParams } from "./sign.js";
8
+ /** Default loader: return the source unchanged (no transform). Use when there's no image CDN — you
9
+ * still get CLS-safe sizing + lazy loading, just no responsive variants. */
10
+ export const identityLoader = ({ src }) => src;
11
+ /**
12
+ * Cloudflare Images loader — builds `/cdn-cgi/image/<options>/<source>` URLs that the Cloudflare edge
13
+ * resizes on the fly (also emits `format=auto` for webp/avif negotiation). Works on Cloudflare Pages /
14
+ * Workers with Images enabled.
15
+ */
16
+ export function cloudflareLoader(options = {}) {
17
+ const base = options.base ?? "";
18
+ return ({ src, width, quality }) => {
19
+ const params = `format=auto,width=${width}${quality !== undefined ? `,quality=${quality}` : ""}`;
20
+ const source = `${base}${src}`.replace(/^\//, ""); // the leading slash is provided by the prefix below
21
+ return `/cdn-cgi/image/${params}/${source}`;
22
+ };
23
+ }
24
+ /**
25
+ * Loader for nifra's **self-hosted** resize endpoint (`createImageHandler` from `@nifrajs/image/server`,
26
+ * backed by `Bun.Image`/sharp/WASM). Builds `<endpoint>?src=…&w=…[&q=…][&s=…]` (the endpoint negotiates
27
+ * the output format). Pure + dependency-free. For runtimes without a native codec, pair the endpoint
28
+ * with `wasmImageBackend`, or use the CDN `cloudflareLoader` instead.
29
+ */
30
+ export function selfHostedLoader(options) {
31
+ const { endpoint, secret } = options;
32
+ return ({ src, width, quality }) => {
33
+ const w = String(width);
34
+ const q = quality !== undefined ? String(quality) : undefined;
35
+ const params = new URLSearchParams({ src, w });
36
+ if (q !== undefined)
37
+ params.set("q", q);
38
+ if (secret !== undefined)
39
+ params.set("s", signImageParams(secret, { src, w, q }));
40
+ return `${endpoint}?${params.toString()}`;
41
+ };
42
+ }
43
+ /**
44
+ * Mint a **signed** self-hosted image URL on the server — for cases the (stable) `selfHostedLoader`
45
+ * doesn't cover, chiefly **time-limited** access (`expiresIn`) to private images. Server-only (it holds
46
+ * the secret). Pair with a passthrough loader, or use the signed string as a plain `src`.
47
+ *
48
+ * ```ts
49
+ * const url = signImageUrl("/_image", { src: "/private/a.jpg", width: 800 }, { secret, expiresIn: 300 })
50
+ * ```
51
+ */
52
+ export function signImageUrl(endpoint, image, options) {
53
+ const w = String(image.width);
54
+ const q = image.quality !== undefined ? String(image.quality) : undefined;
55
+ const exp = options.expiresIn !== undefined
56
+ ? String(Math.floor(Date.now() / 1000) + options.expiresIn)
57
+ : undefined;
58
+ const params = new URLSearchParams({ src: image.src, w });
59
+ if (q !== undefined)
60
+ params.set("q", q);
61
+ if (exp !== undefined)
62
+ params.set("exp", exp);
63
+ params.set("s", signImageParams(options.secret, { src: image.src, w, q, exp }));
64
+ return `${endpoint}?${params.toString()}`;
65
+ }
66
+ /**
67
+ * Resolve {@link ImageProps} + an {@link ImageLoader} into `<img>` attributes. CLS-safe (`width`/
68
+ * `height` required + > 0, else a dev error), lazy + async-decoding by default, with a responsive
69
+ * `srcSet` built from `widths` via the loader. If every width produces the same URL (e.g.
70
+ * {@link identityLoader}), `srcSet` is omitted (it'd be redundant).
71
+ */
72
+ export function resolveImage(props, loader = identityLoader) {
73
+ if (!Number.isFinite(props.width) ||
74
+ props.width <= 0 ||
75
+ !Number.isFinite(props.height) ||
76
+ props.height <= 0) {
77
+ throw new Error(`[nifra/image] <Image> requires positive width + height (got ${props.width}×${props.height}) — they reserve layout space to prevent CLS.`);
78
+ }
79
+ const eager = props.priority === true || props.loading === "eager";
80
+ const widths = [...new Set(props.widths ?? [props.width, props.width * 2])].sort((a, b) => a - b);
81
+ // Build the loader args, omitting `quality` when unset (exactOptionalPropertyTypes: no `undefined`).
82
+ const at = (w) => loader(props.quality === undefined
83
+ ? { src: props.src, width: w }
84
+ : { src: props.src, width: w, quality: props.quality });
85
+ const entries = widths.map((w) => ({ url: at(w), w }));
86
+ const allSame = entries.every((e) => e.url === entries[0]?.url);
87
+ const srcSet = allSame ? undefined : entries.map((e) => `${e.url} ${e.w}w`).join(", ");
88
+ // `src` is the 1× (intrinsic-width) variant — the fallback for browsers ignoring srcSet.
89
+ const base = {
90
+ src: at(props.width),
91
+ width: props.width,
92
+ height: props.height,
93
+ alt: props.alt,
94
+ loading: eager ? "eager" : "lazy",
95
+ decoding: "async",
96
+ ...(srcSet !== undefined ? { srcSet } : {}),
97
+ ...(props.sizes !== undefined ? { sizes: props.sizes } : {}),
98
+ ...(props.priority === true ? { fetchPriority: "high" } : {}),
99
+ };
100
+ return base;
101
+ }
102
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * `@nifrajs/image/server` — nifra's **self-hosted** image resize endpoint. `createImageHandler` returns a
3
+ * `(req: Request) => Promise<Response>` you mount at the path your {@link selfHostedLoader} points to
4
+ * (e.g. `"/_image"`). It validates the query, safely resolves the source (local file under a `root`, or
5
+ * a remote URL on an `allowedOrigins` allowlist — both **fail-closed** against SSRF), then decodes →
6
+ * resizes (never upscaling) → re-encodes via a pluggable {@link ImageBackend}. The default
7
+ * {@link bunImageBackend} uses `Bun.Image` (libjpeg-turbo / libspng / libwebp, off-thread).
8
+ *
9
+ * This subpath touches the filesystem and a native codec, so it is for **Node/Bun servers**, not the
10
+ * edge. On Workers / Vercel-Edge / Deno-Deploy there is no native codec — use the CDN `cloudflareLoader`
11
+ * from `@nifrajs/image` instead. The dependency-free core (`@nifrajs/image`) never imports this module.
12
+ */
13
+ import { type ImageBackend } from "./backend.js";
14
+ export * from "./backend.js";
15
+ export interface ImageHandlerOptions {
16
+ /** Codec backend. Default: {@link bunImageBackend} (requires the Bun runtime). */
17
+ readonly backend?: ImageBackend;
18
+ /** Absolute directory that local (path) sources resolve under. Path-traversal- and symlink-guarded.
19
+ * Omit to **disable local sources** (every path source → 403). */
20
+ readonly root?: string;
21
+ /** Exact origins (`https://cdn.example`) allowed for remote sources. Omitted/empty ⇒ **no remote
22
+ * sources** (fail-closed against SSRF). Only `http:`/`https:` URLs are ever considered. */
23
+ readonly allowedOrigins?: readonly string[];
24
+ /** Max bytes read from a source before rejecting (413). Default 20 MiB. */
25
+ readonly maxSourceBytes?: number;
26
+ /** Max source pixels (w×h) before rejecting (413) — decompression-bomb guard. Default 40 MP. */
27
+ readonly maxSourcePixels?: number;
28
+ /** Hard cap on the requested width; larger `?w` is clamped down. Default 3840. */
29
+ readonly maxWidth?: number;
30
+ /** Max concurrent transforms (codec work is CPU/memory-heavy). Excess requests queue. Default 4. */
31
+ readonly concurrency?: number;
32
+ /** `Cache-Control: public, max-age=<n>, immutable` seconds. Default 1 year. */
33
+ readonly cacheMaxAge?: number;
34
+ /** Quality used when `?q` is absent. Default 75. */
35
+ readonly defaultQuality?: number;
36
+ /** Remote-fetch timeout (ms). Default 10 000. */
37
+ readonly fetchTimeoutMs?: number;
38
+ /** `fetch` implementation for remote sources (injectable for custom timeouts/proxy and for tests).
39
+ * Default: the global `fetch`. */
40
+ readonly fetch?: typeof fetch;
41
+ /** Require **signed URLs**: every request must carry a valid `&s=` HMAC over `(src, w, q[, exp])` or
42
+ * it's rejected with `403`. Use the SAME `secret` as your `selfHostedLoader`/`signImageUrl`. This
43
+ * locks the endpoint to URLs your app minted — the defense against resize-bombing. */
44
+ readonly signing?: {
45
+ readonly secret: string;
46
+ };
47
+ }
48
+ /**
49
+ * Build the resize request handler. Mount its return value at the `selfHostedLoader` endpoint:
50
+ *
51
+ * ```ts
52
+ * const image = createImageHandler({ root: "./public", allowedOrigins: ["https://cdn.example"] })
53
+ * // inside your router: if (url.pathname === "/_image") return image(req)
54
+ * ```
55
+ */
56
+ export declare function createImageHandler(options?: ImageHandlerOptions): (req: Request) => Promise<Response>;
57
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAEL,KAAK,YAAY,EAGlB,MAAM,cAAc,CAAA;AAOrB,cAAc,cAAc,CAAA;AAE5B,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAA;IAC/B;sEACkE;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB;+FAC2F;IAC3F,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,gGAAgG;IAChG,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,iDAAiD;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC;sCACkC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IAC7B;;0FAEsF;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/C;AAoBD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,mBAAwB,GAChC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAGrC"}
package/dist/server.js ADDED
@@ -0,0 +1,330 @@
1
+ /**
2
+ * `@nifrajs/image/server` — nifra's **self-hosted** image resize endpoint. `createImageHandler` returns a
3
+ * `(req: Request) => Promise<Response>` you mount at the path your {@link selfHostedLoader} points to
4
+ * (e.g. `"/_image"`). It validates the query, safely resolves the source (local file under a `root`, or
5
+ * a remote URL on an `allowedOrigins` allowlist — both **fail-closed** against SSRF), then decodes →
6
+ * resizes (never upscaling) → re-encodes via a pluggable {@link ImageBackend}. The default
7
+ * {@link bunImageBackend} uses `Bun.Image` (libjpeg-turbo / libspng / libwebp, off-thread).
8
+ *
9
+ * This subpath touches the filesystem and a native codec, so it is for **Node/Bun servers**, not the
10
+ * edge. On Workers / Vercel-Edge / Deno-Deploy there is no native codec — use the CDN `cloudflareLoader`
11
+ * from `@nifrajs/image` instead. The dependency-free core (`@nifrajs/image`) never imports this module.
12
+ */
13
+ import { readFile, realpath, stat } from "node:fs/promises";
14
+ import { resolve as resolvePath, sep } from "node:path";
15
+ import { bunImageBackend, ImageProcessingError, } from "./backend.js";
16
+ import { verifyImageParams } from "./sign.js";
17
+ // The codec seam + every official backend (Bun/sharp/WASM) live in the edge-safe `./backend.ts`
18
+ // (`@nifrajs/image/backends`), importable WITHOUT this module's `node:fs` — so a WASM backend can ship to
19
+ // the edge. Re-export them so `@nifrajs/image/server` stays the single import for Node servers.
20
+ export * from "./backend.js";
21
+ const MAX_SRC_LEN = 2048;
22
+ /**
23
+ * Build the resize request handler. Mount its return value at the `selfHostedLoader` endpoint:
24
+ *
25
+ * ```ts
26
+ * const image = createImageHandler({ root: "./public", allowedOrigins: ["https://cdn.example"] })
27
+ * // inside your router: if (url.pathname === "/_image") return image(req)
28
+ * ```
29
+ */
30
+ export function createImageHandler(options = {}) {
31
+ const cfg = resolveConfig(options);
32
+ return (req) => handle(req, cfg);
33
+ }
34
+ function resolveConfig(options) {
35
+ const concurrency = Math.max(1, options.concurrency ?? 4);
36
+ const sem = createSemaphore(concurrency);
37
+ return {
38
+ backend: options.backend ?? bunImageBackend(),
39
+ root: options.root !== undefined ? resolvePath(options.root) : null,
40
+ allowedOrigins: new Set(options.allowedOrigins ?? []),
41
+ maxSourceBytes: options.maxSourceBytes ?? 20 * 1024 * 1024,
42
+ maxSourcePixels: options.maxSourcePixels ?? 40_000_000,
43
+ maxWidth: options.maxWidth ?? 3840,
44
+ cacheMaxAge: options.cacheMaxAge ?? 31_536_000,
45
+ defaultQuality: options.defaultQuality ?? 75,
46
+ fetchTimeoutMs: options.fetchTimeoutMs ?? 10_000,
47
+ fetchImpl: options.fetch ?? globalThis.fetch,
48
+ signing: options.signing ?? null,
49
+ acquire: sem.acquire,
50
+ release: sem.release,
51
+ };
52
+ }
53
+ async function handle(req, cfg) {
54
+ // 1. Method — only safe reads. (HEAD shares the GET path; the body is stripped at the end.)
55
+ if (req.method !== "GET" && req.method !== "HEAD") {
56
+ return errorResponse(405, "method_not_allowed", { Allow: "GET, HEAD" });
57
+ }
58
+ // 2. Validate the query at the trust boundary (strict scalar parsing — no Number() coercion).
59
+ const url = new URL(req.url);
60
+ const src = url.searchParams.get("src");
61
+ if (src === null || src.length === 0 || src.length > MAX_SRC_LEN) {
62
+ return errorResponse(400, "invalid_src");
63
+ }
64
+ const requestedWidth = parsePositiveInt(url.searchParams.get("w"));
65
+ if (requestedWidth === null)
66
+ return errorResponse(400, "invalid_width");
67
+ const qParam = url.searchParams.get("q");
68
+ let quality = cfg.defaultQuality;
69
+ if (qParam !== null) {
70
+ const q = parsePositiveInt(qParam);
71
+ if (q === null)
72
+ return errorResponse(400, "invalid_quality");
73
+ quality = Math.min(100, q);
74
+ }
75
+ const width = Math.min(cfg.maxWidth, requestedWidth);
76
+ const wantsWebp = (req.headers.get("accept") ?? "").includes("image/webp");
77
+ // 2b. Signed-URL enforcement (when configured): reject any request we didn't mint. Verifies the
78
+ // `&s=` HMAC over the raw (src, w, q[, exp]) — exactly what the loader/`signImageUrl` signed —
79
+ // and the expiry. Done before the ETag/fetch so unsigned/forged requests cost nothing.
80
+ if (cfg.signing !== null) {
81
+ const sig = url.searchParams.get("s");
82
+ const ok = sig !== null &&
83
+ verifyImageParams(cfg.signing.secret, {
84
+ src,
85
+ w: url.searchParams.get("w") ?? "",
86
+ q: qParam ?? undefined,
87
+ exp: url.searchParams.get("exp") ?? undefined,
88
+ }, sig, Math.floor(Date.now() / 1000));
89
+ if (!ok)
90
+ return errorResponse(403, "invalid_signature");
91
+ }
92
+ // 3. Strong validator from the request-deterministic inputs, computed BEFORE any fetch/decode so a
93
+ // conditional request short-circuits all expensive work. The clamped width is a pure function of
94
+ // (src, requestedWidth), so (src, requestedWidth, quality, wantsWebp) fully keys the response.
95
+ const etag = `"${fnv1a(`${src}|${requestedWidth}|${quality}|${wantsWebp ? 1 : 0}`)}"`;
96
+ const cacheHeaders = {
97
+ "Cache-Control": `public, max-age=${cfg.cacheMaxAge}, immutable`,
98
+ ETag: etag,
99
+ Vary: "Accept",
100
+ };
101
+ if (ifNoneMatch(req, etag)) {
102
+ return new Response(null, { status: 304, headers: cacheHeaders });
103
+ }
104
+ // 4. Resolve + read the source under the SSRF policy (fail-closed: unknown source kind → 403/400).
105
+ const source = await readSource(src, cfg);
106
+ if (!source.ok)
107
+ return errorResponse(source.status, source.code);
108
+ // 5. Probe → enforce the portable pixel cap → clamp to intrinsic (never upscale) → transform.
109
+ // The codec work is bounded by the concurrency semaphore.
110
+ await cfg.acquire();
111
+ try {
112
+ const probe = await cfg.backend.probe(source.bytes);
113
+ if (probe.width * probe.height > cfg.maxSourcePixels) {
114
+ return errorResponse(413, "source_too_large");
115
+ }
116
+ const targetWidth = Math.max(1, Math.min(width, probe.width));
117
+ const format = negotiateFormat(probe.format, wantsWebp);
118
+ const out = await cfg.backend.transform({
119
+ bytes: source.bytes,
120
+ width: targetWidth,
121
+ quality,
122
+ format,
123
+ });
124
+ const headers = new Headers(cacheHeaders);
125
+ headers.set("Content-Type", out.contentType);
126
+ headers.set("Content-Length", String(out.bytes.byteLength));
127
+ headers.set("X-Content-Type-Options", "nosniff"); // never let a client sniff the re-encoded bytes
128
+ // HEAD: identical headers, no body.
129
+ if (req.method === "HEAD")
130
+ return new Response(null, { status: 200, headers });
131
+ return new Response(out.bytes, { status: 200, headers });
132
+ }
133
+ catch (err) {
134
+ if (err instanceof ImageProcessingError) {
135
+ if (err.kind === "too_large")
136
+ return errorResponse(413, "source_too_large");
137
+ return errorResponse(415, "unsupported_media_type"); // decode | unsupported
138
+ }
139
+ // Unexpected — never leak internals to the client.
140
+ return errorResponse(500, "internal_error");
141
+ }
142
+ finally {
143
+ cfg.release();
144
+ }
145
+ }
146
+ /**
147
+ * Resolve `src` to bytes under the SSRF policy. A value that parses as an absolute URL is "remote" and
148
+ * must be `http(s)` **and** on the `allowedOrigins` allowlist; anything else is a local path resolved
149
+ * under `root` with traversal + symlink containment checks. Both branches fail closed.
150
+ */
151
+ async function readSource(src, cfg) {
152
+ const asUrl = tryParseUrl(src);
153
+ if (asUrl !== null) {
154
+ if (asUrl.protocol !== "http:" && asUrl.protocol !== "https:") {
155
+ return { ok: false, status: 400, code: "unsupported_scheme" };
156
+ }
157
+ if (!cfg.allowedOrigins.has(asUrl.origin)) {
158
+ return { ok: false, status: 403, code: "source_not_allowed" };
159
+ }
160
+ return fetchRemote(asUrl, cfg);
161
+ }
162
+ return readLocal(src, cfg);
163
+ }
164
+ async function fetchRemote(url, cfg) {
165
+ let res;
166
+ try {
167
+ res = await cfg.fetchImpl(url, {
168
+ redirect: "error", // a redirect could bounce to a disallowed origin — refuse it.
169
+ signal: AbortSignal.timeout(cfg.fetchTimeoutMs),
170
+ headers: { Accept: "image/*" },
171
+ });
172
+ }
173
+ catch (err) {
174
+ const name = err instanceof Error ? err.name : "";
175
+ return name === "TimeoutError"
176
+ ? { ok: false, status: 504, code: "upstream_timeout" }
177
+ : { ok: false, status: 502, code: "upstream_unreachable" };
178
+ }
179
+ if (!res.ok)
180
+ return { ok: false, status: 502, code: "upstream_error" };
181
+ const bytes = await readBoundedBytes(res, cfg.maxSourceBytes);
182
+ if (bytes === null)
183
+ return { ok: false, status: 413, code: "source_too_large" };
184
+ return { ok: true, bytes };
185
+ }
186
+ async function readLocal(src, cfg) {
187
+ if (cfg.root === null)
188
+ return { ok: false, status: 403, code: "source_not_allowed" };
189
+ // `src` is already percent-decoded by URLSearchParams — do NOT decode again (double-decode bypass).
190
+ if (src.includes("\0"))
191
+ return { ok: false, status: 400, code: "invalid_src" };
192
+ // Strip leading slashes so a site-absolute ("/hero.jpg") or protocol-relative ("//evil") path is
193
+ // treated as root-relative; `resolve` then collapses any "../" and the containment check rejects it.
194
+ const rel = src.replace(/^\/+/, "");
195
+ const resolved = resolvePath(cfg.root, rel);
196
+ if (resolved !== cfg.root && !resolved.startsWith(cfg.root + sep)) {
197
+ return { ok: false, status: 403, code: "source_not_allowed" };
198
+ }
199
+ let info;
200
+ try {
201
+ info = await stat(resolved);
202
+ }
203
+ catch {
204
+ return { ok: false, status: 404, code: "source_not_found" };
205
+ }
206
+ if (!info.isFile())
207
+ return { ok: false, status: 404, code: "source_not_found" };
208
+ if (info.size > cfg.maxSourceBytes)
209
+ return { ok: false, status: 413, code: "source_too_large" };
210
+ // Defense-in-depth: a symlink inside root could point outside it — re-check the real path.
211
+ try {
212
+ const real = await realpath(resolved);
213
+ const realRoot = await realpath(cfg.root);
214
+ if (real !== realRoot && !real.startsWith(realRoot + sep)) {
215
+ return { ok: false, status: 403, code: "source_not_allowed" };
216
+ }
217
+ }
218
+ catch {
219
+ return { ok: false, status: 404, code: "source_not_found" };
220
+ }
221
+ return { ok: true, bytes: new Uint8Array(await readFile(resolved)) };
222
+ }
223
+ /** Read a response body, aborting (→ null) once `limit` bytes are exceeded. Trusts neither the
224
+ * `Content-Length` header (checked as a fast reject) nor an absent one — the running total is the gate. */
225
+ async function readBoundedBytes(res, limit) {
226
+ const declared = res.headers.get("content-length");
227
+ if (declared !== null && /^\d+$/.test(declared) && Number(declared) > limit)
228
+ return null;
229
+ const reader = res.body?.getReader();
230
+ if (reader === undefined) {
231
+ const buf = new Uint8Array(await res.arrayBuffer());
232
+ return buf.byteLength > limit ? null : buf;
233
+ }
234
+ const chunks = [];
235
+ let total = 0;
236
+ for (;;) {
237
+ const { done, value } = await reader.read();
238
+ if (done)
239
+ break;
240
+ total += value.byteLength;
241
+ if (total > limit) {
242
+ await reader.cancel();
243
+ return null;
244
+ }
245
+ chunks.push(value);
246
+ }
247
+ const out = new Uint8Array(total);
248
+ let offset = 0;
249
+ for (const c of chunks) {
250
+ out.set(c, offset);
251
+ offset += c.byteLength;
252
+ }
253
+ return out;
254
+ }
255
+ /** Prefer WebP when the client advertises it (supports alpha + best compression). Otherwise preserve a
256
+ * JPEG source as JPEG; everything else falls back to PNG (lossless, keeps any alpha). `Vary: Accept`
257
+ * keeps the WebP / non-WebP variants cached separately. */
258
+ function negotiateFormat(sourceFormat, wantsWebp) {
259
+ if (wantsWebp)
260
+ return "webp";
261
+ if (sourceFormat === "jpeg" || sourceFormat === "jpg")
262
+ return "jpeg";
263
+ return "png";
264
+ }
265
+ /** Strict positive-integer parse: canonical decimal digits only (rejects signs, decimals, `1e3`, `0x`,
266
+ * whitespace, empty). Returns null on any non-canonical input. */
267
+ function parsePositiveInt(raw) {
268
+ if (raw === null || !/^\d+$/.test(raw))
269
+ return null;
270
+ const n = Number(raw);
271
+ return Number.isSafeInteger(n) && n >= 1 ? n : null;
272
+ }
273
+ function tryParseUrl(src) {
274
+ try {
275
+ return new URL(src);
276
+ }
277
+ catch {
278
+ return null;
279
+ }
280
+ }
281
+ function ifNoneMatch(req, etag) {
282
+ const header = req.headers.get("if-none-match");
283
+ if (header === null)
284
+ return false;
285
+ if (header.trim() === "*")
286
+ return true;
287
+ return header.split(",").some((t) => t.trim() === etag);
288
+ }
289
+ /** 32-bit FNV-1a over a short key string → hex. Used only as a cache validator (ETag), not security. */
290
+ function fnv1a(input) {
291
+ let h = 0x811c9dc5;
292
+ for (let i = 0; i < input.length; i++) {
293
+ h ^= input.charCodeAt(i);
294
+ h = Math.imul(h, 0x01000193);
295
+ }
296
+ return (h >>> 0).toString(16);
297
+ }
298
+ function errorResponse(status, code, extraHeaders = {}) {
299
+ return new Response(JSON.stringify({ error: code }), {
300
+ status,
301
+ headers: { "Content-Type": "application/json; charset=utf-8", ...extraHeaders },
302
+ });
303
+ }
304
+ /**
305
+ * Async counting semaphore with direct slot hand-off — a released slot is passed straight to the next
306
+ * waiter (the active count is never transiently decremented), so a concurrent `acquire()` can't slip
307
+ * past the limit in the microtask gap. Bounds concurrent codec work.
308
+ */
309
+ function createSemaphore(max) {
310
+ let active = 0;
311
+ const waiters = [];
312
+ const acquire = async () => {
313
+ if (active < max) {
314
+ active++;
315
+ return;
316
+ }
317
+ await new Promise((res) => waiters.push(res)); // slot handed over by release(), active unchanged
318
+ };
319
+ const release = () => {
320
+ const next = waiters.shift();
321
+ if (next !== undefined) {
322
+ next(); // hand the slot to the waiter without touching `active`
323
+ }
324
+ else {
325
+ active--;
326
+ }
327
+ };
328
+ return { acquire, release };
329
+ }
330
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}