@veryfront/ext-image-sharp 0.1.1186 → 0.1.1189
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/esm/deno.d.ts +37 -0
- package/esm/deno.d.ts.map +1 -0
- package/esm/deno.js +33 -0
- package/esm/src/index.d.ts +18 -0
- package/esm/src/index.d.ts.map +1 -0
- package/esm/src/index.js +140 -0
- package/esm/src/sharp-runtime.d.ts +55 -0
- package/esm/src/sharp-runtime.d.ts.map +1 -0
- package/esm/src/sharp-runtime.js +573 -0
- package/package.json +3 -6
package/esm/deno.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
declare namespace _default {
|
|
2
|
+
let name: string;
|
|
3
|
+
let version: string;
|
|
4
|
+
let exports: string;
|
|
5
|
+
namespace veryfront {
|
|
6
|
+
let extension: boolean;
|
|
7
|
+
let activation: string;
|
|
8
|
+
namespace contracts {
|
|
9
|
+
let provides: string[];
|
|
10
|
+
}
|
|
11
|
+
let capabilities: ({
|
|
12
|
+
type: string;
|
|
13
|
+
paths: string[];
|
|
14
|
+
keys?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
type: string;
|
|
17
|
+
keys: string[];
|
|
18
|
+
paths?: undefined;
|
|
19
|
+
} | {
|
|
20
|
+
type: string;
|
|
21
|
+
paths?: undefined;
|
|
22
|
+
keys?: undefined;
|
|
23
|
+
})[];
|
|
24
|
+
}
|
|
25
|
+
let imports: {
|
|
26
|
+
sharp: string;
|
|
27
|
+
"@std/assert": string;
|
|
28
|
+
"@std/testing/bdd": string;
|
|
29
|
+
"veryfront/extensions": string;
|
|
30
|
+
"veryfront/extensions/image": string;
|
|
31
|
+
};
|
|
32
|
+
namespace tasks {
|
|
33
|
+
let test: string;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export default _default;
|
|
37
|
+
//# sourceMappingURL=deno.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deno.d.ts","sourceRoot":"","sources":["../src/deno.js"],"names":[],"mappings":""}
|
package/esm/deno.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
"name": "@veryfront/ext-image-sharp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"exports": "./src/index.ts",
|
|
5
|
+
"veryfront": {
|
|
6
|
+
"extension": true,
|
|
7
|
+
"activation": "explicit",
|
|
8
|
+
"contracts": {
|
|
9
|
+
"provides": ["ImageOptimizationEngine"]
|
|
10
|
+
},
|
|
11
|
+
"capabilities": [
|
|
12
|
+
{
|
|
13
|
+
"type": "fs:read",
|
|
14
|
+
"paths": ["/proc/self/exe", "/usr/bin/ldd"]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"type": "env:read",
|
|
18
|
+
"keys": ["MALLOC_ARENA_MAX", "npm_package_config_libvips"]
|
|
19
|
+
},
|
|
20
|
+
{ "type": "native:ffi" }
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"imports": {
|
|
24
|
+
"sharp": "npm:sharp@0.35.3",
|
|
25
|
+
"@std/assert": "jsr:@std/assert@1.0.19",
|
|
26
|
+
"@std/testing/bdd": "jsr:@std/testing@1.0.17/bdd",
|
|
27
|
+
"veryfront/extensions": "../../src/extensions/types.ts",
|
|
28
|
+
"veryfront/extensions/image": "../../src/extensions/image/index.ts"
|
|
29
|
+
},
|
|
30
|
+
"tasks": {
|
|
31
|
+
"test": "deno test --allow-read=/proc/self/exe,/usr/bin/ldd --allow-env=MALLOC_ARENA_MAX,npm_package_config_libvips --allow-ffi src/"
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sharp implementation of the dependency-free ImageOptimizationEngine.
|
|
3
|
+
*
|
|
4
|
+
* @module extensions/ext-image-sharp
|
|
5
|
+
*/
|
|
6
|
+
import "../_dnt.polyfills.js";
|
|
7
|
+
import type { ExtensionFactory } from "veryfront/extensions";
|
|
8
|
+
import { type ImageOptimizationEngine, type ImageOptimizationRequest, type ImageOptimizationResult } from "veryfront/extensions/image";
|
|
9
|
+
/** Native image decoder, resizer, and encoder supplied by this extension. */
|
|
10
|
+
export declare class SharpImageOptimizationEngine implements ImageOptimizationEngine {
|
|
11
|
+
#private;
|
|
12
|
+
readonly cacheIdentity: string;
|
|
13
|
+
constructor();
|
|
14
|
+
optimize(request: ImageOptimizationRequest): Promise<ImageOptimizationResult>;
|
|
15
|
+
}
|
|
16
|
+
declare const factory: ExtensionFactory;
|
|
17
|
+
export default factory;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EACL,KAAK,uBAAuB,EAE5B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAE7B,MAAM,4BAA4B,CAAC;AAyEpC,6EAA6E;AAC7E,qBAAa,4BAA6B,YAAW,uBAAuB;;IAC1E,QAAQ,CAAC,aAAa,EAAG,MAAM,CAAC;;IAiBhC,QAAQ,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;CAG9E;AA4BD,QAAA,MAAM,OAAO,EAAE,gBA2Bd,CAAC;AAEF,eAAe,OAAO,CAAC"}
|
package/esm/src/index.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sharp implementation of the dependency-free ImageOptimizationEngine.
|
|
3
|
+
*
|
|
4
|
+
* @module extensions/ext-image-sharp
|
|
5
|
+
*/
|
|
6
|
+
import "../_dnt.polyfills.js";
|
|
7
|
+
import { ImageOptimizationEngineName, MAX_IMAGE_OPTIMIZATION_ENGINE_IDENTITY_CHARACTERS, } from "veryfront/extensions/image";
|
|
8
|
+
import { arch, platform } from "node:process";
|
|
9
|
+
import sharp from "sharp";
|
|
10
|
+
import extensionPackage from "../deno.js";
|
|
11
|
+
import { BoundSharpImageOptimizationEngine, captureSharpRuntime, SHARP_IMAGE_LIMITS, } from "./sharp-runtime.js";
|
|
12
|
+
const ENGINE_SEMANTICS_VERSION = "veryfront.image-sharp.v2";
|
|
13
|
+
const SHARP_VERSION_PATTERN = /^npm:sharp@((?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*))$/;
|
|
14
|
+
const VERSION_COMPONENTS = [
|
|
15
|
+
"vips",
|
|
16
|
+
"aom",
|
|
17
|
+
"ffi",
|
|
18
|
+
"heif",
|
|
19
|
+
"mozjpeg",
|
|
20
|
+
"png",
|
|
21
|
+
"webp",
|
|
22
|
+
"zlib-ng",
|
|
23
|
+
];
|
|
24
|
+
const executeRegularExpression = RegExp.prototype.exec;
|
|
25
|
+
const apply = Reflect.apply;
|
|
26
|
+
const arrayIsArray = Array.isArray;
|
|
27
|
+
const arrayJoin = Array.prototype.join;
|
|
28
|
+
const arrayPush = Array.prototype.push;
|
|
29
|
+
const defineProperty = Object.defineProperty;
|
|
30
|
+
const freeze = Object.freeze;
|
|
31
|
+
const getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
|
|
32
|
+
const getPrototypeOf = Object.getPrototypeOf;
|
|
33
|
+
const ownKeys = Reflect.ownKeys;
|
|
34
|
+
const standardObjectPrototype = Object.prototype;
|
|
35
|
+
const sharpRuntime = captureSharpRuntime(sharp);
|
|
36
|
+
function exactSharpVersion(specifier) {
|
|
37
|
+
const match = apply(executeRegularExpression, SHARP_VERSION_PATTERN, [specifier]);
|
|
38
|
+
if (!match?.[1]) {
|
|
39
|
+
throw new TypeError("ext-image-sharp requires an exact Sharp dependency version");
|
|
40
|
+
}
|
|
41
|
+
return match[1];
|
|
42
|
+
}
|
|
43
|
+
function createCacheIdentity() {
|
|
44
|
+
const components = [
|
|
45
|
+
`ext-image-sharp@${extensionPackage.version}`,
|
|
46
|
+
`sharp@${exactSharpVersion(extensionPackage.imports.sharp)}`,
|
|
47
|
+
ENGINE_SEMANTICS_VERSION,
|
|
48
|
+
`runtime@${platform}-${arch}`,
|
|
49
|
+
];
|
|
50
|
+
for (let index = 0; index < VERSION_COMPONENTS.length; index++) {
|
|
51
|
+
const name = VERSION_COMPONENTS[index];
|
|
52
|
+
const version = sharpRuntime.versions[name];
|
|
53
|
+
if (typeof version !== "string" || version.length === 0) {
|
|
54
|
+
throw new TypeError(`Sharp did not report its ${name} backend version`);
|
|
55
|
+
}
|
|
56
|
+
apply(arrayPush, components, [`${name}@${version}`]);
|
|
57
|
+
}
|
|
58
|
+
apply(arrayPush, components, [
|
|
59
|
+
`limits@${SHARP_IMAGE_LIMITS.maxDecodedPixels}:` +
|
|
60
|
+
`${SHARP_IMAGE_LIMITS.maxOutputBytesPerVariant}:` +
|
|
61
|
+
`${SHARP_IMAGE_LIMITS.maxTotalOutputBytes}`,
|
|
62
|
+
]);
|
|
63
|
+
const identity = apply(arrayJoin, components, ["|"]);
|
|
64
|
+
if (identity.length > MAX_IMAGE_OPTIMIZATION_ENGINE_IDENTITY_CHARACTERS) {
|
|
65
|
+
throw new TypeError("ext-image-sharp cache identity exceeds the core limit");
|
|
66
|
+
}
|
|
67
|
+
return identity;
|
|
68
|
+
}
|
|
69
|
+
const CACHE_IDENTITY = createCacheIdentity();
|
|
70
|
+
/** Native image decoder, resizer, and encoder supplied by this extension. */
|
|
71
|
+
export class SharpImageOptimizationEngine {
|
|
72
|
+
cacheIdentity;
|
|
73
|
+
#engine;
|
|
74
|
+
constructor() {
|
|
75
|
+
this.#engine = new BoundSharpImageOptimizationEngine(sharpRuntime, CACHE_IDENTITY);
|
|
76
|
+
defineProperty(this, "cacheIdentity", {
|
|
77
|
+
value: CACHE_IDENTITY,
|
|
78
|
+
enumerable: true,
|
|
79
|
+
configurable: false,
|
|
80
|
+
writable: false,
|
|
81
|
+
});
|
|
82
|
+
freeze(this);
|
|
83
|
+
}
|
|
84
|
+
optimize(request) {
|
|
85
|
+
return this.#engine.optimize(request);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
freeze(SharpImageOptimizationEngine.prototype);
|
|
89
|
+
freeze(SharpImageOptimizationEngine);
|
|
90
|
+
function readConfig(value) {
|
|
91
|
+
if (value === undefined)
|
|
92
|
+
return;
|
|
93
|
+
if (typeof value !== "object" || value === null || arrayIsArray(value)) {
|
|
94
|
+
throw new TypeError("ext-image-sharp config must be an object");
|
|
95
|
+
}
|
|
96
|
+
let prototype;
|
|
97
|
+
let keys;
|
|
98
|
+
try {
|
|
99
|
+
prototype = getPrototypeOf(value);
|
|
100
|
+
keys = ownKeys(getOwnPropertyDescriptors(value));
|
|
101
|
+
}
|
|
102
|
+
catch (cause) {
|
|
103
|
+
throw new TypeError("ext-image-sharp config could not be inspected", {
|
|
104
|
+
cause,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (prototype !== standardObjectPrototype && prototype !== null) {
|
|
108
|
+
throw new TypeError("ext-image-sharp config must not inherit configuration");
|
|
109
|
+
}
|
|
110
|
+
if (keys.length !== 0) {
|
|
111
|
+
throw new TypeError("ext-image-sharp does not accept configuration properties");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const factory = (config) => {
|
|
115
|
+
readConfig(config);
|
|
116
|
+
const engine = new SharpImageOptimizationEngine();
|
|
117
|
+
return {
|
|
118
|
+
name: "ext-image-sharp",
|
|
119
|
+
version: extensionPackage.version,
|
|
120
|
+
capabilities: [
|
|
121
|
+
{
|
|
122
|
+
type: "fs:read",
|
|
123
|
+
paths: ["/proc/self/exe", "/usr/bin/ldd"],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
type: "env:read",
|
|
127
|
+
keys: ["MALLOC_ARENA_MAX", "npm_package_config_libvips"],
|
|
128
|
+
},
|
|
129
|
+
{ type: "native:ffi" },
|
|
130
|
+
],
|
|
131
|
+
contracts: {
|
|
132
|
+
provides: ["ImageOptimizationEngine"],
|
|
133
|
+
},
|
|
134
|
+
setup(ctx) {
|
|
135
|
+
ctx.provide(ImageOptimizationEngineName, engine);
|
|
136
|
+
ctx.logger.debug(`[ext-image-sharp] ${ImageOptimizationEngineName} registered`);
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
export default factory;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Descriptor-safe Sharp runtime capture and bounded image optimization.
|
|
3
|
+
*
|
|
4
|
+
* This module is intentionally not a package entry point. The public extension
|
|
5
|
+
* binds it to the exact Sharp dependency declared by the extension manifest.
|
|
6
|
+
*/
|
|
7
|
+
import type { ImageOptimizationEngine, ImageOptimizationRequest, ImageOptimizationResult } from "veryfront/extensions/image";
|
|
8
|
+
/** Provider-level resource limits, independent of core's stricter boundary. */
|
|
9
|
+
export declare const SHARP_IMAGE_LIMITS: Readonly<{
|
|
10
|
+
maxInputBytes: number;
|
|
11
|
+
maxDecodedPixels: 100000000;
|
|
12
|
+
maxDimension: 32768;
|
|
13
|
+
maxTargetWidths: 64;
|
|
14
|
+
maxFormats: 4;
|
|
15
|
+
maxVariants: 260;
|
|
16
|
+
maxOutputBytesPerVariant: number;
|
|
17
|
+
maxTotalOutputBytes: number;
|
|
18
|
+
}>;
|
|
19
|
+
export interface SharpImageLimits {
|
|
20
|
+
readonly maxInputBytes: number;
|
|
21
|
+
readonly maxDecodedPixels: number;
|
|
22
|
+
readonly maxDimension: number;
|
|
23
|
+
readonly maxTargetWidths: number;
|
|
24
|
+
readonly maxFormats: number;
|
|
25
|
+
readonly maxVariants: number;
|
|
26
|
+
readonly maxOutputBytesPerVariant: number;
|
|
27
|
+
readonly maxTotalOutputBytes: number;
|
|
28
|
+
}
|
|
29
|
+
/** Captured calls into one exact Sharp module implementation. */
|
|
30
|
+
export interface CapturedSharpRuntime {
|
|
31
|
+
readonly versions: Readonly<Record<string, string>>;
|
|
32
|
+
create(input: Uint8Array, options: Readonly<Record<string, unknown>>): object;
|
|
33
|
+
autoOrient(pipeline: object): object;
|
|
34
|
+
metadata(pipeline: object): unknown;
|
|
35
|
+
clone(pipeline: object): object;
|
|
36
|
+
resize(pipeline: object, options: Readonly<Record<string, unknown>>): object;
|
|
37
|
+
webp(pipeline: object, options: Readonly<Record<string, unknown>>): object;
|
|
38
|
+
avif(pipeline: object, options: Readonly<Record<string, unknown>>): object;
|
|
39
|
+
jpeg(pipeline: object, options: Readonly<Record<string, unknown>>): object;
|
|
40
|
+
png(pipeline: object, options: Readonly<Record<string, unknown>>): object;
|
|
41
|
+
toBuffer(pipeline: object, options: Readonly<Record<string, unknown>>): unknown;
|
|
42
|
+
}
|
|
43
|
+
/** Capture Sharp's callable, versions, and prototype methods exactly once. */
|
|
44
|
+
export declare function captureSharpRuntime(value: unknown): CapturedSharpRuntime;
|
|
45
|
+
/**
|
|
46
|
+
* Internal engine used by the public extension and focused fake-runtime tests.
|
|
47
|
+
* Every call snapshots one request and creates an independent Sharp pipeline.
|
|
48
|
+
*/
|
|
49
|
+
export declare class BoundSharpImageOptimizationEngine implements ImageOptimizationEngine {
|
|
50
|
+
#private;
|
|
51
|
+
readonly cacheIdentity: string;
|
|
52
|
+
constructor(runtime: CapturedSharpRuntime, cacheIdentity: string, limits?: SharpImageLimits);
|
|
53
|
+
optimize(request: ImageOptimizationRequest): Promise<ImageOptimizationResult>;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=sharp-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sharp-runtime.d.ts","sourceRoot":"","sources":["../../src/src/sharp-runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,uBAAuB,EAEvB,wBAAwB,EACxB,uBAAuB,EAExB,MAAM,4BAA4B,CAAC;AAgEpC,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB;;;;;;;;;EAS7B,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAID,iEAAiE;AACjE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC;IAC9E,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC;IAC7E,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC;IAC3E,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC;IAC3E,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC;IAC3E,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC;CACjF;AAgXD,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CA6FxE;AAkMD;;;GAGG;AACH,qBAAa,iCAAkC,YAAW,uBAAuB;;IAC/E,QAAQ,CAAC,aAAa,EAAG,MAAM,CAAC;gBAK9B,OAAO,EAAE,oBAAoB,EAC7B,aAAa,EAAE,MAAM,EACrB,MAAM,GAAE,gBAAqC;IAazC,QAAQ,CACZ,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC;CAkEpC"}
|
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Descriptor-safe Sharp runtime capture and bounded image optimization.
|
|
3
|
+
*
|
|
4
|
+
* This module is intentionally not a package entry point. The public extension
|
|
5
|
+
* binds it to the exact Sharp dependency declared by the extension manifest.
|
|
6
|
+
*/
|
|
7
|
+
const apply = Reflect.apply;
|
|
8
|
+
const arrayIncludes = Array.prototype.includes;
|
|
9
|
+
const arrayIsArray = Array.isArray;
|
|
10
|
+
const arrayPush = Array.prototype.push;
|
|
11
|
+
const arraySort = Array.prototype.sort;
|
|
12
|
+
const createObject = Object.create;
|
|
13
|
+
const defineProperty = Object.defineProperty;
|
|
14
|
+
const freeze = Object.freeze;
|
|
15
|
+
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
16
|
+
const getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
|
|
17
|
+
const getPrototypeOf = Object.getPrototypeOf;
|
|
18
|
+
const hasOwn = Object.hasOwn;
|
|
19
|
+
const numberIsInteger = Number.isInteger;
|
|
20
|
+
const numberIsSafeInteger = Number.isSafeInteger;
|
|
21
|
+
const ownKeys = Reflect.ownKeys;
|
|
22
|
+
const promiseResolve = Promise.resolve;
|
|
23
|
+
const PromiseConstructor = Promise;
|
|
24
|
+
const setAdd = Set.prototype.add;
|
|
25
|
+
const setHas = Set.prototype.has;
|
|
26
|
+
const SetConstructor = Set;
|
|
27
|
+
const setBytes = Uint8Array.prototype.set;
|
|
28
|
+
const sortNumbers = (left, right) => left - right;
|
|
29
|
+
const standardArrayPrototype = Array.prototype;
|
|
30
|
+
const standardObjectPrototype = Object.prototype;
|
|
31
|
+
const Uint8ArrayConstructor = Uint8Array;
|
|
32
|
+
const typedArrayPrototype = getPrototypeOf(Uint8Array.prototype);
|
|
33
|
+
const byteLengthGetter = getOwnPropertyDescriptor(typedArrayPrototype, "byteLength")?.get;
|
|
34
|
+
const typedArrayNameGetter = getOwnPropertyDescriptor(typedArrayPrototype, Symbol.toStringTag)?.get;
|
|
35
|
+
const abortedGetter = getOwnPropertyDescriptor(AbortSignal.prototype, "aborted")?.get;
|
|
36
|
+
if (!byteLengthGetter || !typedArrayNameGetter || !abortedGetter) {
|
|
37
|
+
throw new TypeError("Required image optimization intrinsics are unavailable");
|
|
38
|
+
}
|
|
39
|
+
const REQUEST_PROPERTIES = freeze([
|
|
40
|
+
"input",
|
|
41
|
+
"targetWidths",
|
|
42
|
+
"formats",
|
|
43
|
+
"quality",
|
|
44
|
+
"signal",
|
|
45
|
+
]);
|
|
46
|
+
const SUPPORTED_FORMATS = freeze([
|
|
47
|
+
"webp",
|
|
48
|
+
"avif",
|
|
49
|
+
"jpeg",
|
|
50
|
+
"png",
|
|
51
|
+
]);
|
|
52
|
+
const MAX_SHARP_PROTOTYPE_DEPTH = 16;
|
|
53
|
+
/** Provider-level resource limits, independent of core's stricter boundary. */
|
|
54
|
+
export const SHARP_IMAGE_LIMITS = freeze({
|
|
55
|
+
maxInputBytes: 64 * 1024 * 1024,
|
|
56
|
+
maxDecodedPixels: 100_000_000,
|
|
57
|
+
maxDimension: 32_768,
|
|
58
|
+
maxTargetWidths: 64,
|
|
59
|
+
maxFormats: SUPPORTED_FORMATS.length,
|
|
60
|
+
maxVariants: 260,
|
|
61
|
+
maxOutputBytesPerVariant: 64 * 1024 * 1024,
|
|
62
|
+
maxTotalOutputBytes: 256 * 1024 * 1024,
|
|
63
|
+
});
|
|
64
|
+
function invoke(method, receiver, args) {
|
|
65
|
+
return apply(method, receiver, args);
|
|
66
|
+
}
|
|
67
|
+
function ownDescriptors(value, label) {
|
|
68
|
+
try {
|
|
69
|
+
return getOwnPropertyDescriptors(value);
|
|
70
|
+
}
|
|
71
|
+
catch (cause) {
|
|
72
|
+
throw new TypeError(`${label} properties could not be inspected`, { cause });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function readDataProperty(descriptors, property, label) {
|
|
76
|
+
const descriptor = descriptors[property];
|
|
77
|
+
if (descriptor === undefined || !hasOwn(descriptor, "value")) {
|
|
78
|
+
throw new TypeError(`${label} ${property} must be a data property`);
|
|
79
|
+
}
|
|
80
|
+
return descriptor.value;
|
|
81
|
+
}
|
|
82
|
+
function readOptionalDataProperty(descriptors, property, label) {
|
|
83
|
+
const descriptor = descriptors[property];
|
|
84
|
+
if (descriptor === undefined)
|
|
85
|
+
return undefined;
|
|
86
|
+
if (!hasOwn(descriptor, "value")) {
|
|
87
|
+
throw new TypeError(`${label} ${property} must be a data property`);
|
|
88
|
+
}
|
|
89
|
+
return descriptor.value;
|
|
90
|
+
}
|
|
91
|
+
function assertPlainRecord(value, label) {
|
|
92
|
+
let prototype;
|
|
93
|
+
try {
|
|
94
|
+
prototype = getPrototypeOf(value);
|
|
95
|
+
}
|
|
96
|
+
catch (cause) {
|
|
97
|
+
throw new TypeError(`${label} prototype could not be inspected`, { cause });
|
|
98
|
+
}
|
|
99
|
+
if (prototype !== standardObjectPrototype && prototype !== null) {
|
|
100
|
+
throw new TypeError(`${label} must not inherit custom behavior`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function assertExactProperties(descriptors, expected, label) {
|
|
104
|
+
const keys = ownKeys(descriptors);
|
|
105
|
+
if (keys.length !== expected.length) {
|
|
106
|
+
throw new TypeError(`${label} contains unsupported properties`);
|
|
107
|
+
}
|
|
108
|
+
for (let index = 0; index < expected.length; index++) {
|
|
109
|
+
if (!hasOwn(descriptors, expected[index])) {
|
|
110
|
+
throw new TypeError(`${label} contains unsupported properties`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function denseDataArray(value, maximumLength, label) {
|
|
115
|
+
if (!arrayIsArray(value)) {
|
|
116
|
+
throw new TypeError(`${label} must be an array`);
|
|
117
|
+
}
|
|
118
|
+
let prototype;
|
|
119
|
+
try {
|
|
120
|
+
prototype = getPrototypeOf(value);
|
|
121
|
+
}
|
|
122
|
+
catch (cause) {
|
|
123
|
+
throw new TypeError(`${label} prototype could not be inspected`, { cause });
|
|
124
|
+
}
|
|
125
|
+
if (prototype !== standardArrayPrototype) {
|
|
126
|
+
throw new TypeError(`${label} must use the standard array prototype`);
|
|
127
|
+
}
|
|
128
|
+
const descriptors = ownDescriptors(value, label);
|
|
129
|
+
const length = readDataProperty(descriptors, "length", label);
|
|
130
|
+
if (!numberIsSafeInteger(length) ||
|
|
131
|
+
length < 0 ||
|
|
132
|
+
length > maximumLength) {
|
|
133
|
+
throw new TypeError(`${label} exceeds its supported length`);
|
|
134
|
+
}
|
|
135
|
+
const entries = [];
|
|
136
|
+
for (let index = 0; index < length; index++) {
|
|
137
|
+
const descriptor = descriptors[String(index)];
|
|
138
|
+
if (descriptor === undefined ||
|
|
139
|
+
!hasOwn(descriptor, "value") ||
|
|
140
|
+
descriptor.enumerable !== true) {
|
|
141
|
+
throw new TypeError(`${label} must be a dense data-property array`);
|
|
142
|
+
}
|
|
143
|
+
invoke(arrayPush, entries, [descriptor.value]);
|
|
144
|
+
}
|
|
145
|
+
if (ownKeys(descriptors).length !== entries.length + 1) {
|
|
146
|
+
throw new TypeError(`${label} must not define custom properties`);
|
|
147
|
+
}
|
|
148
|
+
return entries;
|
|
149
|
+
}
|
|
150
|
+
function byteLength(value, label) {
|
|
151
|
+
try {
|
|
152
|
+
return invoke(byteLengthGetter, value, []);
|
|
153
|
+
}
|
|
154
|
+
catch (cause) {
|
|
155
|
+
throw new TypeError(`${label} must be a Uint8Array`, { cause });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function copyBytes(value, maximumLength, label) {
|
|
159
|
+
let typedArrayName;
|
|
160
|
+
try {
|
|
161
|
+
typedArrayName = invoke(typedArrayNameGetter, value, []);
|
|
162
|
+
}
|
|
163
|
+
catch (cause) {
|
|
164
|
+
throw new TypeError(`${label} must be a Uint8Array`, { cause });
|
|
165
|
+
}
|
|
166
|
+
if (typedArrayName !== "Uint8Array") {
|
|
167
|
+
throw new TypeError(`${label} must be a Uint8Array`);
|
|
168
|
+
}
|
|
169
|
+
const length = byteLength(value, label);
|
|
170
|
+
if (length === 0) {
|
|
171
|
+
throw new TypeError(`${label} must not be empty`);
|
|
172
|
+
}
|
|
173
|
+
if (length > maximumLength) {
|
|
174
|
+
throw new TypeError(`${label} exceeds ${maximumLength} bytes`);
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const copy = new Uint8ArrayConstructor(length);
|
|
178
|
+
invoke(setBytes, copy, [value]);
|
|
179
|
+
return copy;
|
|
180
|
+
}
|
|
181
|
+
catch (cause) {
|
|
182
|
+
throw new TypeError(`${label} could not be copied`, { cause });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function isAborted(signal) {
|
|
186
|
+
return invoke(abortedGetter, signal, []);
|
|
187
|
+
}
|
|
188
|
+
function snapshotAbortSignal(value) {
|
|
189
|
+
try {
|
|
190
|
+
invoke(abortedGetter, value, []);
|
|
191
|
+
}
|
|
192
|
+
catch (cause) {
|
|
193
|
+
throw new TypeError("Image optimization signal must be an AbortSignal", {
|
|
194
|
+
cause,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
function cancellationError(cause) {
|
|
200
|
+
const message = "Image optimization was cancelled or exceeded its deadline";
|
|
201
|
+
return cause === undefined ? new Error(message) : new Error(message, { cause });
|
|
202
|
+
}
|
|
203
|
+
function throwIfAborted(signal) {
|
|
204
|
+
if (isAborted(signal))
|
|
205
|
+
throw cancellationError();
|
|
206
|
+
}
|
|
207
|
+
async function awaitNativeOperation(value, signal) {
|
|
208
|
+
const operation = invoke(promiseResolve, PromiseConstructor, [value]);
|
|
209
|
+
let result;
|
|
210
|
+
try {
|
|
211
|
+
result = await operation;
|
|
212
|
+
}
|
|
213
|
+
catch (cause) {
|
|
214
|
+
if (isAborted(signal))
|
|
215
|
+
throw cancellationError(cause);
|
|
216
|
+
throw cause;
|
|
217
|
+
}
|
|
218
|
+
throwIfAborted(signal);
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
function snapshotRequest(value, limits) {
|
|
222
|
+
if (typeof value !== "object" || value === null || arrayIsArray(value)) {
|
|
223
|
+
throw new TypeError("Image optimization request must be an object");
|
|
224
|
+
}
|
|
225
|
+
assertPlainRecord(value, "Image optimization request");
|
|
226
|
+
const descriptors = ownDescriptors(value, "Image optimization request");
|
|
227
|
+
assertExactProperties(descriptors, REQUEST_PROPERTIES, "Image optimization request");
|
|
228
|
+
const signal = snapshotAbortSignal(readDataProperty(descriptors, "signal", "Image optimization request"));
|
|
229
|
+
throwIfAborted(signal);
|
|
230
|
+
const input = copyBytes(readDataProperty(descriptors, "input", "Image optimization request"), limits.maxInputBytes, "Image optimization input");
|
|
231
|
+
const targetWidthEntries = denseDataArray(readDataProperty(descriptors, "targetWidths", "Image optimization request"), limits.maxTargetWidths, "Image optimization target widths");
|
|
232
|
+
const targetWidths = [];
|
|
233
|
+
for (let index = 0; index < targetWidthEntries.length; index++) {
|
|
234
|
+
const width = targetWidthEntries[index];
|
|
235
|
+
if (!numberIsInteger(width) ||
|
|
236
|
+
width <= 0 ||
|
|
237
|
+
width > limits.maxDimension) {
|
|
238
|
+
throw new TypeError(`Image optimization target widths must be integers from 1 through ${limits.maxDimension}`);
|
|
239
|
+
}
|
|
240
|
+
if (!invoke(arrayIncludes, targetWidths, [width])) {
|
|
241
|
+
invoke(arrayPush, targetWidths, [width]);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const formatEntries = denseDataArray(readDataProperty(descriptors, "formats", "Image optimization request"), limits.maxFormats, "Image optimization formats");
|
|
245
|
+
if (formatEntries.length === 0) {
|
|
246
|
+
throw new TypeError("Image optimization formats must not be empty");
|
|
247
|
+
}
|
|
248
|
+
const requestedFormats = new SetConstructor();
|
|
249
|
+
for (let index = 0; index < formatEntries.length; index++) {
|
|
250
|
+
const format = formatEntries[index];
|
|
251
|
+
if (format !== "webp" && format !== "avif" && format !== "jpeg" &&
|
|
252
|
+
format !== "png") {
|
|
253
|
+
throw new TypeError("Image optimization format is unsupported");
|
|
254
|
+
}
|
|
255
|
+
if (invoke(setHas, requestedFormats, [format])) {
|
|
256
|
+
throw new TypeError("Image optimization formats must be unique");
|
|
257
|
+
}
|
|
258
|
+
invoke(setAdd, requestedFormats, [format]);
|
|
259
|
+
}
|
|
260
|
+
const formats = [];
|
|
261
|
+
for (let index = 0; index < SUPPORTED_FORMATS.length; index++) {
|
|
262
|
+
const format = SUPPORTED_FORMATS[index];
|
|
263
|
+
if (invoke(setHas, requestedFormats, [format])) {
|
|
264
|
+
invoke(arrayPush, formats, [format]);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const quality = readDataProperty(descriptors, "quality", "Image optimization request");
|
|
268
|
+
if (!numberIsInteger(quality) || quality < 1 || quality > 100) {
|
|
269
|
+
throw new TypeError("Image optimization quality must be an integer from 1 through 100");
|
|
270
|
+
}
|
|
271
|
+
return freeze({
|
|
272
|
+
input,
|
|
273
|
+
targetWidths: freeze(targetWidths),
|
|
274
|
+
formats: freeze(formats),
|
|
275
|
+
quality: quality,
|
|
276
|
+
signal,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
function findMethod(prototype, property) {
|
|
280
|
+
const visited = new SetConstructor();
|
|
281
|
+
let owner = prototype;
|
|
282
|
+
let depth = 0;
|
|
283
|
+
while (owner !== null) {
|
|
284
|
+
if (depth >= MAX_SHARP_PROTOTYPE_DEPTH ||
|
|
285
|
+
invoke(setHas, visited, [owner])) {
|
|
286
|
+
throw new TypeError("Sharp has an invalid prototype chain");
|
|
287
|
+
}
|
|
288
|
+
invoke(setAdd, visited, [owner]);
|
|
289
|
+
const descriptor = getOwnPropertyDescriptor(owner, property);
|
|
290
|
+
if (descriptor !== undefined) {
|
|
291
|
+
if (!hasOwn(descriptor, "value") || typeof descriptor.value !== "function") {
|
|
292
|
+
throw new TypeError(`Sharp ${property} must be a data-property method`);
|
|
293
|
+
}
|
|
294
|
+
return descriptor.value;
|
|
295
|
+
}
|
|
296
|
+
owner = getPrototypeOf(owner);
|
|
297
|
+
depth++;
|
|
298
|
+
}
|
|
299
|
+
throw new TypeError(`Sharp does not implement ${property}()`);
|
|
300
|
+
}
|
|
301
|
+
function snapshotVersions(value) {
|
|
302
|
+
if (typeof value !== "object" || value === null || arrayIsArray(value)) {
|
|
303
|
+
throw new TypeError("Sharp versions must be an object");
|
|
304
|
+
}
|
|
305
|
+
const descriptors = ownDescriptors(value, "Sharp versions");
|
|
306
|
+
const versions = createObject(null);
|
|
307
|
+
for (const property of ownKeys(descriptors)) {
|
|
308
|
+
if (typeof property !== "string") {
|
|
309
|
+
throw new TypeError("Sharp versions must not contain symbol properties");
|
|
310
|
+
}
|
|
311
|
+
const version = readDataProperty(descriptors, property, "Sharp versions");
|
|
312
|
+
if (typeof version !== "string" || version.length === 0 ||
|
|
313
|
+
version.length > 64) {
|
|
314
|
+
throw new TypeError(`Sharp reported an invalid ${property} version`);
|
|
315
|
+
}
|
|
316
|
+
defineProperty(versions, property, {
|
|
317
|
+
value: version,
|
|
318
|
+
enumerable: true,
|
|
319
|
+
configurable: false,
|
|
320
|
+
writable: false,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return freeze(versions);
|
|
324
|
+
}
|
|
325
|
+
function requirePipeline(value, operation) {
|
|
326
|
+
if ((typeof value !== "object" && typeof value !== "function") || value === null) {
|
|
327
|
+
throw new TypeError(`Sharp ${operation} did not return an image pipeline`);
|
|
328
|
+
}
|
|
329
|
+
return value;
|
|
330
|
+
}
|
|
331
|
+
/** Capture Sharp's callable, versions, and prototype methods exactly once. */
|
|
332
|
+
export function captureSharpRuntime(value) {
|
|
333
|
+
if (typeof value !== "function") {
|
|
334
|
+
throw new TypeError("Sharp must export an image constructor function");
|
|
335
|
+
}
|
|
336
|
+
let prototypeDescriptor;
|
|
337
|
+
let versionsDescriptor;
|
|
338
|
+
try {
|
|
339
|
+
prototypeDescriptor = getOwnPropertyDescriptor(value, "prototype");
|
|
340
|
+
versionsDescriptor = getOwnPropertyDescriptor(value, "versions");
|
|
341
|
+
}
|
|
342
|
+
catch (cause) {
|
|
343
|
+
throw new TypeError("Sharp export could not be inspected", { cause });
|
|
344
|
+
}
|
|
345
|
+
if (prototypeDescriptor === undefined ||
|
|
346
|
+
!hasOwn(prototypeDescriptor, "value") ||
|
|
347
|
+
typeof prototypeDescriptor.value !== "object" ||
|
|
348
|
+
prototypeDescriptor.value === null) {
|
|
349
|
+
throw new TypeError("Sharp prototype must be an own data property");
|
|
350
|
+
}
|
|
351
|
+
if (versionsDescriptor === undefined || !hasOwn(versionsDescriptor, "value")) {
|
|
352
|
+
throw new TypeError("Sharp versions must be an own data property");
|
|
353
|
+
}
|
|
354
|
+
const callable = value;
|
|
355
|
+
const prototype = prototypeDescriptor.value;
|
|
356
|
+
const methods = freeze({
|
|
357
|
+
autoOrient: findMethod(prototype, "autoOrient"),
|
|
358
|
+
metadata: findMethod(prototype, "metadata"),
|
|
359
|
+
clone: findMethod(prototype, "clone"),
|
|
360
|
+
resize: findMethod(prototype, "resize"),
|
|
361
|
+
webp: findMethod(prototype, "webp"),
|
|
362
|
+
avif: findMethod(prototype, "avif"),
|
|
363
|
+
jpeg: findMethod(prototype, "jpeg"),
|
|
364
|
+
png: findMethod(prototype, "png"),
|
|
365
|
+
toBuffer: findMethod(prototype, "toBuffer"),
|
|
366
|
+
});
|
|
367
|
+
const versions = snapshotVersions(versionsDescriptor.value);
|
|
368
|
+
return freeze({
|
|
369
|
+
versions,
|
|
370
|
+
create(input, options) {
|
|
371
|
+
return requirePipeline(invoke(callable, undefined, [input, options]), "constructor");
|
|
372
|
+
},
|
|
373
|
+
autoOrient(pipeline) {
|
|
374
|
+
return requirePipeline(invoke(methods.autoOrient, pipeline, []), "autoOrient");
|
|
375
|
+
},
|
|
376
|
+
metadata(pipeline) {
|
|
377
|
+
return invoke(methods.metadata, pipeline, []);
|
|
378
|
+
},
|
|
379
|
+
clone(pipeline) {
|
|
380
|
+
return requirePipeline(invoke(methods.clone, pipeline, []), "clone");
|
|
381
|
+
},
|
|
382
|
+
resize(pipeline, options) {
|
|
383
|
+
return requirePipeline(invoke(methods.resize, pipeline, [options]), "resize");
|
|
384
|
+
},
|
|
385
|
+
webp(pipeline, options) {
|
|
386
|
+
return requirePipeline(invoke(methods.webp, pipeline, [options]), "webp");
|
|
387
|
+
},
|
|
388
|
+
avif(pipeline, options) {
|
|
389
|
+
return requirePipeline(invoke(methods.avif, pipeline, [options]), "avif");
|
|
390
|
+
},
|
|
391
|
+
jpeg(pipeline, options) {
|
|
392
|
+
return requirePipeline(invoke(methods.jpeg, pipeline, [options]), "jpeg");
|
|
393
|
+
},
|
|
394
|
+
png(pipeline, options) {
|
|
395
|
+
return requirePipeline(invoke(methods.png, pipeline, [options]), "png");
|
|
396
|
+
},
|
|
397
|
+
toBuffer(pipeline, options) {
|
|
398
|
+
return invoke(methods.toBuffer, pipeline, [options]);
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
function assertDimension(value, label, limits) {
|
|
403
|
+
if (!numberIsInteger(value) ||
|
|
404
|
+
value <= 0 ||
|
|
405
|
+
value > limits.maxDimension) {
|
|
406
|
+
throw new TypeError(`${label} must be an integer from 1 through ${limits.maxDimension}`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function readDimensions(value, label, limits) {
|
|
410
|
+
if (typeof value !== "object" || value === null || arrayIsArray(value)) {
|
|
411
|
+
throw new TypeError(`${label} must be an object`);
|
|
412
|
+
}
|
|
413
|
+
const descriptors = ownDescriptors(value, label);
|
|
414
|
+
const width = readDataProperty(descriptors, "width", label);
|
|
415
|
+
const height = readDataProperty(descriptors, "height", label);
|
|
416
|
+
assertDimension(width, `${label} width`, limits);
|
|
417
|
+
assertDimension(height, `${label} height`, limits);
|
|
418
|
+
if (width * height > limits.maxDecodedPixels) {
|
|
419
|
+
throw new TypeError(`${label} exceeds ${limits.maxDecodedPixels} pixels`);
|
|
420
|
+
}
|
|
421
|
+
return freeze({ width, height });
|
|
422
|
+
}
|
|
423
|
+
function sourceDimensions(value, limits) {
|
|
424
|
+
if (typeof value !== "object" || value === null || arrayIsArray(value)) {
|
|
425
|
+
throw new TypeError("Sharp metadata must be an object");
|
|
426
|
+
}
|
|
427
|
+
const descriptors = ownDescriptors(value, "Sharp metadata");
|
|
428
|
+
const oriented = readOptionalDataProperty(descriptors, "autoOrient", "Sharp metadata");
|
|
429
|
+
if (oriented !== undefined) {
|
|
430
|
+
return readDimensions(oriented, "Auto-oriented image", limits);
|
|
431
|
+
}
|
|
432
|
+
return readDimensions(value, "Source image", limits);
|
|
433
|
+
}
|
|
434
|
+
function outputWidths(configured, sourceWidth, limits) {
|
|
435
|
+
const widths = [];
|
|
436
|
+
for (let index = 0; index < configured.length; index++) {
|
|
437
|
+
const width = configured[index];
|
|
438
|
+
if (width <= sourceWidth)
|
|
439
|
+
invoke(arrayPush, widths, [width]);
|
|
440
|
+
}
|
|
441
|
+
if (!invoke(arrayIncludes, widths, [sourceWidth])) {
|
|
442
|
+
invoke(arrayPush, widths, [sourceWidth]);
|
|
443
|
+
}
|
|
444
|
+
invoke(arraySort, widths, [sortNumbers]);
|
|
445
|
+
if (widths.length > limits.maxTargetWidths + 1) {
|
|
446
|
+
throw new TypeError(`Image optimization produces more than ${limits.maxTargetWidths + 1} output widths`);
|
|
447
|
+
}
|
|
448
|
+
return freeze(widths);
|
|
449
|
+
}
|
|
450
|
+
function encode(runtime, pipeline, format, quality) {
|
|
451
|
+
if (format === "webp")
|
|
452
|
+
return runtime.webp(pipeline, freeze({ quality }));
|
|
453
|
+
if (format === "avif")
|
|
454
|
+
return runtime.avif(pipeline, freeze({ quality }));
|
|
455
|
+
if (format === "jpeg") {
|
|
456
|
+
return runtime.jpeg(pipeline, freeze({ quality, progressive: true }));
|
|
457
|
+
}
|
|
458
|
+
return runtime.png(pipeline, freeze({ quality, compressionLevel: 9, adaptiveFiltering: true }));
|
|
459
|
+
}
|
|
460
|
+
function readOutput(value, format, width, sourceWidth, sourceHeight, aggregate, limits) {
|
|
461
|
+
if (typeof value !== "object" || value === null || arrayIsArray(value)) {
|
|
462
|
+
throw new TypeError("Sharp output must be an object");
|
|
463
|
+
}
|
|
464
|
+
const descriptors = ownDescriptors(value, "Sharp output");
|
|
465
|
+
const dataValue = readDataProperty(descriptors, "data", "Sharp output");
|
|
466
|
+
const infoValue = readDataProperty(descriptors, "info", "Sharp output");
|
|
467
|
+
const dataLength = byteLength(dataValue, "Sharp output data");
|
|
468
|
+
if (dataLength === 0)
|
|
469
|
+
throw new TypeError("Sharp returned an empty image");
|
|
470
|
+
if (dataLength > limits.maxOutputBytesPerVariant) {
|
|
471
|
+
throw new TypeError(`Sharp output exceeds ${limits.maxOutputBytesPerVariant} bytes`);
|
|
472
|
+
}
|
|
473
|
+
if (typeof infoValue !== "object" || infoValue === null || arrayIsArray(infoValue)) {
|
|
474
|
+
throw new TypeError("Sharp output info must be an object");
|
|
475
|
+
}
|
|
476
|
+
const infoDescriptors = ownDescriptors(infoValue, "Sharp output info");
|
|
477
|
+
const outputWidth = readDataProperty(infoDescriptors, "width", "Sharp output info");
|
|
478
|
+
const outputHeight = readDataProperty(infoDescriptors, "height", "Sharp output info");
|
|
479
|
+
const outputSize = readDataProperty(infoDescriptors, "size", "Sharp output info");
|
|
480
|
+
const outputFormat = readDataProperty(infoDescriptors, "format", "Sharp output info");
|
|
481
|
+
assertDimension(outputWidth, "Sharp output width", limits);
|
|
482
|
+
assertDimension(outputHeight, "Sharp output height", limits);
|
|
483
|
+
if (outputWidth !== width) {
|
|
484
|
+
throw new TypeError(`Sharp encoded ${outputWidth}px for requested ${width}px output`);
|
|
485
|
+
}
|
|
486
|
+
const expectedHeight = Math.max(1, Math.round(sourceHeight * width / sourceWidth));
|
|
487
|
+
if (outputHeight !== expectedHeight) {
|
|
488
|
+
throw new TypeError("Sharp did not preserve the source aspect ratio");
|
|
489
|
+
}
|
|
490
|
+
if (!numberIsSafeInteger(outputSize) || outputSize !== dataLength) {
|
|
491
|
+
throw new TypeError("Sharp output size metadata does not match its bytes");
|
|
492
|
+
}
|
|
493
|
+
if (outputFormat !== format &&
|
|
494
|
+
!(format === "avif" && outputFormat === "heif")) {
|
|
495
|
+
throw new TypeError(`Sharp returned ${String(outputFormat)} for ${format} output`);
|
|
496
|
+
}
|
|
497
|
+
aggregate.value += dataLength;
|
|
498
|
+
if (!numberIsSafeInteger(aggregate.value) ||
|
|
499
|
+
aggregate.value > limits.maxTotalOutputBytes) {
|
|
500
|
+
throw new TypeError(`Sharp outputs exceed ${limits.maxTotalOutputBytes} bytes in total`);
|
|
501
|
+
}
|
|
502
|
+
return freeze({
|
|
503
|
+
format,
|
|
504
|
+
width,
|
|
505
|
+
height: outputHeight,
|
|
506
|
+
data: copyBytes(dataValue, limits.maxOutputBytesPerVariant, "Sharp output data"),
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
const SHARP_INPUT_OPTIONS = freeze({
|
|
510
|
+
failOn: "warning",
|
|
511
|
+
limitInputPixels: SHARP_IMAGE_LIMITS.maxDecodedPixels,
|
|
512
|
+
pages: 1,
|
|
513
|
+
sequentialRead: true,
|
|
514
|
+
});
|
|
515
|
+
const SHARP_OUTPUT_OPTIONS = freeze({ resolveWithObject: true });
|
|
516
|
+
/**
|
|
517
|
+
* Internal engine used by the public extension and focused fake-runtime tests.
|
|
518
|
+
* Every call snapshots one request and creates an independent Sharp pipeline.
|
|
519
|
+
*/
|
|
520
|
+
export class BoundSharpImageOptimizationEngine {
|
|
521
|
+
cacheIdentity;
|
|
522
|
+
#runtime;
|
|
523
|
+
#limits;
|
|
524
|
+
constructor(runtime, cacheIdentity, limits = SHARP_IMAGE_LIMITS) {
|
|
525
|
+
this.#runtime = runtime;
|
|
526
|
+
this.#limits = limits;
|
|
527
|
+
defineProperty(this, "cacheIdentity", {
|
|
528
|
+
value: cacheIdentity,
|
|
529
|
+
enumerable: true,
|
|
530
|
+
configurable: false,
|
|
531
|
+
writable: false,
|
|
532
|
+
});
|
|
533
|
+
freeze(this);
|
|
534
|
+
}
|
|
535
|
+
async optimize(request) {
|
|
536
|
+
const snapshot = snapshotRequest(request, this.#limits);
|
|
537
|
+
throwIfAborted(snapshot.signal);
|
|
538
|
+
let source = this.#runtime.create(snapshot.input, SHARP_INPUT_OPTIONS);
|
|
539
|
+
throwIfAborted(snapshot.signal);
|
|
540
|
+
source = this.#runtime.autoOrient(source);
|
|
541
|
+
throwIfAborted(snapshot.signal);
|
|
542
|
+
const metadata = await awaitNativeOperation(this.#runtime.metadata(source), snapshot.signal);
|
|
543
|
+
const sourceSize = sourceDimensions(metadata, this.#limits);
|
|
544
|
+
const widths = outputWidths(snapshot.targetWidths, sourceSize.width, this.#limits);
|
|
545
|
+
if (widths.length * snapshot.formats.length > this.#limits.maxVariants) {
|
|
546
|
+
throw new TypeError(`Image optimization produces more than ${this.#limits.maxVariants} variants`);
|
|
547
|
+
}
|
|
548
|
+
const variants = [];
|
|
549
|
+
const aggregate = { value: 0 };
|
|
550
|
+
for (let widthIndex = 0; widthIndex < widths.length; widthIndex++) {
|
|
551
|
+
const width = widths[widthIndex];
|
|
552
|
+
for (let formatIndex = 0; formatIndex < snapshot.formats.length; formatIndex++) {
|
|
553
|
+
const format = snapshot.formats[formatIndex];
|
|
554
|
+
throwIfAborted(snapshot.signal);
|
|
555
|
+
let pipeline = this.#runtime.clone(source);
|
|
556
|
+
pipeline = this.#runtime.resize(pipeline, freeze({ width, fit: "inside", withoutEnlargement: true }));
|
|
557
|
+
pipeline = encode(this.#runtime, pipeline, format, snapshot.quality);
|
|
558
|
+
const output = await awaitNativeOperation(this.#runtime.toBuffer(pipeline, SHARP_OUTPUT_OPTIONS), snapshot.signal);
|
|
559
|
+
throwIfAborted(snapshot.signal);
|
|
560
|
+
invoke(arrayPush, variants, [
|
|
561
|
+
readOutput(output, format, width, sourceSize.width, sourceSize.height, aggregate, this.#limits),
|
|
562
|
+
]);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return freeze({
|
|
566
|
+
sourceWidth: sourceSize.width,
|
|
567
|
+
sourceHeight: sourceSize.height,
|
|
568
|
+
variants: freeze(variants),
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
freeze(BoundSharpImageOptimizationEngine.prototype);
|
|
573
|
+
freeze(BoundSharpImageOptimizationEngine);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veryfront/ext-image-sharp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1189",
|
|
4
4
|
"description": "Veryfront first-party extension package for ext-image-sharp",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"veryfront",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"scripts": {},
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
30
|
+
"node": ">=22.3.0"
|
|
31
31
|
},
|
|
32
32
|
"publishConfig": {
|
|
33
33
|
"access": "public"
|
|
@@ -35,9 +35,6 @@
|
|
|
35
35
|
"veryfront": {
|
|
36
36
|
"extension": true,
|
|
37
37
|
"activation": "explicit",
|
|
38
|
-
"npm": {
|
|
39
|
-
"nodeEngine": ">=20.9.0"
|
|
40
|
-
},
|
|
41
38
|
"contracts": {
|
|
42
39
|
"provides": [
|
|
43
40
|
"ImageOptimizationEngine"
|
|
@@ -67,7 +64,7 @@
|
|
|
67
64
|
"sharp": "0.35.3"
|
|
68
65
|
},
|
|
69
66
|
"peerDependencies": {
|
|
70
|
-
"veryfront": "^0.1.
|
|
67
|
+
"veryfront": "^0.1.1189"
|
|
71
68
|
},
|
|
72
69
|
"type": "module",
|
|
73
70
|
"types": "./esm/src/index.d.ts",
|