@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.
- package/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/backend.d.ts +128 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +170 -0
- package/dist/backend.js.map +1 -0
- package/dist/dimensions.d.ts +23 -0
- package/dist/dimensions.d.ts.map +1 -0
- package/dist/dimensions.js +113 -0
- package/dist/dimensions.js.map +1 -0
- package/dist/html.d.ts +21 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +26 -0
- package/dist/html.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/resolve.d.ts +102 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +102 -0
- package/dist/resolve.js.map +1 -0
- package/dist/server.d.ts +57 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +330 -0
- package/dist/server.js.map +1 -0
- package/dist/sign.d.ts +30 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +161 -0
- package/dist/sign.js.map +1 -0
- package/package.json +56 -0
- package/src/backend.ts +273 -0
- package/src/dimensions.ts +129 -0
- package/src/html.ts +47 -0
- package/src/index.ts +26 -0
- package/src/resolve.ts +180 -0
- package/src/server.ts +397 -0
- package/src/sign.ts +187 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nifra Authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @nifrajs/image
|
|
2
|
+
|
|
3
|
+
Image optimization for nifra — a CLS-safe responsive <Image> + pluggable loaders (CDN or self-hosted) + pure-JS dimension reading. Zero-dependency core; optional Bun.Image-backed resize endpoint at @nifrajs/image/server.
|
|
4
|
+
|
|
5
|
+
Part of the **[nifra](https://nifra.dev)** full-stack TypeScript framework — one core, five UI libraries, every runtime. Scaffold a new app with `bun create nifra`.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
bun add @nifrajs/image
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Docs
|
|
14
|
+
|
|
15
|
+
- Reference: <https://nifra.dev/docs>
|
|
16
|
+
- AI-readable: <https://nifra.dev/llms.txt>
|
|
17
|
+
|
|
18
|
+
MIT
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/image/backends` — the codec seam + the official {@link ImageBackend} implementations, kept
|
|
3
|
+
* **dependency-free and edge-safe** (no `node:` imports), so the WASM backend can run on Workers /
|
|
4
|
+
* Vercel-Edge / Deno-Deploy. `createImageHandler` (in `@nifrajs/image/server`, Node-only — it touches the
|
|
5
|
+
* filesystem) consumes this seam and owns all request-level security; a backend only
|
|
6
|
+
* decodes/resizes/encodes and translates codec failures into {@link ImageProcessingError}.
|
|
7
|
+
*
|
|
8
|
+
* Pick a backend for your runtime:
|
|
9
|
+
* - {@link bunImageBackend} — `Bun.Image` (libjpeg-turbo / libspng / libwebp). Bun servers.
|
|
10
|
+
* - {@link sharpImageBackend} — pass your `sharp` import. Node servers.
|
|
11
|
+
* - {@link wasmImageBackend} — pass WASM codecs (e.g. jSquash). Any runtime, including the edge.
|
|
12
|
+
*/
|
|
13
|
+
/** Output formats nifra's endpoint can emit. AVIF is intentionally excluded — `Bun.Image` reports
|
|
14
|
+
* `ERR_IMAGE_FORMAT_UNSUPPORTED` for AVIF encode on common platforms, so offering it would 500. */
|
|
15
|
+
export type OutputFormat = "webp" | "jpeg" | "png";
|
|
16
|
+
export declare const CONTENT_TYPE: Record<OutputFormat, string>;
|
|
17
|
+
/** Header-only probe of a source image: intrinsic dimensions + decoded format. Must be cheap (no full
|
|
18
|
+
* decode) — it gates the decompression-bomb and no-upscale checks before the expensive resize. */
|
|
19
|
+
export interface ImageProbe {
|
|
20
|
+
readonly width: number;
|
|
21
|
+
readonly height: number;
|
|
22
|
+
/** Lowercased source format as the codec sees it (`"png"`, `"jpeg"`, `"webp"`, `"gif"`, …). */
|
|
23
|
+
readonly format: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ResizeInput {
|
|
26
|
+
readonly bytes: Uint8Array;
|
|
27
|
+
/** Target width in px — already clamped to `[1, maxWidth]` **and** to the source's intrinsic width
|
|
28
|
+
* (the handler never asks a backend to upscale). Aspect ratio is preserved. */
|
|
29
|
+
readonly width: number;
|
|
30
|
+
/** Encoder quality `1..100` (ignored for lossless `png`). */
|
|
31
|
+
readonly quality: number;
|
|
32
|
+
readonly format: OutputFormat;
|
|
33
|
+
}
|
|
34
|
+
export interface ResizeOutput {
|
|
35
|
+
readonly bytes: Uint8Array;
|
|
36
|
+
readonly contentType: string;
|
|
37
|
+
readonly format: OutputFormat;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The codec seam. The handler owns all request-level security (validation, SSRF, byte/pixel caps,
|
|
41
|
+
* concurrency, caching); a backend only decodes/resizes/encodes. Backends MUST translate codec failures
|
|
42
|
+
* into {@link ImageProcessingError} so the handler can map them to stable HTTP statuses.
|
|
43
|
+
*/
|
|
44
|
+
export interface ImageBackend {
|
|
45
|
+
/** Cheap, header-only metadata read. Throws {@link ImageProcessingError} (`decode` / `too_large`). */
|
|
46
|
+
probe(bytes: Uint8Array): Promise<ImageProbe>;
|
|
47
|
+
/** Decode → resize to `input.width` (aspect-preserving) → encode to `input.format`. */
|
|
48
|
+
transform(input: ResizeInput): Promise<ResizeOutput>;
|
|
49
|
+
}
|
|
50
|
+
/** Normalized, backend-agnostic processing failure. Lets the handler map codec errors to HTTP status
|
|
51
|
+
* without coupling to any one codec's error codes. */
|
|
52
|
+
export declare class ImageProcessingError extends Error {
|
|
53
|
+
readonly kind: "decode" | "too_large" | "unsupported";
|
|
54
|
+
constructor(kind: "decode" | "too_large" | "unsupported", message: string);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* {@link ImageBackend} backed by `Bun.Image` (libjpeg-turbo / libspng / libwebp, decoded off the main
|
|
58
|
+
* thread). Requires the Bun runtime. The default backend of `createImageHandler`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function bunImageBackend(): ImageBackend;
|
|
61
|
+
/** The slice of a [sharp](https://sharp.pixelplumbing.com) instance this backend uses. Declared
|
|
62
|
+
* structurally so `@nifrajs/image` has no dependency on sharp — pass your own `sharp` import. */
|
|
63
|
+
export type SharpLike = (input: Uint8Array) => SharpInstance;
|
|
64
|
+
interface SharpInstance {
|
|
65
|
+
metadata(): Promise<{
|
|
66
|
+
width?: number;
|
|
67
|
+
height?: number;
|
|
68
|
+
format?: string;
|
|
69
|
+
}>;
|
|
70
|
+
resize(options: {
|
|
71
|
+
width: number;
|
|
72
|
+
withoutEnlargement?: boolean;
|
|
73
|
+
}): SharpInstance;
|
|
74
|
+
webp(options: {
|
|
75
|
+
quality: number;
|
|
76
|
+
}): SharpInstance;
|
|
77
|
+
jpeg(options: {
|
|
78
|
+
quality: number;
|
|
79
|
+
}): SharpInstance;
|
|
80
|
+
png(): SharpInstance;
|
|
81
|
+
toBuffer(): Promise<Uint8Array>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* {@link ImageBackend} backed by [sharp](https://sharp.pixelplumbing.com) (libvips) for Node servers.
|
|
85
|
+
* Pass your `sharp` import — `@nifrajs/image` never imports it, so it stays dependency-free and you control
|
|
86
|
+
* the version:
|
|
87
|
+
*
|
|
88
|
+
* ```ts
|
|
89
|
+
* import sharp from "sharp"
|
|
90
|
+
* createImageHandler({ backend: sharpImageBackend(sharp), root: "./public" })
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function sharpImageBackend(sharp: SharpLike): ImageBackend;
|
|
94
|
+
/** A decoded image: RGBA pixels + dimensions — the lingua franca of WASM codecs (jSquash, Photon, …). */
|
|
95
|
+
export interface DecodedImage {
|
|
96
|
+
/** RGBA bytes, length `width * height * 4`. */
|
|
97
|
+
readonly data: Uint8Array;
|
|
98
|
+
readonly width: number;
|
|
99
|
+
readonly height: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Pluggable WASM codec set — decode/resize/encode. Declared structurally so `@nifrajs/image` depends on no
|
|
103
|
+
* WASM library; wire your own (jSquash is the common pure-WASM, edge-safe choice). The handler probes
|
|
104
|
+
* dimensions from the source header (bomb-safe), so `decode` runs only inside `transform`.
|
|
105
|
+
*/
|
|
106
|
+
export interface WasmImageCodecs {
|
|
107
|
+
/** Decode any supported encoded image → RGBA. Throw on undecodable input. */
|
|
108
|
+
decode(bytes: Uint8Array): Promise<DecodedImage> | DecodedImage;
|
|
109
|
+
/** Resize RGBA to the target dimensions (the handler keeps the aspect ratio). */
|
|
110
|
+
resize(image: DecodedImage, width: number, height: number): Promise<DecodedImage> | DecodedImage;
|
|
111
|
+
/** Encode RGBA → the target format's bytes. */
|
|
112
|
+
encode(image: DecodedImage, format: OutputFormat, quality: number): Promise<Uint8Array> | Uint8Array;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* {@link ImageBackend} backed by injected WASM codecs — the only backend that runs on the **edge**
|
|
116
|
+
* (Workers / Vercel-Edge / Deno-Deploy), where neither `Bun.Image` nor sharp exists. `probe` reads the
|
|
117
|
+
* source header via nifra's dependency-free reader (so decompression bombs are rejected before any
|
|
118
|
+
* decode); `transform` decodes → resizes (aspect-preserving) → encodes through your codecs.
|
|
119
|
+
*
|
|
120
|
+
* ```ts
|
|
121
|
+
* import decode from "@jsquash/jpeg/decode"; import resize from "@jsquash/resize"
|
|
122
|
+
* import encodeWebp from "@jsquash/webp/encode" // …+ png/jpeg encoders
|
|
123
|
+
* const backend = wasmImageBackend({ decode, resize: (img, w, h) => resize(img, { width: w, height: h }), encode })
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export declare function wasmImageBackend(codecs: WasmImageCodecs): ImageBackend;
|
|
127
|
+
export {};
|
|
128
|
+
//# sourceMappingURL=backend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/image/backends` — the codec seam + the official {@link ImageBackend} implementations, kept
|
|
3
|
+
* **dependency-free and edge-safe** (no `node:` imports), so the WASM backend can run on Workers /
|
|
4
|
+
* Vercel-Edge / Deno-Deploy. `createImageHandler` (in `@nifrajs/image/server`, Node-only — it touches the
|
|
5
|
+
* filesystem) consumes this seam and owns all request-level security; a backend only
|
|
6
|
+
* decodes/resizes/encodes and translates codec failures into {@link ImageProcessingError}.
|
|
7
|
+
*
|
|
8
|
+
* Pick a backend for your runtime:
|
|
9
|
+
* - {@link bunImageBackend} — `Bun.Image` (libjpeg-turbo / libspng / libwebp). Bun servers.
|
|
10
|
+
* - {@link sharpImageBackend} — pass your `sharp` import. Node servers.
|
|
11
|
+
* - {@link wasmImageBackend} — pass WASM codecs (e.g. jSquash). Any runtime, including the edge.
|
|
12
|
+
*/
|
|
13
|
+
import { imageDimensions } from "./dimensions.js";
|
|
14
|
+
export const CONTENT_TYPE = {
|
|
15
|
+
webp: "image/webp",
|
|
16
|
+
jpeg: "image/jpeg",
|
|
17
|
+
png: "image/png",
|
|
18
|
+
};
|
|
19
|
+
/** Normalized, backend-agnostic processing failure. Lets the handler map codec errors to HTTP status
|
|
20
|
+
* without coupling to any one codec's error codes. */
|
|
21
|
+
export class ImageProcessingError extends Error {
|
|
22
|
+
kind;
|
|
23
|
+
constructor(kind, message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.kind = kind;
|
|
26
|
+
this.name = "ImageProcessingError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* {@link ImageBackend} backed by `Bun.Image` (libjpeg-turbo / libspng / libwebp, decoded off the main
|
|
31
|
+
* thread). Requires the Bun runtime. The default backend of `createImageHandler`.
|
|
32
|
+
*/
|
|
33
|
+
export function bunImageBackend() {
|
|
34
|
+
if (typeof globalThis.Bun === "undefined") {
|
|
35
|
+
throw new Error("[nifra/image] bunImageBackend() requires the Bun runtime");
|
|
36
|
+
}
|
|
37
|
+
const Image = globalThis.Bun.Image;
|
|
38
|
+
return {
|
|
39
|
+
async probe(bytes) {
|
|
40
|
+
try {
|
|
41
|
+
const md = await new Image(bytes).metadata();
|
|
42
|
+
return { width: md.width, height: md.height, format: String(md.format).toLowerCase() };
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw toBunError(err);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
async transform({ bytes, width, quality, format }) {
|
|
49
|
+
try {
|
|
50
|
+
const img = new Image(bytes).resize(width);
|
|
51
|
+
const encoded = format === "webp"
|
|
52
|
+
? img.webp({ quality })
|
|
53
|
+
: format === "jpeg"
|
|
54
|
+
? img.jpeg({ quality })
|
|
55
|
+
: img.png();
|
|
56
|
+
const out = await encoded.bytes();
|
|
57
|
+
return { bytes: out, contentType: CONTENT_TYPE[format], format };
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
throw toBunError(err);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** Map a `Bun.Image` failure to a normalized {@link ImageProcessingError}. */
|
|
66
|
+
function toBunError(err) {
|
|
67
|
+
if (err instanceof ImageProcessingError)
|
|
68
|
+
return err;
|
|
69
|
+
const code = err?.code ?? "";
|
|
70
|
+
if (code === "ERR_IMAGE_TOO_MANY_PIXELS") {
|
|
71
|
+
return new ImageProcessingError("too_large", "source image exceeds the codec pixel limit");
|
|
72
|
+
}
|
|
73
|
+
if (code === "ERR_IMAGE_FORMAT_UNSUPPORTED") {
|
|
74
|
+
return new ImageProcessingError("unsupported", "image format not supported by the codec");
|
|
75
|
+
}
|
|
76
|
+
return new ImageProcessingError("decode", "source is not a decodable image");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* {@link ImageBackend} backed by [sharp](https://sharp.pixelplumbing.com) (libvips) for Node servers.
|
|
80
|
+
* Pass your `sharp` import — `@nifrajs/image` never imports it, so it stays dependency-free and you control
|
|
81
|
+
* the version:
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* import sharp from "sharp"
|
|
85
|
+
* createImageHandler({ backend: sharpImageBackend(sharp), root: "./public" })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export function sharpImageBackend(sharp) {
|
|
89
|
+
return {
|
|
90
|
+
async probe(bytes) {
|
|
91
|
+
try {
|
|
92
|
+
const md = await sharp(bytes).metadata();
|
|
93
|
+
if (md.width === undefined || md.height === undefined) {
|
|
94
|
+
throw new ImageProcessingError("decode", "sharp could not read image dimensions");
|
|
95
|
+
}
|
|
96
|
+
return { width: md.width, height: md.height, format: String(md.format ?? "").toLowerCase() };
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
throw toSharpError(err);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
async transform({ bytes, width, quality, format }) {
|
|
103
|
+
try {
|
|
104
|
+
// `withoutEnlargement` is belt-and-braces: the handler already clamps width ≤ intrinsic.
|
|
105
|
+
const pipeline = sharp(bytes).resize({ width, withoutEnlargement: true });
|
|
106
|
+
const encoded = format === "webp"
|
|
107
|
+
? pipeline.webp({ quality })
|
|
108
|
+
: format === "jpeg"
|
|
109
|
+
? pipeline.jpeg({ quality })
|
|
110
|
+
: pipeline.png();
|
|
111
|
+
return { bytes: await encoded.toBuffer(), contentType: CONTENT_TYPE[format], format };
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
throw toSharpError(err);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** Map a sharp failure to a normalized {@link ImageProcessingError} by its message. */
|
|
120
|
+
function toSharpError(err) {
|
|
121
|
+
if (err instanceof ImageProcessingError)
|
|
122
|
+
return err;
|
|
123
|
+
const message = (err instanceof Error ? err.message : "").toLowerCase();
|
|
124
|
+
if (message.includes("pixel limit") || message.includes("too large")) {
|
|
125
|
+
return new ImageProcessingError("too_large", "source image exceeds sharp's pixel limit");
|
|
126
|
+
}
|
|
127
|
+
if (message.includes("unsupported image format") || message.includes("bad seek")) {
|
|
128
|
+
return new ImageProcessingError("decode", "sharp could not decode the source image");
|
|
129
|
+
}
|
|
130
|
+
return new ImageProcessingError("decode", "sharp failed to process the image");
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* {@link ImageBackend} backed by injected WASM codecs — the only backend that runs on the **edge**
|
|
134
|
+
* (Workers / Vercel-Edge / Deno-Deploy), where neither `Bun.Image` nor sharp exists. `probe` reads the
|
|
135
|
+
* source header via nifra's dependency-free reader (so decompression bombs are rejected before any
|
|
136
|
+
* decode); `transform` decodes → resizes (aspect-preserving) → encodes through your codecs.
|
|
137
|
+
*
|
|
138
|
+
* ```ts
|
|
139
|
+
* import decode from "@jsquash/jpeg/decode"; import resize from "@jsquash/resize"
|
|
140
|
+
* import encodeWebp from "@jsquash/webp/encode" // …+ png/jpeg encoders
|
|
141
|
+
* const backend = wasmImageBackend({ decode, resize: (img, w, h) => resize(img, { width: w, height: h }), encode })
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export function wasmImageBackend(codecs) {
|
|
145
|
+
return {
|
|
146
|
+
async probe(bytes) {
|
|
147
|
+
const info = imageDimensions(bytes); // header-only — never allocates a decoded buffer
|
|
148
|
+
if (info === null) {
|
|
149
|
+
throw new ImageProcessingError("decode", "unrecognized image header");
|
|
150
|
+
}
|
|
151
|
+
return { width: info.width, height: info.height, format: info.format };
|
|
152
|
+
},
|
|
153
|
+
async transform({ bytes, width, quality, format }) {
|
|
154
|
+
try {
|
|
155
|
+
const decoded = await codecs.decode(bytes);
|
|
156
|
+
// Aspect-preserving: the handler hands a width already clamped to the intrinsic width.
|
|
157
|
+
const height = Math.max(1, Math.round((decoded.height * width) / decoded.width));
|
|
158
|
+
const resized = width === decoded.width ? decoded : await codecs.resize(decoded, width, height);
|
|
159
|
+
const out = await codecs.encode(resized, format, quality);
|
|
160
|
+
return { bytes: out, contentType: CONTENT_TYPE[format], format };
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
if (err instanceof ImageProcessingError)
|
|
164
|
+
throw err;
|
|
165
|
+
throw new ImageProcessingError("decode", "wasm codec failed to process the image");
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read an image's intrinsic dimensions from its file **header**, in pure JS — no decode, no codec, no
|
|
3
|
+
* dependency. Supports PNG, JPEG, GIF, and WebP (VP8/VP8L/VP8X). Used to give `<Image>` CLS-safe
|
|
4
|
+
* `width`/`height` (build-time tooling can pre-read them into a manifest).
|
|
5
|
+
*/
|
|
6
|
+
export type ImageFormat = "png" | "jpeg" | "gif" | "webp";
|
|
7
|
+
export interface ImageInfo {
|
|
8
|
+
readonly width: number;
|
|
9
|
+
readonly height: number;
|
|
10
|
+
readonly format: ImageFormat;
|
|
11
|
+
}
|
|
12
|
+
/** Parse intrinsic dimensions + format from image header bytes, or `null` if unrecognized/too short. */
|
|
13
|
+
export declare function imageDimensions(bytes: Uint8Array): ImageInfo | null;
|
|
14
|
+
/**
|
|
15
|
+
* Read just the leading bytes of an image file (via the platform `Bun.file`/`fetch` blob) and parse its
|
|
16
|
+
* dimensions. Build-time tooling: pre-read dimensions into a manifest so `<Image>` is CLS-safe without
|
|
17
|
+
* hardcoding sizes. Reads at most `maxBytes` (default 64 KB — enough for any header).
|
|
18
|
+
*/
|
|
19
|
+
export declare function readImageDimensions(source: {
|
|
20
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
21
|
+
stream?: () => ReadableStream<Uint8Array>;
|
|
22
|
+
}, maxBytes?: number): Promise<ImageInfo | null>;
|
|
23
|
+
//# sourceMappingURL=dimensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dimensions.d.ts","sourceRoot":"","sources":["../src/dimensions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;AAEzD,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAC7B;AA6DD,wGAAwG;AACxG,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,CAInE;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE;IAAE,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,cAAc,CAAC,UAAU,CAAC,CAAA;CAAE,EAC1F,QAAQ,SAAS,GAChB,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAyC3B"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const png = (b, dv) => {
|
|
2
|
+
// 8-byte signature, then IHDR (length+type), then width/height as big-endian uint32 @16/@20.
|
|
3
|
+
if (b.length < 24 || b[0] !== 0x89 || b[1] !== 0x50 || b[2] !== 0x4e || b[3] !== 0x47)
|
|
4
|
+
return null;
|
|
5
|
+
return { width: dv.getUint32(16), height: dv.getUint32(20), format: "png" };
|
|
6
|
+
};
|
|
7
|
+
const gif = (b, dv) => {
|
|
8
|
+
// 'GIF', then the logical-screen width/height as little-endian uint16 @6/@8.
|
|
9
|
+
if (b.length < 10 || b[0] !== 0x47 || b[1] !== 0x49 || b[2] !== 0x46)
|
|
10
|
+
return null;
|
|
11
|
+
return { width: dv.getUint16(6, true), height: dv.getUint16(8, true), format: "gif" };
|
|
12
|
+
};
|
|
13
|
+
const jpeg = (b, dv) => {
|
|
14
|
+
if (b.length < 4 || b[0] !== 0xff || b[1] !== 0xd8)
|
|
15
|
+
return null;
|
|
16
|
+
let offset = 2;
|
|
17
|
+
while (offset + 9 < b.length) {
|
|
18
|
+
if (dv.getUint8(offset) !== 0xff)
|
|
19
|
+
return null; // not aligned on a marker → malformed
|
|
20
|
+
const marker = dv.getUint8(offset + 1);
|
|
21
|
+
// SOF0..SOF15 carry the frame's height/width — except DHT(c4)/DNL(c8)/DAC(cc).
|
|
22
|
+
if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
|
|
23
|
+
return { height: dv.getUint16(offset + 5), width: dv.getUint16(offset + 7), format: "jpeg" };
|
|
24
|
+
}
|
|
25
|
+
offset += 2 + dv.getUint16(offset + 2); // skip this segment (2-byte marker + segment length)
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
};
|
|
29
|
+
const webp = (b, dv) => {
|
|
30
|
+
// 'RIFF' .... 'WEBP' then a VP8 / VP8L / VP8X chunk.
|
|
31
|
+
if (b.length < 30 || b[0] !== 0x52 || b[1] !== 0x49 || b[8] !== 0x57 || b[9] !== 0x45)
|
|
32
|
+
return null;
|
|
33
|
+
const chunk = String.fromCharCode(b[12], b[13], b[14], b[15]);
|
|
34
|
+
if (chunk === "VP8 ") {
|
|
35
|
+
// lossy: 14-bit width/height (little-endian) at @26/@28, masked to 14 bits.
|
|
36
|
+
return {
|
|
37
|
+
width: dv.getUint16(26, true) & 0x3fff,
|
|
38
|
+
height: dv.getUint16(28, true) & 0x3fff,
|
|
39
|
+
format: "webp",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (chunk === "VP8L") {
|
|
43
|
+
// lossless: after the 0x2f signature @20, 14-bit (width-1) then 14-bit (height-1), bit-packed LE.
|
|
44
|
+
if (b[20] !== 0x2f)
|
|
45
|
+
return null;
|
|
46
|
+
const bits = dv.getUint32(21, true);
|
|
47
|
+
return { width: (bits & 0x3fff) + 1, height: ((bits >> 14) & 0x3fff) + 1, format: "webp" };
|
|
48
|
+
}
|
|
49
|
+
if (chunk === "VP8X") {
|
|
50
|
+
// extended: 24-bit (width-1) then 24-bit (height-1), little-endian, at @24/@27.
|
|
51
|
+
const w = b[24] | (b[25] << 8) | (b[26] << 16);
|
|
52
|
+
const h = b[27] | (b[28] << 8) | (b[29] << 16);
|
|
53
|
+
return { width: w + 1, height: h + 1, format: "webp" };
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
/** Parse intrinsic dimensions + format from image header bytes, or `null` if unrecognized/too short. */
|
|
58
|
+
export function imageDimensions(bytes) {
|
|
59
|
+
if (bytes.length < 4)
|
|
60
|
+
return null;
|
|
61
|
+
const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
62
|
+
return png(bytes, dv) ?? gif(bytes, dv) ?? jpeg(bytes, dv) ?? webp(bytes, dv);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Read just the leading bytes of an image file (via the platform `Bun.file`/`fetch` blob) and parse its
|
|
66
|
+
* dimensions. Build-time tooling: pre-read dimensions into a manifest so `<Image>` is CLS-safe without
|
|
67
|
+
* hardcoding sizes. Reads at most `maxBytes` (default 64 KB — enough for any header).
|
|
68
|
+
*/
|
|
69
|
+
export async function readImageDimensions(source, maxBytes = 65_536) {
|
|
70
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 1) {
|
|
71
|
+
throw new Error("[nifra/image] readImageDimensions: maxBytes must be a positive safe integer");
|
|
72
|
+
}
|
|
73
|
+
if (source.stream !== undefined) {
|
|
74
|
+
const reader = source.stream().getReader();
|
|
75
|
+
const chunks = [];
|
|
76
|
+
let total = 0;
|
|
77
|
+
let reachedEof = false;
|
|
78
|
+
try {
|
|
79
|
+
while (total < maxBytes) {
|
|
80
|
+
const { done, value } = await reader.read();
|
|
81
|
+
if (done) {
|
|
82
|
+
reachedEof = true;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
const remaining = maxBytes - total;
|
|
86
|
+
const chunk = value.byteLength > remaining ? value.subarray(0, remaining) : value;
|
|
87
|
+
chunks.push(chunk);
|
|
88
|
+
total += chunk.byteLength;
|
|
89
|
+
if (value.byteLength > remaining) {
|
|
90
|
+
await reader.cancel();
|
|
91
|
+
reachedEof = true;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!reachedEof)
|
|
96
|
+
await reader.cancel();
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
reader.releaseLock();
|
|
100
|
+
}
|
|
101
|
+
const bytes = new Uint8Array(total);
|
|
102
|
+
let offset = 0;
|
|
103
|
+
for (const chunk of chunks) {
|
|
104
|
+
bytes.set(chunk, offset);
|
|
105
|
+
offset += chunk.byteLength;
|
|
106
|
+
}
|
|
107
|
+
return imageDimensions(bytes);
|
|
108
|
+
}
|
|
109
|
+
const buf = await source.arrayBuffer();
|
|
110
|
+
const bytes = new Uint8Array(buf);
|
|
111
|
+
return imageDimensions(bytes.length > maxBytes ? bytes.subarray(0, maxBytes) : bytes);
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=dimensions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dimensions.js","sourceRoot":"","sources":["../src/dimensions.ts"],"names":[],"mappings":"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"}
|
package/dist/html.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ResolvedImage } from "./resolve.js";
|
|
2
|
+
/** Plain lowercase HTML `<img>` attributes (`srcset`/`fetchpriority`, not React's camelCase). */
|
|
3
|
+
export interface HtmlImageAttrs {
|
|
4
|
+
readonly src: string;
|
|
5
|
+
readonly width: number;
|
|
6
|
+
readonly height: number;
|
|
7
|
+
readonly alt: string;
|
|
8
|
+
readonly loading: "lazy" | "eager";
|
|
9
|
+
readonly decoding: "async";
|
|
10
|
+
readonly srcset?: string;
|
|
11
|
+
readonly sizes?: string;
|
|
12
|
+
readonly fetchpriority?: "high";
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Map a {@link ResolvedImage} to plain lowercase HTML `<img>` attribute names — `srcset` (not React's
|
|
16
|
+
* `srcSet`), `fetchpriority` (not `fetchPriority`) — dropping unset optionals. For the adapters that
|
|
17
|
+
* spread attributes straight onto a host element (Solid / Vue / Svelte / Preact); React consumes
|
|
18
|
+
* `ResolvedImage` directly because it requires the camelCased prop names.
|
|
19
|
+
*/
|
|
20
|
+
export declare function toHtmlAttrs(resolved: ResolvedImage): HtmlImageAttrs;
|
|
21
|
+
//# sourceMappingURL=html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD,iGAAiG;AACjG,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,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,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,cAAc,CAyBnE"}
|
package/dist/html.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map a {@link ResolvedImage} to plain lowercase HTML `<img>` attribute names — `srcset` (not React's
|
|
3
|
+
* `srcSet`), `fetchpriority` (not `fetchPriority`) — dropping unset optionals. For the adapters that
|
|
4
|
+
* spread attributes straight onto a host element (Solid / Vue / Svelte / Preact); React consumes
|
|
5
|
+
* `ResolvedImage` directly because it requires the camelCased prop names.
|
|
6
|
+
*/
|
|
7
|
+
export function toHtmlAttrs(resolved) {
|
|
8
|
+
// Build the required attrs, then add optionals only when present (exactOptionalPropertyTypes:
|
|
9
|
+
// never assign an explicit `undefined`). The result is safe to spread onto an <img>.
|
|
10
|
+
const attrs = {
|
|
11
|
+
src: resolved.src,
|
|
12
|
+
width: resolved.width,
|
|
13
|
+
height: resolved.height,
|
|
14
|
+
alt: resolved.alt,
|
|
15
|
+
loading: resolved.loading,
|
|
16
|
+
decoding: resolved.decoding,
|
|
17
|
+
};
|
|
18
|
+
if (resolved.srcSet !== undefined)
|
|
19
|
+
attrs.srcset = resolved.srcSet;
|
|
20
|
+
if (resolved.sizes !== undefined)
|
|
21
|
+
attrs.sizes = resolved.sizes;
|
|
22
|
+
if (resolved.fetchPriority !== undefined)
|
|
23
|
+
attrs.fetchpriority = resolved.fetchPriority;
|
|
24
|
+
return attrs;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=html.js.map
|
package/dist/html.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAeA;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,QAAuB;IACjD,8FAA8F;IAC9F,qFAAqF;IACrF,MAAM,KAAK,GAUP;QACF,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;KAC5B,CAAA;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS;QAAE,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAA;IACjE,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAA;IAC9D,IAAI,QAAQ,CAAC,aAAa,KAAK,SAAS;QAAE,KAAK,CAAC,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAA;IACtF,OAAO,KAAK,CAAA;AACd,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/image` — image optimization for nifra. A CLS-safe responsive `<Image>` (per-adapter) built on
|
|
3
|
+
* the pure attribute builder `resolveImage`, pluggable CDN `ImageLoader`s (Cloudflare Images + an
|
|
4
|
+
* identity default), and dependency-free intrinsic-dimension reading. nifra bundles no image codec; the
|
|
5
|
+
* resize is delegated to the edge CDN via the loader. Per-adapter `<Image>` lives in the adapter packages.
|
|
6
|
+
*/
|
|
7
|
+
export { type ImageFormat, type ImageInfo, imageDimensions, readImageDimensions, } from "./dimensions.js";
|
|
8
|
+
export { type HtmlImageAttrs, toHtmlAttrs } from "./html.js";
|
|
9
|
+
export { type CloudflareLoaderOptions, cloudflareLoader, type ImageLoader, type ImageProps, identityLoader, type ResolvedImage, resolveImage, type SelfHostedLoaderOptions, type SignImageUrlOptions, selfHostedLoader, signImageUrl, } from "./resolve.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,eAAe,EACf,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,KAAK,cAAc,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5D,OAAO,EACL,KAAK,uBAAuB,EAC5B,gBAAgB,EAChB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,cAAc,EACd,KAAK,aAAa,EAClB,YAAY,EACZ,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,gBAAgB,EAChB,YAAY,GACb,MAAM,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nifrajs/image` — image optimization for nifra. A CLS-safe responsive `<Image>` (per-adapter) built on
|
|
3
|
+
* the pure attribute builder `resolveImage`, pluggable CDN `ImageLoader`s (Cloudflare Images + an
|
|
4
|
+
* identity default), and dependency-free intrinsic-dimension reading. nifra bundles no image codec; the
|
|
5
|
+
* resize is delegated to the edge CDN via the loader. Per-adapter `<Image>` lives in the adapter packages.
|
|
6
|
+
*/
|
|
7
|
+
export { imageDimensions, readImageDimensions, } from "./dimensions.js";
|
|
8
|
+
export { toHtmlAttrs } from "./html.js";
|
|
9
|
+
export { cloudflareLoader, identityLoader, resolveImage, selfHostedLoader, signImageUrl, } from "./resolve.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAGL,eAAe,EACf,mBAAmB,GACpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAuB,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5D,OAAO,EAEL,gBAAgB,EAGhB,cAAc,EAEd,YAAY,EAGZ,gBAAgB,EAChB,YAAY,GACb,MAAM,cAAc,CAAA"}
|