@lens-image/core 0.1.0
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 +244 -0
- package/dist/cjs/adapters/memory.d.ts +55 -0
- package/dist/cjs/adapters/memory.d.ts.map +1 -0
- package/dist/cjs/adapters/memory.js +84 -0
- package/dist/cjs/adapters/memory.js.map +1 -0
- package/dist/cjs/browser.d.ts +46 -0
- package/dist/cjs/browser.d.ts.map +1 -0
- package/dist/cjs/browser.js +79 -0
- package/dist/cjs/browser.js.map +1 -0
- package/dist/cjs/bytes.d.ts +18 -0
- package/dist/cjs/bytes.d.ts.map +1 -0
- package/dist/cjs/bytes.js +31 -0
- package/dist/cjs/bytes.js.map +1 -0
- package/dist/cjs/cache.d.ts +43 -0
- package/dist/cjs/cache.d.ts.map +1 -0
- package/dist/cjs/cache.js +77 -0
- package/dist/cjs/cache.js.map +1 -0
- package/dist/cjs/concurrency.d.ts +35 -0
- package/dist/cjs/concurrency.d.ts.map +1 -0
- package/dist/cjs/concurrency.js +82 -0
- package/dist/cjs/concurrency.js.map +1 -0
- package/dist/cjs/engines/index.d.ts +20 -0
- package/dist/cjs/engines/index.d.ts.map +1 -0
- package/dist/cjs/engines/index.js +38 -0
- package/dist/cjs/engines/index.js.map +1 -0
- package/dist/cjs/engines/passthrough.d.ts +41 -0
- package/dist/cjs/engines/passthrough.d.ts.map +1 -0
- package/dist/cjs/engines/passthrough.js +66 -0
- package/dist/cjs/engines/passthrough.js.map +1 -0
- package/dist/cjs/engines/sharp.d.ts +58 -0
- package/dist/cjs/engines/sharp.d.ts.map +1 -0
- package/dist/cjs/engines/sharp.js +243 -0
- package/dist/cjs/engines/sharp.js.map +1 -0
- package/dist/cjs/errors.d.ts +90 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +97 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/format.d.ts +33 -0
- package/dist/cjs/format.d.ts.map +1 -0
- package/dist/cjs/format.js +102 -0
- package/dist/cjs/format.js.map +1 -0
- package/dist/cjs/hash.d.ts +19 -0
- package/dist/cjs/hash.d.ts.map +1 -0
- package/dist/cjs/hash.js +43 -0
- package/dist/cjs/hash.js.map +1 -0
- package/dist/cjs/index.d.ts +56 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +110 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/naming.d.ts +48 -0
- package/dist/cjs/naming.d.ts.map +1 -0
- package/dist/cjs/naming.js +95 -0
- package/dist/cjs/naming.js.map +1 -0
- package/dist/cjs/optimizer.d.ts +89 -0
- package/dist/cjs/optimizer.d.ts.map +1 -0
- package/dist/cjs/optimizer.js +667 -0
- package/dist/cjs/optimizer.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/retry.d.ts +55 -0
- package/dist/cjs/retry.d.ts.map +1 -0
- package/dist/cjs/retry.js +129 -0
- package/dist/cjs/retry.js.map +1 -0
- package/dist/cjs/server/handler.d.ts +80 -0
- package/dist/cjs/server/handler.d.ts.map +1 -0
- package/dist/cjs/server/handler.js +155 -0
- package/dist/cjs/server/handler.js.map +1 -0
- package/dist/cjs/sniff.d.ts +37 -0
- package/dist/cjs/sniff.d.ts.map +1 -0
- package/dist/cjs/sniff.js +442 -0
- package/dist/cjs/sniff.js.map +1 -0
- package/dist/cjs/source.d.ts +45 -0
- package/dist/cjs/source.d.ts.map +1 -0
- package/dist/cjs/source.js +221 -0
- package/dist/cjs/source.js.map +1 -0
- package/dist/cjs/srcset.d.ts +66 -0
- package/dist/cjs/srcset.d.ts.map +1 -0
- package/dist/cjs/srcset.js +81 -0
- package/dist/cjs/srcset.js.map +1 -0
- package/dist/cjs/types.d.ts +441 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +12 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/validation.d.ts +38 -0
- package/dist/cjs/validation.d.ts.map +1 -0
- package/dist/cjs/validation.js +79 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/adapters/memory.d.ts +55 -0
- package/dist/esm/adapters/memory.d.ts.map +1 -0
- package/dist/esm/adapters/memory.js +80 -0
- package/dist/esm/adapters/memory.js.map +1 -0
- package/dist/esm/browser.d.ts +46 -0
- package/dist/esm/browser.d.ts.map +1 -0
- package/dist/esm/browser.js +49 -0
- package/dist/esm/browser.js.map +1 -0
- package/dist/esm/bytes.d.ts +18 -0
- package/dist/esm/bytes.d.ts.map +1 -0
- package/dist/esm/bytes.js +28 -0
- package/dist/esm/bytes.js.map +1 -0
- package/dist/esm/cache.d.ts +43 -0
- package/dist/esm/cache.d.ts.map +1 -0
- package/dist/esm/cache.js +73 -0
- package/dist/esm/cache.js.map +1 -0
- package/dist/esm/concurrency.d.ts +35 -0
- package/dist/esm/concurrency.d.ts.map +1 -0
- package/dist/esm/concurrency.js +78 -0
- package/dist/esm/concurrency.js.map +1 -0
- package/dist/esm/engines/index.d.ts +20 -0
- package/dist/esm/engines/index.d.ts.map +1 -0
- package/dist/esm/engines/index.js +30 -0
- package/dist/esm/engines/index.js.map +1 -0
- package/dist/esm/engines/passthrough.d.ts +41 -0
- package/dist/esm/engines/passthrough.d.ts.map +1 -0
- package/dist/esm/engines/passthrough.js +63 -0
- package/dist/esm/engines/passthrough.js.map +1 -0
- package/dist/esm/engines/sharp.d.ts +58 -0
- package/dist/esm/engines/sharp.d.ts.map +1 -0
- package/dist/esm/engines/sharp.js +206 -0
- package/dist/esm/engines/sharp.js.map +1 -0
- package/dist/esm/errors.d.ts +90 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +91 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/format.d.ts +33 -0
- package/dist/esm/format.d.ts.map +1 -0
- package/dist/esm/format.js +93 -0
- package/dist/esm/format.js.map +1 -0
- package/dist/esm/hash.d.ts +19 -0
- package/dist/esm/hash.d.ts.map +1 -0
- package/dist/esm/hash.js +38 -0
- package/dist/esm/hash.js.map +1 -0
- package/dist/esm/index.d.ts +56 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +54 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/naming.d.ts +48 -0
- package/dist/esm/naming.d.ts.map +1 -0
- package/dist/esm/naming.js +88 -0
- package/dist/esm/naming.js.map +1 -0
- package/dist/esm/optimizer.d.ts +89 -0
- package/dist/esm/optimizer.d.ts.map +1 -0
- package/dist/esm/optimizer.js +663 -0
- package/dist/esm/optimizer.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/retry.d.ts +55 -0
- package/dist/esm/retry.d.ts.map +1 -0
- package/dist/esm/retry.js +123 -0
- package/dist/esm/retry.js.map +1 -0
- package/dist/esm/server/handler.d.ts +80 -0
- package/dist/esm/server/handler.d.ts.map +1 -0
- package/dist/esm/server/handler.js +151 -0
- package/dist/esm/server/handler.js.map +1 -0
- package/dist/esm/sniff.d.ts +37 -0
- package/dist/esm/sniff.d.ts.map +1 -0
- package/dist/esm/sniff.js +435 -0
- package/dist/esm/sniff.js.map +1 -0
- package/dist/esm/source.d.ts +45 -0
- package/dist/esm/source.d.ts.map +1 -0
- package/dist/esm/source.js +183 -0
- package/dist/esm/source.js.map +1 -0
- package/dist/esm/srcset.d.ts +66 -0
- package/dist/esm/srcset.d.ts.map +1 -0
- package/dist/esm/srcset.js +76 -0
- package/dist/esm/srcset.js.map +1 -0
- package/dist/esm/types.d.ts +441 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +11 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/validation.d.ts +38 -0
- package/dist/esm/validation.d.ts.map +1 -0
- package/dist/esm/validation.js +74 -0
- package/dist/esm/validation.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** Format normalisation and per-format encoding defaults. */
|
|
2
|
+
import { LensError } from './errors.js';
|
|
3
|
+
const ALIASES = {
|
|
4
|
+
jpg: 'jpeg',
|
|
5
|
+
jpeg: 'jpeg',
|
|
6
|
+
jpe: 'jpeg',
|
|
7
|
+
png: 'png',
|
|
8
|
+
webp: 'webp',
|
|
9
|
+
avif: 'avif',
|
|
10
|
+
gif: 'gif',
|
|
11
|
+
tif: 'tiff',
|
|
12
|
+
tiff: 'tiff',
|
|
13
|
+
};
|
|
14
|
+
/** Every format Lens can encode to. */
|
|
15
|
+
export const ENCODABLE_FORMATS = [
|
|
16
|
+
'jpeg',
|
|
17
|
+
'png',
|
|
18
|
+
'webp',
|
|
19
|
+
'avif',
|
|
20
|
+
'gif',
|
|
21
|
+
'tiff',
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Maps a user-supplied format name onto its canonical form.
|
|
25
|
+
*
|
|
26
|
+
* `'jpg'` and `'JPEG'` both become `'jpeg'` so results are keyed consistently
|
|
27
|
+
* no matter how the caller spelled it.
|
|
28
|
+
*
|
|
29
|
+
* @throws {LensError} `INVALID_OPTIONS` for an unknown name.
|
|
30
|
+
*/
|
|
31
|
+
export function normalizeFormat(input) {
|
|
32
|
+
const key = String(input).trim().toLowerCase().replace(/^\./, '');
|
|
33
|
+
const format = ALIASES[key];
|
|
34
|
+
if (!format) {
|
|
35
|
+
throw new LensError('INVALID_OPTIONS', `Unknown output format "${input}". Supported: ${ENCODABLE_FORMATS.join(', ')} (and the alias "jpg").`, { details: { actual: input } });
|
|
36
|
+
}
|
|
37
|
+
return format;
|
|
38
|
+
}
|
|
39
|
+
/** Normalises a list, dropping duplicates while preserving the caller's order. */
|
|
40
|
+
export function normalizeFormats(inputs) {
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
const out = [];
|
|
43
|
+
for (const input of inputs) {
|
|
44
|
+
const format = normalizeFormat(input);
|
|
45
|
+
if (!seen.has(format)) {
|
|
46
|
+
seen.add(format);
|
|
47
|
+
out.push(format);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/** True when the format stores an alpha channel. */
|
|
53
|
+
export function supportsAlpha(format) {
|
|
54
|
+
return format !== 'jpeg';
|
|
55
|
+
}
|
|
56
|
+
/** True when `quality` has any effect on the encode. */
|
|
57
|
+
export function isLossy(format) {
|
|
58
|
+
return format === 'jpeg' || format === 'webp' || format === 'avif';
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Sensible per-format encoder defaults.
|
|
62
|
+
*
|
|
63
|
+
* AVIF gets a lower quality number than JPEG on purpose: the scales are not
|
|
64
|
+
* comparable, and AVIF at q80 is wastefully large for the same perceived
|
|
65
|
+
* result. Callers who pass an explicit quality always win over this.
|
|
66
|
+
*/
|
|
67
|
+
export function defaultQuality(format) {
|
|
68
|
+
switch (format) {
|
|
69
|
+
case 'avif':
|
|
70
|
+
return 60;
|
|
71
|
+
case 'webp':
|
|
72
|
+
return 80;
|
|
73
|
+
case 'jpeg':
|
|
74
|
+
return 82;
|
|
75
|
+
default:
|
|
76
|
+
return 80;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Default encoder effort. AVIF is slow enough that the top setting is a
|
|
81
|
+
* production hazard, so we sit mid-scale.
|
|
82
|
+
*/
|
|
83
|
+
export function defaultEffort(format) {
|
|
84
|
+
switch (format) {
|
|
85
|
+
case 'avif':
|
|
86
|
+
return 4;
|
|
87
|
+
case 'webp':
|
|
88
|
+
return 4;
|
|
89
|
+
default:
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/format.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,OAAO,GAAgC;IAC3C,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,MAAM;CACb,CAAC;AAEF,uCAAuC;AACvC,MAAM,CAAC,MAAM,iBAAiB,GAA2B;IACvD,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;CACP,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,KAAgC;IAC9D,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,SAAS,CACjB,iBAAiB,EACjB,0BAA0B,KAAK,iBAAiB,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB,EACrG,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,KAA0B,EAAE,EAAE,CACpD,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAC9B,MAA8C;IAE9C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAe,CAAC;IACpC,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACjB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,aAAa,CAAC,MAAmB;IAC/C,OAAO,MAAM,KAAK,MAAM,CAAC;AAC3B,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,OAAO,CAAC,MAAmB;IACzC,OAAO,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,MAAM,CAAC;AACrE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,EAAE,CAAC;QACZ;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,MAAmB;IAC/C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;YACT,OAAO,CAAC,CAAC;QACX,KAAK,MAAM;YACT,OAAO,CAAC,CAAC;QACX;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Content hashing, via `node:crypto`. Used for cache keys, ETags and filenames. */
|
|
2
|
+
/** Full lowercase hex SHA-256 of some bytes. */
|
|
3
|
+
export declare function sha256(data: Uint8Array): string;
|
|
4
|
+
/**
|
|
5
|
+
* Short hash for use in URLs and filenames.
|
|
6
|
+
*
|
|
7
|
+
* 10 hex characters is 40 bits. For content-addressed asset names that is a
|
|
8
|
+
* ~1-in-a-million collision chance at a million distinct images, which is the
|
|
9
|
+
* same trade-off webpack and friends make for chunk hashes.
|
|
10
|
+
*/
|
|
11
|
+
export declare function shortHash(data: Uint8Array | string, length?: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* Stable hash of an arbitrary options object.
|
|
14
|
+
*
|
|
15
|
+
* Object keys are sorted so `{ a, b }` and `{ b, a }` produce the same cache
|
|
16
|
+
* key - otherwise property order would silently fragment the cache.
|
|
17
|
+
*/
|
|
18
|
+
export declare function hashOptions(value: unknown): string;
|
|
19
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/hash.ts"],"names":[],"mappings":"AAAA,oFAAoF;AAIpF,gDAAgD;AAChD,wBAAgB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAE/C;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,CAGxE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD"}
|
package/dist/esm/hash.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Content hashing, via `node:crypto`. Used for cache keys, ETags and filenames. */
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
/** Full lowercase hex SHA-256 of some bytes. */
|
|
4
|
+
export function sha256(data) {
|
|
5
|
+
return createHash('sha256').update(data).digest('hex');
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Short hash for use in URLs and filenames.
|
|
9
|
+
*
|
|
10
|
+
* 10 hex characters is 40 bits. For content-addressed asset names that is a
|
|
11
|
+
* ~1-in-a-million collision chance at a million distinct images, which is the
|
|
12
|
+
* same trade-off webpack and friends make for chunk hashes.
|
|
13
|
+
*/
|
|
14
|
+
export function shortHash(data, length = 10) {
|
|
15
|
+
const hash = typeof data === 'string' ? createHash('sha256').update(data).digest('hex') : sha256(data);
|
|
16
|
+
return hash.slice(0, length);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Stable hash of an arbitrary options object.
|
|
20
|
+
*
|
|
21
|
+
* Object keys are sorted so `{ a, b }` and `{ b, a }` produce the same cache
|
|
22
|
+
* key - otherwise property order would silently fragment the cache.
|
|
23
|
+
*/
|
|
24
|
+
export function hashOptions(value) {
|
|
25
|
+
return createHash('sha256').update(stableStringify(value)).digest('hex').slice(0, 16);
|
|
26
|
+
}
|
|
27
|
+
function stableStringify(value) {
|
|
28
|
+
if (value === null || typeof value !== 'object')
|
|
29
|
+
return JSON.stringify(value) ?? 'null';
|
|
30
|
+
if (Array.isArray(value))
|
|
31
|
+
return `[${value.map(stableStringify).join(',')}]`;
|
|
32
|
+
const entries = Object.entries(value)
|
|
33
|
+
.filter(([, v]) => v !== undefined)
|
|
34
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
35
|
+
.map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`);
|
|
36
|
+
return `{${entries.join(',')}}`;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/hash.ts"],"names":[],"mappings":"AAAA,oFAAoF;AAEpF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,gDAAgD;AAChD,MAAM,UAAU,MAAM,CAAC,IAAgB;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAyB,EAAE,MAAM,GAAG,EAAE;IAC9D,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvG,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC;IACxF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC7E,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;SAC7D,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;SAClC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SAChD,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@lens-image/core` - composable image optimization for Node.js.
|
|
3
|
+
*
|
|
4
|
+
* Zero runtime dependencies. The codec (sharp) and the storage backend are both
|
|
5
|
+
* injected interfaces, so the core is testable, swappable and installs nothing
|
|
6
|
+
* you did not ask for.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { ImageOptimizer } from '@lens-image/core';
|
|
11
|
+
* import { LocalAdapter } from '@lens-image/adapter-local';
|
|
12
|
+
*
|
|
13
|
+
* const optimizer = new ImageOptimizer({
|
|
14
|
+
* adapter: new LocalAdapter({ root: './public/images', baseUrl: '/images' }),
|
|
15
|
+
* quality: 80,
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* const result = await optimizer.optimize({
|
|
19
|
+
* source: './photo.jpg',
|
|
20
|
+
* formats: ['webp', 'jpg'],
|
|
21
|
+
* sizes: [{ width: 1200 }, { width: 600 }, { width: 300 }],
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* console.log(result.formats.webp.srcset);
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @packageDocumentation
|
|
28
|
+
*/
|
|
29
|
+
export { ImageOptimizer, DEFAULT_CACHE_CONTROL } from './optimizer.js';
|
|
30
|
+
export { LensError, throwIfAborted, wrapError } from './errors.js';
|
|
31
|
+
export type { LensErrorCode, LensErrorDetails } from './errors.js';
|
|
32
|
+
export { createSharpEngine, createPassthroughEngine, detectEngine, resetEngineDetection, resetSharpCache, } from './engines/index.js';
|
|
33
|
+
export type { SharpEngineOptions, PassthroughEngineOptions } from './engines/index.js';
|
|
34
|
+
export { MemoryAdapter } from './adapters/memory.js';
|
|
35
|
+
export type { MemoryAdapterOptions, MemoryEntry } from './adapters/memory.js';
|
|
36
|
+
export { MemoryCacheStore, nullCacheStore } from './cache.js';
|
|
37
|
+
export { createUploadHandler, serializeResult } from './server/handler.js';
|
|
38
|
+
export type { UploadHandlerOptions } from './server/handler.js';
|
|
39
|
+
export { buildSrcset, toPicture, pickVariant } from './srcset.js';
|
|
40
|
+
export type { PictureData, PictureSource } from './srcset.js';
|
|
41
|
+
export { sniff, sniffFormat, mimeTypeFor, extensionFor, SNIFF_HEADER_BYTES } from './sniff.js';
|
|
42
|
+
export { resolveSource, stem, formatBytes } from './source.js';
|
|
43
|
+
export type { ResolvedSource, ResolveOptions } from './source.js';
|
|
44
|
+
export { normalizeFormat, normalizeFormats, supportsAlpha, isLossy, defaultQuality, defaultEffort, ENCODABLE_FORMATS, } from './format.js';
|
|
45
|
+
export { buildKey, joinKey, slugify, defaultLabel, DEFAULT_KEY_TEMPLATE } from './naming.js';
|
|
46
|
+
export { validateImage, resolveValidation, DEFAULT_MAX_BYTES, DEFAULT_MAX_PIXELS, DEFAULT_ALLOWED_FORMATS, } from './validation.js';
|
|
47
|
+
export type { ResolvedValidation } from './validation.js';
|
|
48
|
+
export { withRetry, resolveRetry, backoffDelay, isRetryableError } from './retry.js';
|
|
49
|
+
export type { ResolvedRetry } from './retry.js';
|
|
50
|
+
export { mapLimit, delay } from './concurrency.js';
|
|
51
|
+
export type { Settled } from './concurrency.js';
|
|
52
|
+
export { sha256, shortHash, hashOptions } from './hash.js';
|
|
53
|
+
export type { CacheStore, DetectedFormat, EncodedImage, FitMode, FormatResult, ImageEngine, ImageFormat, ImageFormatInput, ImageMetadata, ImageOptimizerOptions, ImageSource, KeyContext, LensWarning, OptimizeOptions, OptimizeResult, Position, ResizeSpec, RetryOptions, SizeSpec, StorageAdapter, StorageContext, StorageFile, StorageObject, ThumbnailSpec, TransformOp, ValidationOptions, Variant, } from './types.js';
|
|
54
|
+
/** The version of `@lens-image/core`, useful in `User-Agent` strings and bug reports. */
|
|
55
|
+
export declare const VERSION = "0.1.0";
|
|
56
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAGvE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,oBAAoB,EACpB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAGvF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAG9E,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG9D,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,OAAO,EACP,cAAc,EACd,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC7F,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACrF,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACnD,YAAY,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG3D,YAAY,EACV,UAAU,EACV,cAAc,EACd,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,qBAAqB,EACrB,WAAW,EACX,UAAU,EACV,WAAW,EACX,eAAe,EACf,cAAc,EACd,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,cAAc,EACd,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,OAAO,GACR,MAAM,YAAY,CAAC;AAEpB,yFAAyF;AACzF,eAAO,MAAM,OAAO,UAAU,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@lens-image/core` - composable image optimization for Node.js.
|
|
3
|
+
*
|
|
4
|
+
* Zero runtime dependencies. The codec (sharp) and the storage backend are both
|
|
5
|
+
* injected interfaces, so the core is testable, swappable and installs nothing
|
|
6
|
+
* you did not ask for.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { ImageOptimizer } from '@lens-image/core';
|
|
11
|
+
* import { LocalAdapter } from '@lens-image/adapter-local';
|
|
12
|
+
*
|
|
13
|
+
* const optimizer = new ImageOptimizer({
|
|
14
|
+
* adapter: new LocalAdapter({ root: './public/images', baseUrl: '/images' }),
|
|
15
|
+
* quality: 80,
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* const result = await optimizer.optimize({
|
|
19
|
+
* source: './photo.jpg',
|
|
20
|
+
* formats: ['webp', 'jpg'],
|
|
21
|
+
* sizes: [{ width: 1200 }, { width: 600 }, { width: 300 }],
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* console.log(result.formats.webp.srcset);
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @packageDocumentation
|
|
28
|
+
*/
|
|
29
|
+
// --- The main entry point ---------------------------------------------------
|
|
30
|
+
export { ImageOptimizer, DEFAULT_CACHE_CONTROL } from './optimizer.js';
|
|
31
|
+
// --- Errors -----------------------------------------------------------------
|
|
32
|
+
export { LensError, throwIfAborted, wrapError } from './errors.js';
|
|
33
|
+
// --- Engines ----------------------------------------------------------------
|
|
34
|
+
export { createSharpEngine, createPassthroughEngine, detectEngine, resetEngineDetection, resetSharpCache, } from './engines/index.js';
|
|
35
|
+
// --- Built-in adapters ------------------------------------------------------
|
|
36
|
+
export { MemoryAdapter } from './adapters/memory.js';
|
|
37
|
+
// --- Caching ----------------------------------------------------------------
|
|
38
|
+
export { MemoryCacheStore, nullCacheStore } from './cache.js';
|
|
39
|
+
// --- Server helpers ---------------------------------------------------------
|
|
40
|
+
export { createUploadHandler, serializeResult } from './server/handler.js';
|
|
41
|
+
// --- HTML helpers -----------------------------------------------------------
|
|
42
|
+
export { buildSrcset, toPicture, pickVariant } from './srcset.js';
|
|
43
|
+
// --- Utilities, exported because adapter authors need them ------------------
|
|
44
|
+
export { sniff, sniffFormat, mimeTypeFor, extensionFor, SNIFF_HEADER_BYTES } from './sniff.js';
|
|
45
|
+
export { resolveSource, stem, formatBytes } from './source.js';
|
|
46
|
+
export { normalizeFormat, normalizeFormats, supportsAlpha, isLossy, defaultQuality, defaultEffort, ENCODABLE_FORMATS, } from './format.js';
|
|
47
|
+
export { buildKey, joinKey, slugify, defaultLabel, DEFAULT_KEY_TEMPLATE } from './naming.js';
|
|
48
|
+
export { validateImage, resolveValidation, DEFAULT_MAX_BYTES, DEFAULT_MAX_PIXELS, DEFAULT_ALLOWED_FORMATS, } from './validation.js';
|
|
49
|
+
export { withRetry, resolveRetry, backoffDelay, isRetryableError } from './retry.js';
|
|
50
|
+
export { mapLimit, delay } from './concurrency.js';
|
|
51
|
+
export { sha256, shortHash, hashOptions } from './hash.js';
|
|
52
|
+
/** The version of `@lens-image/core`, useful in `User-Agent` strings and bug reports. */
|
|
53
|
+
export const VERSION = '0.1.0';
|
|
54
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,+EAA+E;AAC/E,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvE,+EAA+E;AAC/E,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGnE,+EAA+E;AAC/E,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,YAAY,EACZ,oBAAoB,EACpB,eAAe,GAChB,MAAM,oBAAoB,CAAC;AAG5B,+EAA+E;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE9D,+EAA+E;AAC/E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG3E,+EAA+E;AAC/E,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGlE,+EAA+E;AAC/E,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,OAAO,EACP,cAAc,EACd,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC7F,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErF,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAiC3D,yFAAyF;AACzF,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output key templating.
|
|
3
|
+
*
|
|
4
|
+
* Keys are how everything downstream addresses a variant - the S3 object key,
|
|
5
|
+
* the path under a local root, the public URL. They must be deterministic
|
|
6
|
+
* (so caching works), URL-safe (so nobody has to think about encoding), and
|
|
7
|
+
* predictable enough that a developer can eyeball a bucket listing.
|
|
8
|
+
*/
|
|
9
|
+
import type { KeyContext } from './types.js';
|
|
10
|
+
/** `{hash}/{name}-{label}.{ext}` - content-addressed and cacheable forever. */
|
|
11
|
+
export declare const DEFAULT_KEY_TEMPLATE = "{hash}/{name}-{label}.{ext}";
|
|
12
|
+
/**
|
|
13
|
+
* Makes a string safe for a URL path segment.
|
|
14
|
+
*
|
|
15
|
+
* Lowercases, strips diacritics, collapses anything non-alphanumeric to a
|
|
16
|
+
* single dash. Empty input becomes `'image'` so a key is never malformed.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* slugify('Café Photo (2).JPG'); // 'cafe-photo-2-jpg'
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function slugify(input: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Renders a key template, or calls a key function.
|
|
26
|
+
*
|
|
27
|
+
* Tokens: `{name} {hash} {fullHash} {format} {ext} {width} {height}
|
|
28
|
+
* {quality} {label}`.
|
|
29
|
+
*
|
|
30
|
+
* @throws {LensError} `INVALID_OPTIONS` when the template references an unknown
|
|
31
|
+
* token, or a key function returns something unusable. Failing loudly here
|
|
32
|
+
* beats silently uploading a file called `undefined.webp`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* buildKey('{name}/{width}x{height}.{ext}', ctx); // 'hero/1200x800.webp'
|
|
37
|
+
* buildKey((c) => `${c.format}/${c.hash}.${c.ext}`, ctx);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildKey(template: string | ((ctx: KeyContext) => string), ctx: KeyContext, prefix?: string): string;
|
|
41
|
+
/** Joins a prefix and a key, normalising slashes and stripping `..` segments. */
|
|
42
|
+
export declare function joinKey(prefix: string, key: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Default label for a size spec: `1200w`, `800h`, or `1200x800` when both
|
|
45
|
+
* dimensions are pinned.
|
|
46
|
+
*/
|
|
47
|
+
export declare function defaultLabel(width?: number, height?: number): string;
|
|
48
|
+
//# sourceMappingURL=naming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../src/naming.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAIlE;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,UAAU,KAAK,MAAM,CAAC,EAChD,GAAG,EAAE,UAAU,EACf,MAAM,SAAK,GACV,MAAM,CAWR;AAeD,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAM3D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAKpE"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output key templating.
|
|
3
|
+
*
|
|
4
|
+
* Keys are how everything downstream addresses a variant - the S3 object key,
|
|
5
|
+
* the path under a local root, the public URL. They must be deterministic
|
|
6
|
+
* (so caching works), URL-safe (so nobody has to think about encoding), and
|
|
7
|
+
* predictable enough that a developer can eyeball a bucket listing.
|
|
8
|
+
*/
|
|
9
|
+
import { LensError } from './errors.js';
|
|
10
|
+
/** `{hash}/{name}-{label}.{ext}` - content-addressed and cacheable forever. */
|
|
11
|
+
export const DEFAULT_KEY_TEMPLATE = '{hash}/{name}-{label}.{ext}';
|
|
12
|
+
const TOKEN = /\{(\w+)\}/g;
|
|
13
|
+
/**
|
|
14
|
+
* Makes a string safe for a URL path segment.
|
|
15
|
+
*
|
|
16
|
+
* Lowercases, strips diacritics, collapses anything non-alphanumeric to a
|
|
17
|
+
* single dash. Empty input becomes `'image'` so a key is never malformed.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* slugify('Café Photo (2).JPG'); // 'cafe-photo-2-jpg'
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export function slugify(input) {
|
|
25
|
+
const slug = input
|
|
26
|
+
.normalize('NFKD')
|
|
27
|
+
.replace(/[̀-ͯ]/g, '')
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
30
|
+
.replace(/^-+|-+$/g, '')
|
|
31
|
+
.slice(0, 100);
|
|
32
|
+
return slug.length > 0 ? slug : 'image';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Renders a key template, or calls a key function.
|
|
36
|
+
*
|
|
37
|
+
* Tokens: `{name} {hash} {fullHash} {format} {ext} {width} {height}
|
|
38
|
+
* {quality} {label}`.
|
|
39
|
+
*
|
|
40
|
+
* @throws {LensError} `INVALID_OPTIONS` when the template references an unknown
|
|
41
|
+
* token, or a key function returns something unusable. Failing loudly here
|
|
42
|
+
* beats silently uploading a file called `undefined.webp`.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* buildKey('{name}/{width}x{height}.{ext}', ctx); // 'hero/1200x800.webp'
|
|
47
|
+
* buildKey((c) => `${c.format}/${c.hash}.${c.ext}`, ctx);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function buildKey(template, ctx, prefix = '') {
|
|
51
|
+
const raw = typeof template === 'function' ? template(ctx) : renderTemplate(template, ctx);
|
|
52
|
+
if (typeof raw !== 'string' || raw.trim().length === 0) {
|
|
53
|
+
throw new LensError('INVALID_OPTIONS', 'The key function must return a non-empty string. ' +
|
|
54
|
+
`Received ${raw === undefined ? 'undefined' : JSON.stringify(raw)}.`);
|
|
55
|
+
}
|
|
56
|
+
return joinKey(prefix, raw);
|
|
57
|
+
}
|
|
58
|
+
function renderTemplate(template, ctx) {
|
|
59
|
+
return template.replace(TOKEN, (_match, token) => {
|
|
60
|
+
if (!(token in ctx)) {
|
|
61
|
+
throw new LensError('INVALID_OPTIONS', `Unknown token "{${token}}" in key template "${template}". ` +
|
|
62
|
+
`Available: ${Object.keys(ctx).map((k) => `{${k}}`).join(' ')}.`);
|
|
63
|
+
}
|
|
64
|
+
return String(ctx[token]);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Joins a prefix and a key, normalising slashes and stripping `..` segments. */
|
|
68
|
+
export function joinKey(prefix, key) {
|
|
69
|
+
const parts = `${prefix}/${key}`
|
|
70
|
+
.split('/')
|
|
71
|
+
.map((segment) => segment.trim())
|
|
72
|
+
.filter((segment) => segment.length > 0 && segment !== '.' && segment !== '..');
|
|
73
|
+
return parts.join('/');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Default label for a size spec: `1200w`, `800h`, or `1200x800` when both
|
|
77
|
+
* dimensions are pinned.
|
|
78
|
+
*/
|
|
79
|
+
export function defaultLabel(width, height) {
|
|
80
|
+
if (width && height)
|
|
81
|
+
return `${width}x${height}`;
|
|
82
|
+
if (width)
|
|
83
|
+
return `${width}w`;
|
|
84
|
+
if (height)
|
|
85
|
+
return `${height}h`;
|
|
86
|
+
return 'original';
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=naming.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming.js","sourceRoot":"","sources":["../../src/naming.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The orchestrator: validate, plan, encode, upload, assemble.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately the only stateful class in the library. Everything it calls -
|
|
5
|
+
* sniffing, validation, key building, retry, the engine, the adapter - is a
|
|
6
|
+
* pure function or an injected interface, which is what makes the whole thing
|
|
7
|
+
* testable without a codec or a network.
|
|
8
|
+
*/
|
|
9
|
+
import type { ImageEngine, ImageMetadata, ImageOptimizerOptions, OptimizeOptions, OptimizeResult, StorageAdapter } from './types.js';
|
|
10
|
+
/** `public, max-age=31536000, immutable` - safe because keys are content-addressed. */
|
|
11
|
+
export declare const DEFAULT_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
12
|
+
/**
|
|
13
|
+
* Optimizes images and hands them to a storage backend.
|
|
14
|
+
*
|
|
15
|
+
* Construct one per configuration and reuse it - it memoises engine detection
|
|
16
|
+
* and owns the result cache, so a fresh instance per request throws both away.
|
|
17
|
+
*
|
|
18
|
+
* @example Upload responsive WebP + JPEG to S3
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { ImageOptimizer } from '@lens-image/core';
|
|
21
|
+
* import { S3Adapter } from '@lens-image/adapter-s3';
|
|
22
|
+
*
|
|
23
|
+
* const optimizer = new ImageOptimizer({
|
|
24
|
+
* adapter: new S3Adapter({ bucket: 'my-images', region: 'us-east-1' }),
|
|
25
|
+
* quality: 80,
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* const result = await optimizer.optimize({
|
|
29
|
+
* source: '/path/to/image.jpg',
|
|
30
|
+
* formats: ['webp', 'jpg'],
|
|
31
|
+
* sizes: [{ width: 1200 }, { width: 600 }, { width: 300 }],
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* result.formats.webp.urls['600w']; // 'https://my-images.s3.amazonaws.com/…-600w.webp'
|
|
35
|
+
* result.formats.webp.srcset; // ready for <source srcset={…}>
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example Process without storing
|
|
39
|
+
* ```ts
|
|
40
|
+
* const optimizer = new ImageOptimizer(); // no adapter
|
|
41
|
+
* const { variants } = await optimizer.optimize({ source: bytes, formats: ['webp'] });
|
|
42
|
+
* await writeFile('out.webp', variants[0].data!); // bytes come back on the variant
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare class ImageOptimizer {
|
|
46
|
+
#private;
|
|
47
|
+
constructor(options?: ImageOptimizerOptions);
|
|
48
|
+
/** The storage backend, or `null` in process-only mode. */
|
|
49
|
+
get adapter(): StorageAdapter | null;
|
|
50
|
+
/** Resolves the codec backend, running detection on first use. */
|
|
51
|
+
engine(): Promise<ImageEngine>;
|
|
52
|
+
/**
|
|
53
|
+
* Processes one image into every requested format and size.
|
|
54
|
+
*
|
|
55
|
+
* Formats are independent: if AVIF encoding fails, the WebP and JPEG outputs
|
|
56
|
+
* still resolve and the failure is reported on `result.warnings` rather than
|
|
57
|
+
* thrown. Only a total failure - nothing at all was produced - rejects.
|
|
58
|
+
*
|
|
59
|
+
* @throws {LensError} `VALIDATION_FAILED`, `UNSUPPORTED_INPUT`,
|
|
60
|
+
* `ALL_FORMATS_FAILED`, `UPLOAD_FAILED`, `ABORTED`.
|
|
61
|
+
*/
|
|
62
|
+
optimize(options: OptimizeOptions): Promise<OptimizeResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Optimizes several images with the same options.
|
|
65
|
+
*
|
|
66
|
+
* Runs sequentially by design. Each `optimize` call already parallelises its
|
|
67
|
+
* own variants, so overlapping whole images multiplies peak memory by the
|
|
68
|
+
* number of images - and decoded bitmaps, not encoded files, are what fills
|
|
69
|
+
* the heap. Wrap in your own queue if you have the headroom.
|
|
70
|
+
*/
|
|
71
|
+
optimizeMany(sources: readonly OptimizeOptions['source'][], options?: Omit<OptimizeOptions, 'source'>): Promise<OptimizeResult[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Reads an image's metadata without producing anything.
|
|
74
|
+
*
|
|
75
|
+
* Uses the engine when one is available and falls back to header sniffing,
|
|
76
|
+
* so it works with or without sharp installed.
|
|
77
|
+
*/
|
|
78
|
+
inspect(source: OptimizeOptions['source']): Promise<ImageMetadata>;
|
|
79
|
+
/**
|
|
80
|
+
* Deletes every variant of a previous result from storage.
|
|
81
|
+
*
|
|
82
|
+
* @throws {LensError} `ADAPTER_REQUIRED` with no adapter configured,
|
|
83
|
+
* `ADAPTER_UNSUPPORTED` when the adapter cannot delete.
|
|
84
|
+
*/
|
|
85
|
+
delete(result: OptimizeResult): Promise<void>;
|
|
86
|
+
/** Releases adapter resources. Safe to call more than once. */
|
|
87
|
+
dispose(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=optimizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimizer.d.ts","sourceRoot":"","sources":["../../src/optimizer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,OAAO,KAAK,EAGV,WAAW,EAEX,aAAa,EACb,qBAAqB,EAGrB,eAAe,EACf,cAAc,EAGd,cAAc,EAKf,MAAM,YAAY,CAAC;AAEpB,uFAAuF;AACvF,eAAO,MAAM,qBAAqB,wCAAwC,CAAC;AAW3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,cAAc;;gBAMb,OAAO,GAAE,qBAA0B;IAkB/C,2DAA2D;IAC3D,IAAI,OAAO,IAAI,cAAc,GAAG,IAAI,CAEnC;IAED,kEAAkE;IAC5D,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC;IAKpC;;;;;;;;;OASG;IACG,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAmJjE;;;;;;;OAOG;IACG,YAAY,CAChB,OAAO,EAAE,SAAS,eAAe,CAAC,QAAQ,CAAC,EAAE,EAC7C,OAAO,GAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAM,GAC5C,OAAO,CAAC,cAAc,EAAE,CAAC;IAQ5B;;;;;OAKG;IACG,OAAO,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAOxE;;;;;OAKG;IACG,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBnD,+DAA+D;IACzD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAqa/B"}
|