@nifrajs/image 3.1.0 → 3.3.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/dist/server.d.ts +4 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +155 -63
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/server.ts +154 -63
package/dist/server.d.ts
CHANGED
|
@@ -41,8 +41,11 @@ export interface ImageHandlerOptions {
|
|
|
41
41
|
readonly sourceConcurrency?: number;
|
|
42
42
|
/** Maximum queued image requests beyond the admission width. Default `concurrency * 16`. */
|
|
43
43
|
readonly maxQueue?: number;
|
|
44
|
-
/** `Cache-Control: public, max-age=<n
|
|
44
|
+
/** `Cache-Control: public, max-age=<n>` seconds. Default 1 hour. */
|
|
45
45
|
readonly cacheMaxAge?: number;
|
|
46
|
+
/** Add `immutable` to Cache-Control. Off by default: enable only when every source URL changes with
|
|
47
|
+
* its content, otherwise browsers are allowed to retain a changed image until the full max-age. */
|
|
48
|
+
readonly immutable?: boolean;
|
|
46
49
|
/** Quality used when `?q` is absent. Default 75. */
|
|
47
50
|
readonly defaultQuality?: number;
|
|
48
51
|
/** Remote-fetch timeout (ms). Default 10 000. */
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,EAEL,KAAK,YAAY,EAGlB,MAAM,cAAc,CAAA;AAOrB,cAAc,cAAc,CAAA;AAE5B,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,YAAY,CAAA;IAC/B;sEACkE;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB;+FAC2F;IAC3F,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,gGAAgG;IAChG,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;IACjC,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,oGAAoG;IACpG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B;uGACmG;IACnG,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC,iDAAiD;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAA;IAChC;sCACkC;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IAC7B;;0FAEsF;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAC/C;AAmCD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,mBAAwB,GAChC,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAGrC"}
|
package/dist/server.js
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* edge. On Workers / Vercel-Edge / Deno-Deploy there is no native codec - use the CDN `cloudflareLoader`
|
|
11
11
|
* from `@nifrajs/image` instead. The dependency-free core (`@nifrajs/image`) never imports this module.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import { constants as FS } from "node:fs";
|
|
14
|
+
import { open, realpath } from "node:fs/promises";
|
|
14
15
|
import { resolve as resolvePath, sep } from "node:path";
|
|
15
16
|
import { bunImageBackend, ImageProcessingError, } from "./backend.js";
|
|
16
17
|
import { verifyImageParams } from "./sign.js";
|
|
@@ -47,11 +48,24 @@ function resolveConfig(options) {
|
|
|
47
48
|
const concurrency = options.concurrency ?? 4;
|
|
48
49
|
const sourceConcurrency = options.sourceConcurrency ?? concurrency * 2;
|
|
49
50
|
const maxQueue = options.maxQueue ?? concurrency * 16;
|
|
51
|
+
const maxWidth = options.maxWidth ?? 3840;
|
|
52
|
+
const cacheMaxAge = options.cacheMaxAge ?? 3_600;
|
|
53
|
+
const defaultQuality = options.defaultQuality ?? 75;
|
|
54
|
+
const fetchTimeoutMs = options.fetchTimeoutMs ?? 10_000;
|
|
55
|
+
const immutable = options.immutable ?? false;
|
|
50
56
|
assertNonNegativeSafeInteger(maxSourceBytes, "maxSourceBytes");
|
|
51
57
|
assertPositiveSafeInteger(maxSourcePixels, "maxSourcePixels");
|
|
52
58
|
assertPositiveSafeInteger(concurrency, "concurrency");
|
|
53
59
|
assertPositiveSafeInteger(sourceConcurrency, "sourceConcurrency");
|
|
54
60
|
assertNonNegativeSafeInteger(maxQueue, "maxQueue");
|
|
61
|
+
assertPositiveSafeInteger(maxWidth, "maxWidth");
|
|
62
|
+
assertNonNegativeSafeInteger(cacheMaxAge, "cacheMaxAge");
|
|
63
|
+
assertPositiveSafeInteger(fetchTimeoutMs, "fetchTimeoutMs");
|
|
64
|
+
if (!Number.isSafeInteger(defaultQuality) || defaultQuality < 1 || defaultQuality > 100) {
|
|
65
|
+
throw new RangeError("image: defaultQuality must be a safe integer from 1 to 100");
|
|
66
|
+
}
|
|
67
|
+
if (typeof immutable !== "boolean")
|
|
68
|
+
throw new TypeError("image: immutable must be a boolean");
|
|
55
69
|
if (sourceConcurrency < concurrency) {
|
|
56
70
|
// Otherwise the codec lane can never fill, and `concurrency` silently means something narrower.
|
|
57
71
|
throw new RangeError("image: sourceConcurrency must be >= concurrency");
|
|
@@ -62,10 +76,11 @@ function resolveConfig(options) {
|
|
|
62
76
|
allowedOrigins: new Set(options.allowedOrigins ?? []),
|
|
63
77
|
maxSourceBytes,
|
|
64
78
|
maxSourcePixels,
|
|
65
|
-
maxWidth
|
|
66
|
-
cacheMaxAge
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
maxWidth,
|
|
80
|
+
cacheMaxAge,
|
|
81
|
+
immutable,
|
|
82
|
+
defaultQuality,
|
|
83
|
+
fetchTimeoutMs,
|
|
69
84
|
fetchImpl: options.fetch ?? globalThis.fetch,
|
|
70
85
|
signing: options.signing ?? null,
|
|
71
86
|
admit: createSemaphore(sourceConcurrency, maxQueue),
|
|
@@ -113,30 +128,25 @@ async function handle(req, cfg) {
|
|
|
113
128
|
if (!ok)
|
|
114
129
|
return errorResponse(403, "invalid_signature");
|
|
115
130
|
}
|
|
116
|
-
// 3.
|
|
117
|
-
// conditional request short-circuits all expensive work. The clamped width is a pure function of
|
|
118
|
-
// (src, requestedWidth), so (src, requestedWidth, quality, wantsWebp) fully keys the response.
|
|
119
|
-
const etag = `"${fnv1a(`${src}|${requestedWidth}|${quality}|${wantsWebp ? 1 : 0}`)}"`;
|
|
120
|
-
const cacheHeaders = {
|
|
121
|
-
"Cache-Control": `public, max-age=${cfg.cacheMaxAge}, immutable`,
|
|
122
|
-
ETag: etag,
|
|
123
|
-
Vary: "Accept",
|
|
124
|
-
};
|
|
125
|
-
if (ifNoneMatch(req, etag)) {
|
|
126
|
-
return new Response(null, { status: 304, headers: cacheHeaders });
|
|
127
|
-
}
|
|
128
|
-
// 4/5. Admission precedes source reads so queued requests do not retain large buffers. It is a
|
|
131
|
+
// 3/4. Admission precedes source reads so queued requests do not retain large buffers. It is a
|
|
129
132
|
// separate, wider lane from the codec one below: a remote source is a network wait, and letting
|
|
130
133
|
// it hold a codec slot would let `concurrency` slow origins stall every transform on the box.
|
|
131
|
-
|
|
134
|
+
const admission = await cfg.admit.acquire(req.signal);
|
|
135
|
+
if (admission === "full")
|
|
132
136
|
return errorResponse(503, "image_queue_full");
|
|
137
|
+
if (admission === "aborted")
|
|
138
|
+
return errorResponse(499, "request_cancelled");
|
|
133
139
|
try {
|
|
134
|
-
const source = await readSource(src, cfg);
|
|
140
|
+
const source = await readSource(src, cfg, req.signal);
|
|
135
141
|
if (!source.ok)
|
|
136
142
|
return errorResponse(source.status, source.code);
|
|
137
143
|
// The bytes are in hand; now queue for CPU. This semaphore never refuses - its waiter bound is the
|
|
138
144
|
// admission width - so a wait here is a wait, not a 503.
|
|
139
|
-
await cfg.codec.acquire();
|
|
145
|
+
const codecAdmission = await cfg.codec.acquire(req.signal);
|
|
146
|
+
if (codecAdmission === "full")
|
|
147
|
+
return errorResponse(503, "image_queue_full");
|
|
148
|
+
if (codecAdmission === "aborted")
|
|
149
|
+
return errorResponse(499, "request_cancelled");
|
|
140
150
|
let out;
|
|
141
151
|
try {
|
|
142
152
|
// Probe → enforce the portable pixel cap → clamp to intrinsic (never upscale) → transform.
|
|
@@ -144,6 +154,8 @@ async function handle(req, cfg) {
|
|
|
144
154
|
if (probe.width * probe.height > cfg.maxSourcePixels) {
|
|
145
155
|
return errorResponse(413, "source_too_large");
|
|
146
156
|
}
|
|
157
|
+
if (req.signal.aborted)
|
|
158
|
+
return errorResponse(499, "request_cancelled");
|
|
147
159
|
const targetWidth = Math.max(1, Math.min(width, probe.width));
|
|
148
160
|
const format = negotiateFormat(probe.format, wantsWebp);
|
|
149
161
|
out = await cfg.backend.transform({
|
|
@@ -152,14 +164,25 @@ async function handle(req, cfg) {
|
|
|
152
164
|
quality,
|
|
153
165
|
format,
|
|
154
166
|
});
|
|
167
|
+
if (req.signal.aborted)
|
|
168
|
+
return errorResponse(499, "request_cancelled");
|
|
155
169
|
}
|
|
156
170
|
finally {
|
|
157
171
|
cfg.codec.release();
|
|
158
172
|
}
|
|
159
|
-
|
|
173
|
+
// A strong validator describes the bytes actually emitted. Hashing request parameters here would
|
|
174
|
+
// falsely return 304 when a mutable local/remote source changes at the same URL.
|
|
175
|
+
const etag = `"${await sha256(out.bytes)}"`;
|
|
176
|
+
const headers = new Headers({
|
|
177
|
+
"Cache-Control": `public, max-age=${cfg.cacheMaxAge}${cfg.immutable ? ", immutable" : ""}`,
|
|
178
|
+
ETag: etag,
|
|
179
|
+
Vary: "Accept",
|
|
180
|
+
});
|
|
160
181
|
headers.set("Content-Type", out.contentType);
|
|
161
182
|
headers.set("Content-Length", String(out.bytes.byteLength));
|
|
162
183
|
headers.set("X-Content-Type-Options", "nosniff"); // never let a client sniff the re-encoded bytes
|
|
184
|
+
if (ifNoneMatch(req, etag))
|
|
185
|
+
return new Response(null, { status: 304, headers });
|
|
163
186
|
// HEAD: identical headers, no body.
|
|
164
187
|
if (req.method === "HEAD")
|
|
165
188
|
return new Response(null, { status: 200, headers });
|
|
@@ -183,7 +206,9 @@ async function handle(req, cfg) {
|
|
|
183
206
|
* must be `http(s)` **and** on the `allowedOrigins` allowlist; anything else is a local path resolved
|
|
184
207
|
* under `root` with traversal + symlink containment checks. Both branches fail closed.
|
|
185
208
|
*/
|
|
186
|
-
async function readSource(src, cfg) {
|
|
209
|
+
async function readSource(src, cfg, signal) {
|
|
210
|
+
if (signal.aborted)
|
|
211
|
+
return { ok: false, status: 499, code: "request_cancelled" };
|
|
187
212
|
const asUrl = tryParseUrl(src);
|
|
188
213
|
if (asUrl !== null) {
|
|
189
214
|
if (asUrl.protocol !== "http:" && asUrl.protocol !== "https:") {
|
|
@@ -192,33 +217,52 @@ async function readSource(src, cfg) {
|
|
|
192
217
|
if (!cfg.allowedOrigins.has(asUrl.origin)) {
|
|
193
218
|
return { ok: false, status: 403, code: "source_not_allowed" };
|
|
194
219
|
}
|
|
195
|
-
return fetchRemote(asUrl, cfg);
|
|
220
|
+
return fetchRemote(asUrl, cfg, signal);
|
|
196
221
|
}
|
|
197
|
-
return readLocal(src, cfg);
|
|
222
|
+
return readLocal(src, cfg, signal);
|
|
198
223
|
}
|
|
199
|
-
async function fetchRemote(url, cfg) {
|
|
200
|
-
|
|
224
|
+
async function fetchRemote(url, cfg, requestSignal) {
|
|
225
|
+
const controller = new AbortController();
|
|
226
|
+
let timedOut = false;
|
|
227
|
+
const onRequestAbort = () => controller.abort(requestSignal.reason);
|
|
228
|
+
requestSignal.addEventListener("abort", onRequestAbort, { once: true });
|
|
229
|
+
// Close the add/check race: a signal that aborted immediately before listener registration still
|
|
230
|
+
// cancels the fetch rather than running an abandoned request to completion.
|
|
231
|
+
if (requestSignal.aborted)
|
|
232
|
+
onRequestAbort();
|
|
233
|
+
const timer = setTimeout(() => {
|
|
234
|
+
timedOut = true;
|
|
235
|
+
controller.abort();
|
|
236
|
+
}, cfg.fetchTimeoutMs);
|
|
201
237
|
try {
|
|
202
|
-
res = await cfg.fetchImpl(url, {
|
|
238
|
+
const res = await cfg.fetchImpl(url, {
|
|
203
239
|
redirect: "error", // a redirect could bounce to a disallowed origin - refuse it.
|
|
204
|
-
signal:
|
|
240
|
+
signal: controller.signal,
|
|
205
241
|
headers: { Accept: "image/*" },
|
|
206
242
|
});
|
|
243
|
+
if (!res.ok)
|
|
244
|
+
return { ok: false, status: 502, code: "upstream_error" };
|
|
245
|
+
const bytes = await readBoundedBytes(res, cfg.maxSourceBytes);
|
|
246
|
+
if (bytes === null)
|
|
247
|
+
return { ok: false, status: 413, code: "source_too_large" };
|
|
248
|
+
if (requestSignal.aborted)
|
|
249
|
+
return { ok: false, status: 499, code: "request_cancelled" };
|
|
250
|
+
return { ok: true, bytes };
|
|
207
251
|
}
|
|
208
252
|
catch (err) {
|
|
209
253
|
const name = err instanceof Error ? err.name : "";
|
|
210
|
-
|
|
254
|
+
if (requestSignal.aborted)
|
|
255
|
+
return { ok: false, status: 499, code: "request_cancelled" };
|
|
256
|
+
return timedOut || name === "TimeoutError"
|
|
211
257
|
? { ok: false, status: 504, code: "upstream_timeout" }
|
|
212
258
|
: { ok: false, status: 502, code: "upstream_unreachable" };
|
|
213
259
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
return { ok: false, status: 413, code: "source_too_large" };
|
|
219
|
-
return { ok: true, bytes };
|
|
260
|
+
finally {
|
|
261
|
+
clearTimeout(timer);
|
|
262
|
+
requestSignal.removeEventListener("abort", onRequestAbort);
|
|
263
|
+
}
|
|
220
264
|
}
|
|
221
|
-
async function readLocal(src, cfg) {
|
|
265
|
+
async function readLocal(src, cfg, signal) {
|
|
222
266
|
if (cfg.root === null)
|
|
223
267
|
return { ok: false, status: 403, code: "source_not_allowed" };
|
|
224
268
|
// `src` is already percent-decoded by URLSearchParams - do NOT decode again (double-decode bypass).
|
|
@@ -231,29 +275,57 @@ async function readLocal(src, cfg) {
|
|
|
231
275
|
if (resolved !== cfg.root && !resolved.startsWith(cfg.root + sep)) {
|
|
232
276
|
return { ok: false, status: 403, code: "source_not_allowed" };
|
|
233
277
|
}
|
|
234
|
-
|
|
278
|
+
// Resolve containment, then open that verified name exactly once. Reopening `resolved` after these
|
|
279
|
+
// checks would let a writable source tree swap a symlink between verification and read.
|
|
280
|
+
let real;
|
|
235
281
|
try {
|
|
236
|
-
|
|
282
|
+
real = await realpath(resolved);
|
|
283
|
+
const realRoot = await realpath(cfg.root);
|
|
284
|
+
if (real !== realRoot && !real.startsWith(realRoot + sep)) {
|
|
285
|
+
return { ok: false, status: 403, code: "source_not_allowed" };
|
|
286
|
+
}
|
|
237
287
|
}
|
|
238
288
|
catch {
|
|
239
289
|
return { ok: false, status: 404, code: "source_not_found" };
|
|
240
290
|
}
|
|
241
|
-
|
|
242
|
-
return { ok: false, status: 404, code: "source_not_found" };
|
|
243
|
-
if (info.size > cfg.maxSourceBytes)
|
|
244
|
-
return { ok: false, status: 413, code: "source_too_large" };
|
|
245
|
-
// Defense-in-depth: a symlink inside root could point outside it - re-check the real path.
|
|
291
|
+
let handle;
|
|
246
292
|
try {
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
if (
|
|
250
|
-
return { ok: false, status:
|
|
293
|
+
handle = await open(real, FS.O_RDONLY | FS.O_NOFOLLOW);
|
|
294
|
+
const info = await handle.stat();
|
|
295
|
+
if (!info.isFile())
|
|
296
|
+
return { ok: false, status: 404, code: "source_not_found" };
|
|
297
|
+
if (info.size > cfg.maxSourceBytes) {
|
|
298
|
+
return { ok: false, status: 413, code: "source_too_large" };
|
|
299
|
+
}
|
|
300
|
+
// Read no more than the descriptor size that passed the cap. If the file grows concurrently, the
|
|
301
|
+
// appended bytes are deliberately ignored; if it shrinks, return only the bytes actually read.
|
|
302
|
+
const bytes = new Uint8Array(info.size);
|
|
303
|
+
let offset = 0;
|
|
304
|
+
while (offset < bytes.byteLength) {
|
|
305
|
+
if (signal.aborted)
|
|
306
|
+
return { ok: false, status: 499, code: "request_cancelled" };
|
|
307
|
+
const { bytesRead } = await handle.read(bytes, offset, bytes.byteLength - offset, offset);
|
|
308
|
+
if (bytesRead === 0)
|
|
309
|
+
break;
|
|
310
|
+
offset += bytesRead;
|
|
251
311
|
}
|
|
312
|
+
if (signal.aborted)
|
|
313
|
+
return { ok: false, status: 499, code: "request_cancelled" };
|
|
314
|
+
return { ok: true, bytes: offset === bytes.byteLength ? bytes : bytes.slice(0, offset) };
|
|
252
315
|
}
|
|
253
316
|
catch {
|
|
254
317
|
return { ok: false, status: 404, code: "source_not_found" };
|
|
255
318
|
}
|
|
256
|
-
|
|
319
|
+
finally {
|
|
320
|
+
if (handle !== undefined) {
|
|
321
|
+
try {
|
|
322
|
+
await handle.close();
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
// The response has already been decided; descriptor-close failure cannot change it.
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
257
329
|
}
|
|
258
330
|
/** Read a response body, aborting (→ null) once `limit` bytes are exceeded. Trusts neither the
|
|
259
331
|
* `Content-Length` header (checked as a fast reject) nor an absent one - the running total is the gate. */
|
|
@@ -321,14 +393,13 @@ function ifNoneMatch(req, etag) {
|
|
|
321
393
|
return true;
|
|
322
394
|
return header.split(",").some((t) => t.trim() === etag);
|
|
323
395
|
}
|
|
324
|
-
/**
|
|
325
|
-
function
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return (h >>> 0).toString(16);
|
|
396
|
+
/** Collision-resistant content digest for a strong representation validator. */
|
|
397
|
+
async function sha256(bytes) {
|
|
398
|
+
// Copy so a backend-owned mutable buffer cannot change while Web Crypto is reading it.
|
|
399
|
+
const stable = new Uint8Array(bytes.byteLength);
|
|
400
|
+
stable.set(bytes);
|
|
401
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", stable));
|
|
402
|
+
return [...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
332
403
|
}
|
|
333
404
|
function errorResponse(status, code, extraHeaders = {}) {
|
|
334
405
|
return new Response(JSON.stringify({ error: code }), {
|
|
@@ -339,20 +410,41 @@ function errorResponse(status, code, extraHeaders = {}) {
|
|
|
339
410
|
function createSemaphore(max, maxWaiters) {
|
|
340
411
|
let active = 0;
|
|
341
412
|
const waiters = [];
|
|
342
|
-
const acquire = async () => {
|
|
413
|
+
const acquire = async (signal) => {
|
|
414
|
+
if (signal?.aborted === true)
|
|
415
|
+
return "aborted";
|
|
343
416
|
if (active < max) {
|
|
344
417
|
active++;
|
|
345
|
-
return
|
|
418
|
+
return "acquired";
|
|
346
419
|
}
|
|
347
420
|
if (waiters.length >= maxWaiters)
|
|
348
|
-
return
|
|
349
|
-
|
|
350
|
-
|
|
421
|
+
return "full";
|
|
422
|
+
return new Promise((resolve) => {
|
|
423
|
+
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
424
|
+
const waiter = {
|
|
425
|
+
grant: () => {
|
|
426
|
+
cleanup();
|
|
427
|
+
resolve("acquired"); // slot handed over by release(); active remains unchanged
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
const onAbort = () => {
|
|
431
|
+
const index = waiters.indexOf(waiter);
|
|
432
|
+
if (index === -1)
|
|
433
|
+
return; // already granted synchronously by release()
|
|
434
|
+
waiters.splice(index, 1);
|
|
435
|
+
cleanup();
|
|
436
|
+
resolve("aborted");
|
|
437
|
+
};
|
|
438
|
+
waiters.push(waiter);
|
|
439
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
440
|
+
if (signal?.aborted === true)
|
|
441
|
+
onAbort(); // close the add/check race
|
|
442
|
+
});
|
|
351
443
|
};
|
|
352
444
|
const release = () => {
|
|
353
445
|
const next = waiters.shift();
|
|
354
446
|
if (next !== undefined) {
|
|
355
|
-
next(); // hand the slot to the waiter without touching `active`
|
|
447
|
+
next.grant(); // hand the slot to the waiter without touching `active`
|
|
356
448
|
}
|
|
357
449
|
else {
|
|
358
450
|
active--;
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/image",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "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.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
package/src/server.ts
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* from `@nifrajs/image` instead. The dependency-free core (`@nifrajs/image`) never imports this module.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { constants as FS } from "node:fs"
|
|
15
|
+
import { open, realpath } from "node:fs/promises"
|
|
15
16
|
import { resolve as resolvePath, sep } from "node:path"
|
|
16
17
|
|
|
17
18
|
import {
|
|
@@ -57,8 +58,11 @@ export interface ImageHandlerOptions {
|
|
|
57
58
|
readonly sourceConcurrency?: number
|
|
58
59
|
/** Maximum queued image requests beyond the admission width. Default `concurrency * 16`. */
|
|
59
60
|
readonly maxQueue?: number
|
|
60
|
-
/** `Cache-Control: public, max-age=<n
|
|
61
|
+
/** `Cache-Control: public, max-age=<n>` seconds. Default 1 hour. */
|
|
61
62
|
readonly cacheMaxAge?: number
|
|
63
|
+
/** Add `immutable` to Cache-Control. Off by default: enable only when every source URL changes with
|
|
64
|
+
* its content, otherwise browsers are allowed to retain a changed image until the full max-age. */
|
|
65
|
+
readonly immutable?: boolean
|
|
62
66
|
/** Quality used when `?q` is absent. Default 75. */
|
|
63
67
|
readonly defaultQuality?: number
|
|
64
68
|
/** Remote-fetch timeout (ms). Default 10 000. */
|
|
@@ -94,6 +98,7 @@ interface ResolvedConfig {
|
|
|
94
98
|
readonly maxSourcePixels: number
|
|
95
99
|
readonly maxWidth: number
|
|
96
100
|
readonly cacheMaxAge: number
|
|
101
|
+
readonly immutable: boolean
|
|
97
102
|
readonly defaultQuality: number
|
|
98
103
|
readonly fetchTimeoutMs: number
|
|
99
104
|
readonly fetchImpl: typeof fetch
|
|
@@ -125,11 +130,23 @@ function resolveConfig(options: ImageHandlerOptions): ResolvedConfig {
|
|
|
125
130
|
const concurrency = options.concurrency ?? 4
|
|
126
131
|
const sourceConcurrency = options.sourceConcurrency ?? concurrency * 2
|
|
127
132
|
const maxQueue = options.maxQueue ?? concurrency * 16
|
|
133
|
+
const maxWidth = options.maxWidth ?? 3840
|
|
134
|
+
const cacheMaxAge = options.cacheMaxAge ?? 3_600
|
|
135
|
+
const defaultQuality = options.defaultQuality ?? 75
|
|
136
|
+
const fetchTimeoutMs = options.fetchTimeoutMs ?? 10_000
|
|
137
|
+
const immutable = options.immutable ?? false
|
|
128
138
|
assertNonNegativeSafeInteger(maxSourceBytes, "maxSourceBytes")
|
|
129
139
|
assertPositiveSafeInteger(maxSourcePixels, "maxSourcePixels")
|
|
130
140
|
assertPositiveSafeInteger(concurrency, "concurrency")
|
|
131
141
|
assertPositiveSafeInteger(sourceConcurrency, "sourceConcurrency")
|
|
132
142
|
assertNonNegativeSafeInteger(maxQueue, "maxQueue")
|
|
143
|
+
assertPositiveSafeInteger(maxWidth, "maxWidth")
|
|
144
|
+
assertNonNegativeSafeInteger(cacheMaxAge, "cacheMaxAge")
|
|
145
|
+
assertPositiveSafeInteger(fetchTimeoutMs, "fetchTimeoutMs")
|
|
146
|
+
if (!Number.isSafeInteger(defaultQuality) || defaultQuality < 1 || defaultQuality > 100) {
|
|
147
|
+
throw new RangeError("image: defaultQuality must be a safe integer from 1 to 100")
|
|
148
|
+
}
|
|
149
|
+
if (typeof immutable !== "boolean") throw new TypeError("image: immutable must be a boolean")
|
|
133
150
|
if (sourceConcurrency < concurrency) {
|
|
134
151
|
// Otherwise the codec lane can never fill, and `concurrency` silently means something narrower.
|
|
135
152
|
throw new RangeError("image: sourceConcurrency must be >= concurrency")
|
|
@@ -140,10 +157,11 @@ function resolveConfig(options: ImageHandlerOptions): ResolvedConfig {
|
|
|
140
157
|
allowedOrigins: new Set(options.allowedOrigins ?? []),
|
|
141
158
|
maxSourceBytes,
|
|
142
159
|
maxSourcePixels,
|
|
143
|
-
maxWidth
|
|
144
|
-
cacheMaxAge
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
maxWidth,
|
|
161
|
+
cacheMaxAge,
|
|
162
|
+
immutable,
|
|
163
|
+
defaultQuality,
|
|
164
|
+
fetchTimeoutMs,
|
|
147
165
|
fetchImpl: options.fetch ?? globalThis.fetch,
|
|
148
166
|
signing: options.signing ?? null,
|
|
149
167
|
admit: createSemaphore(sourceConcurrency, maxQueue),
|
|
@@ -198,29 +216,20 @@ async function handle(req: Request, cfg: ResolvedConfig): Promise<Response> {
|
|
|
198
216
|
if (!ok) return errorResponse(403, "invalid_signature")
|
|
199
217
|
}
|
|
200
218
|
|
|
201
|
-
// 3.
|
|
202
|
-
// conditional request short-circuits all expensive work. The clamped width is a pure function of
|
|
203
|
-
// (src, requestedWidth), so (src, requestedWidth, quality, wantsWebp) fully keys the response.
|
|
204
|
-
const etag = `"${fnv1a(`${src}|${requestedWidth}|${quality}|${wantsWebp ? 1 : 0}`)}"`
|
|
205
|
-
const cacheHeaders: Record<string, string> = {
|
|
206
|
-
"Cache-Control": `public, max-age=${cfg.cacheMaxAge}, immutable`,
|
|
207
|
-
ETag: etag,
|
|
208
|
-
Vary: "Accept",
|
|
209
|
-
}
|
|
210
|
-
if (ifNoneMatch(req, etag)) {
|
|
211
|
-
return new Response(null, { status: 304, headers: cacheHeaders })
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// 4/5. Admission precedes source reads so queued requests do not retain large buffers. It is a
|
|
219
|
+
// 3/4. Admission precedes source reads so queued requests do not retain large buffers. It is a
|
|
215
220
|
// separate, wider lane from the codec one below: a remote source is a network wait, and letting
|
|
216
221
|
// it hold a codec slot would let `concurrency` slow origins stall every transform on the box.
|
|
217
|
-
|
|
222
|
+
const admission = await cfg.admit.acquire(req.signal)
|
|
223
|
+
if (admission === "full") return errorResponse(503, "image_queue_full")
|
|
224
|
+
if (admission === "aborted") return errorResponse(499, "request_cancelled")
|
|
218
225
|
try {
|
|
219
|
-
const source = await readSource(src, cfg)
|
|
226
|
+
const source = await readSource(src, cfg, req.signal)
|
|
220
227
|
if (!source.ok) return errorResponse(source.status, source.code)
|
|
221
228
|
// The bytes are in hand; now queue for CPU. This semaphore never refuses - its waiter bound is the
|
|
222
229
|
// admission width - so a wait here is a wait, not a 503.
|
|
223
|
-
await cfg.codec.acquire()
|
|
230
|
+
const codecAdmission = await cfg.codec.acquire(req.signal)
|
|
231
|
+
if (codecAdmission === "full") return errorResponse(503, "image_queue_full")
|
|
232
|
+
if (codecAdmission === "aborted") return errorResponse(499, "request_cancelled")
|
|
224
233
|
let out: Awaited<ReturnType<ImageBackend["transform"]>>
|
|
225
234
|
try {
|
|
226
235
|
// Probe → enforce the portable pixel cap → clamp to intrinsic (never upscale) → transform.
|
|
@@ -228,6 +237,7 @@ async function handle(req: Request, cfg: ResolvedConfig): Promise<Response> {
|
|
|
228
237
|
if (probe.width * probe.height > cfg.maxSourcePixels) {
|
|
229
238
|
return errorResponse(413, "source_too_large")
|
|
230
239
|
}
|
|
240
|
+
if (req.signal.aborted) return errorResponse(499, "request_cancelled")
|
|
231
241
|
const targetWidth = Math.max(1, Math.min(width, probe.width))
|
|
232
242
|
const format = negotiateFormat(probe.format, wantsWebp)
|
|
233
243
|
out = await cfg.backend.transform({
|
|
@@ -236,14 +246,23 @@ async function handle(req: Request, cfg: ResolvedConfig): Promise<Response> {
|
|
|
236
246
|
quality,
|
|
237
247
|
format,
|
|
238
248
|
})
|
|
249
|
+
if (req.signal.aborted) return errorResponse(499, "request_cancelled")
|
|
239
250
|
} finally {
|
|
240
251
|
cfg.codec.release()
|
|
241
252
|
}
|
|
242
253
|
|
|
243
|
-
|
|
254
|
+
// A strong validator describes the bytes actually emitted. Hashing request parameters here would
|
|
255
|
+
// falsely return 304 when a mutable local/remote source changes at the same URL.
|
|
256
|
+
const etag = `"${await sha256(out.bytes)}"`
|
|
257
|
+
const headers = new Headers({
|
|
258
|
+
"Cache-Control": `public, max-age=${cfg.cacheMaxAge}${cfg.immutable ? ", immutable" : ""}`,
|
|
259
|
+
ETag: etag,
|
|
260
|
+
Vary: "Accept",
|
|
261
|
+
})
|
|
244
262
|
headers.set("Content-Type", out.contentType)
|
|
245
263
|
headers.set("Content-Length", String(out.bytes.byteLength))
|
|
246
264
|
headers.set("X-Content-Type-Options", "nosniff") // never let a client sniff the re-encoded bytes
|
|
265
|
+
if (ifNoneMatch(req, etag)) return new Response(null, { status: 304, headers })
|
|
247
266
|
// HEAD: identical headers, no body.
|
|
248
267
|
if (req.method === "HEAD") return new Response(null, { status: 200, headers })
|
|
249
268
|
return new Response(out.bytes, { status: 200, headers })
|
|
@@ -268,7 +287,12 @@ type SourceResult =
|
|
|
268
287
|
* must be `http(s)` **and** on the `allowedOrigins` allowlist; anything else is a local path resolved
|
|
269
288
|
* under `root` with traversal + symlink containment checks. Both branches fail closed.
|
|
270
289
|
*/
|
|
271
|
-
async function readSource(
|
|
290
|
+
async function readSource(
|
|
291
|
+
src: string,
|
|
292
|
+
cfg: ResolvedConfig,
|
|
293
|
+
signal: AbortSignal,
|
|
294
|
+
): Promise<SourceResult> {
|
|
295
|
+
if (signal.aborted) return { ok: false, status: 499, code: "request_cancelled" }
|
|
272
296
|
const asUrl = tryParseUrl(src)
|
|
273
297
|
if (asUrl !== null) {
|
|
274
298
|
if (asUrl.protocol !== "http:" && asUrl.protocol !== "https:") {
|
|
@@ -277,32 +301,55 @@ async function readSource(src: string, cfg: ResolvedConfig): Promise<SourceResul
|
|
|
277
301
|
if (!cfg.allowedOrigins.has(asUrl.origin)) {
|
|
278
302
|
return { ok: false, status: 403, code: "source_not_allowed" }
|
|
279
303
|
}
|
|
280
|
-
return fetchRemote(asUrl, cfg)
|
|
304
|
+
return fetchRemote(asUrl, cfg, signal)
|
|
281
305
|
}
|
|
282
|
-
return readLocal(src, cfg)
|
|
306
|
+
return readLocal(src, cfg, signal)
|
|
283
307
|
}
|
|
284
308
|
|
|
285
|
-
async function fetchRemote(
|
|
286
|
-
|
|
309
|
+
async function fetchRemote(
|
|
310
|
+
url: URL,
|
|
311
|
+
cfg: ResolvedConfig,
|
|
312
|
+
requestSignal: AbortSignal,
|
|
313
|
+
): Promise<SourceResult> {
|
|
314
|
+
const controller = new AbortController()
|
|
315
|
+
let timedOut = false
|
|
316
|
+
const onRequestAbort = (): void => controller.abort(requestSignal.reason)
|
|
317
|
+
requestSignal.addEventListener("abort", onRequestAbort, { once: true })
|
|
318
|
+
// Close the add/check race: a signal that aborted immediately before listener registration still
|
|
319
|
+
// cancels the fetch rather than running an abandoned request to completion.
|
|
320
|
+
if (requestSignal.aborted) onRequestAbort()
|
|
321
|
+
const timer = setTimeout(() => {
|
|
322
|
+
timedOut = true
|
|
323
|
+
controller.abort()
|
|
324
|
+
}, cfg.fetchTimeoutMs)
|
|
287
325
|
try {
|
|
288
|
-
res = await cfg.fetchImpl(url, {
|
|
326
|
+
const res = await cfg.fetchImpl(url, {
|
|
289
327
|
redirect: "error", // a redirect could bounce to a disallowed origin - refuse it.
|
|
290
|
-
signal:
|
|
328
|
+
signal: controller.signal,
|
|
291
329
|
headers: { Accept: "image/*" },
|
|
292
330
|
})
|
|
331
|
+
if (!res.ok) return { ok: false, status: 502, code: "upstream_error" }
|
|
332
|
+
const bytes = await readBoundedBytes(res, cfg.maxSourceBytes)
|
|
333
|
+
if (bytes === null) return { ok: false, status: 413, code: "source_too_large" }
|
|
334
|
+
if (requestSignal.aborted) return { ok: false, status: 499, code: "request_cancelled" }
|
|
335
|
+
return { ok: true, bytes }
|
|
293
336
|
} catch (err) {
|
|
294
337
|
const name = err instanceof Error ? err.name : ""
|
|
295
|
-
return
|
|
338
|
+
if (requestSignal.aborted) return { ok: false, status: 499, code: "request_cancelled" }
|
|
339
|
+
return timedOut || name === "TimeoutError"
|
|
296
340
|
? { ok: false, status: 504, code: "upstream_timeout" }
|
|
297
341
|
: { ok: false, status: 502, code: "upstream_unreachable" }
|
|
342
|
+
} finally {
|
|
343
|
+
clearTimeout(timer)
|
|
344
|
+
requestSignal.removeEventListener("abort", onRequestAbort)
|
|
298
345
|
}
|
|
299
|
-
if (!res.ok) return { ok: false, status: 502, code: "upstream_error" }
|
|
300
|
-
const bytes = await readBoundedBytes(res, cfg.maxSourceBytes)
|
|
301
|
-
if (bytes === null) return { ok: false, status: 413, code: "source_too_large" }
|
|
302
|
-
return { ok: true, bytes }
|
|
303
346
|
}
|
|
304
347
|
|
|
305
|
-
async function readLocal(
|
|
348
|
+
async function readLocal(
|
|
349
|
+
src: string,
|
|
350
|
+
cfg: ResolvedConfig,
|
|
351
|
+
signal: AbortSignal,
|
|
352
|
+
): Promise<SourceResult> {
|
|
306
353
|
if (cfg.root === null) return { ok: false, status: 403, code: "source_not_allowed" }
|
|
307
354
|
// `src` is already percent-decoded by URLSearchParams - do NOT decode again (double-decode bypass).
|
|
308
355
|
if (src.includes("\0")) return { ok: false, status: 400, code: "invalid_src" }
|
|
@@ -313,25 +360,49 @@ async function readLocal(src: string, cfg: ResolvedConfig): Promise<SourceResult
|
|
|
313
360
|
if (resolved !== cfg.root && !resolved.startsWith(cfg.root + sep)) {
|
|
314
361
|
return { ok: false, status: 403, code: "source_not_allowed" }
|
|
315
362
|
}
|
|
316
|
-
|
|
363
|
+
// Resolve containment, then open that verified name exactly once. Reopening `resolved` after these
|
|
364
|
+
// checks would let a writable source tree swap a symlink between verification and read.
|
|
365
|
+
let real: string
|
|
317
366
|
try {
|
|
318
|
-
|
|
367
|
+
real = await realpath(resolved)
|
|
368
|
+
const realRoot = await realpath(cfg.root)
|
|
369
|
+
if (real !== realRoot && !real.startsWith(realRoot + sep)) {
|
|
370
|
+
return { ok: false, status: 403, code: "source_not_allowed" }
|
|
371
|
+
}
|
|
319
372
|
} catch {
|
|
320
373
|
return { ok: false, status: 404, code: "source_not_found" }
|
|
321
374
|
}
|
|
322
|
-
|
|
323
|
-
if (info.size > cfg.maxSourceBytes) return { ok: false, status: 413, code: "source_too_large" }
|
|
324
|
-
// Defense-in-depth: a symlink inside root could point outside it - re-check the real path.
|
|
375
|
+
let handle: Awaited<ReturnType<typeof open>> | undefined
|
|
325
376
|
try {
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
if (
|
|
329
|
-
|
|
377
|
+
handle = await open(real, FS.O_RDONLY | FS.O_NOFOLLOW)
|
|
378
|
+
const info = await handle.stat()
|
|
379
|
+
if (!info.isFile()) return { ok: false, status: 404, code: "source_not_found" }
|
|
380
|
+
if (info.size > cfg.maxSourceBytes) {
|
|
381
|
+
return { ok: false, status: 413, code: "source_too_large" }
|
|
382
|
+
}
|
|
383
|
+
// Read no more than the descriptor size that passed the cap. If the file grows concurrently, the
|
|
384
|
+
// appended bytes are deliberately ignored; if it shrinks, return only the bytes actually read.
|
|
385
|
+
const bytes = new Uint8Array(info.size)
|
|
386
|
+
let offset = 0
|
|
387
|
+
while (offset < bytes.byteLength) {
|
|
388
|
+
if (signal.aborted) return { ok: false, status: 499, code: "request_cancelled" }
|
|
389
|
+
const { bytesRead } = await handle.read(bytes, offset, bytes.byteLength - offset, offset)
|
|
390
|
+
if (bytesRead === 0) break
|
|
391
|
+
offset += bytesRead
|
|
330
392
|
}
|
|
393
|
+
if (signal.aborted) return { ok: false, status: 499, code: "request_cancelled" }
|
|
394
|
+
return { ok: true, bytes: offset === bytes.byteLength ? bytes : bytes.slice(0, offset) }
|
|
331
395
|
} catch {
|
|
332
396
|
return { ok: false, status: 404, code: "source_not_found" }
|
|
397
|
+
} finally {
|
|
398
|
+
if (handle !== undefined) {
|
|
399
|
+
try {
|
|
400
|
+
await handle.close()
|
|
401
|
+
} catch {
|
|
402
|
+
// The response has already been decided; descriptor-close failure cannot change it.
|
|
403
|
+
}
|
|
404
|
+
}
|
|
333
405
|
}
|
|
334
|
-
return { ok: true, bytes: new Uint8Array(await readFile(resolved)) }
|
|
335
406
|
}
|
|
336
407
|
|
|
337
408
|
/** Read a response body, aborting (→ null) once `limit` bytes are exceeded. Trusts neither the
|
|
@@ -397,14 +468,13 @@ function ifNoneMatch(req: Request, etag: string): boolean {
|
|
|
397
468
|
return header.split(",").some((t) => t.trim() === etag)
|
|
398
469
|
}
|
|
399
470
|
|
|
400
|
-
/**
|
|
401
|
-
function
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
return (h >>> 0).toString(16)
|
|
471
|
+
/** Collision-resistant content digest for a strong representation validator. */
|
|
472
|
+
async function sha256(bytes: Uint8Array): Promise<string> {
|
|
473
|
+
// Copy so a backend-owned mutable buffer cannot change while Web Crypto is reading it.
|
|
474
|
+
const stable = new Uint8Array(bytes.byteLength)
|
|
475
|
+
stable.set(bytes)
|
|
476
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", stable))
|
|
477
|
+
return [...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("")
|
|
408
478
|
}
|
|
409
479
|
|
|
410
480
|
function errorResponse(
|
|
@@ -424,26 +494,47 @@ function errorResponse(
|
|
|
424
494
|
* past the limit in the microtask gap. Bounds concurrent codec work.
|
|
425
495
|
*/
|
|
426
496
|
interface Semaphore {
|
|
427
|
-
acquire(): Promise<
|
|
497
|
+
acquire(signal?: AbortSignal): Promise<"acquired" | "full" | "aborted">
|
|
428
498
|
release(): void
|
|
429
499
|
}
|
|
430
500
|
|
|
431
501
|
function createSemaphore(max: number, maxWaiters: number): Semaphore {
|
|
432
502
|
let active = 0
|
|
433
|
-
|
|
434
|
-
|
|
503
|
+
interface Waiter {
|
|
504
|
+
readonly grant: () => void
|
|
505
|
+
}
|
|
506
|
+
const waiters: Waiter[] = []
|
|
507
|
+
const acquire = async (signal?: AbortSignal): Promise<"acquired" | "full" | "aborted"> => {
|
|
508
|
+
if (signal?.aborted === true) return "aborted"
|
|
435
509
|
if (active < max) {
|
|
436
510
|
active++
|
|
437
|
-
return
|
|
511
|
+
return "acquired"
|
|
438
512
|
}
|
|
439
|
-
if (waiters.length >= maxWaiters) return
|
|
440
|
-
|
|
441
|
-
|
|
513
|
+
if (waiters.length >= maxWaiters) return "full"
|
|
514
|
+
return new Promise((resolve) => {
|
|
515
|
+
const cleanup = (): void => signal?.removeEventListener("abort", onAbort)
|
|
516
|
+
const waiter: Waiter = {
|
|
517
|
+
grant: () => {
|
|
518
|
+
cleanup()
|
|
519
|
+
resolve("acquired") // slot handed over by release(); active remains unchanged
|
|
520
|
+
},
|
|
521
|
+
}
|
|
522
|
+
const onAbort = (): void => {
|
|
523
|
+
const index = waiters.indexOf(waiter)
|
|
524
|
+
if (index === -1) return // already granted synchronously by release()
|
|
525
|
+
waiters.splice(index, 1)
|
|
526
|
+
cleanup()
|
|
527
|
+
resolve("aborted")
|
|
528
|
+
}
|
|
529
|
+
waiters.push(waiter)
|
|
530
|
+
signal?.addEventListener("abort", onAbort, { once: true })
|
|
531
|
+
if (signal?.aborted === true) onAbort() // close the add/check race
|
|
532
|
+
})
|
|
442
533
|
}
|
|
443
534
|
const release = (): void => {
|
|
444
535
|
const next = waiters.shift()
|
|
445
536
|
if (next !== undefined) {
|
|
446
|
-
next() // hand the slot to the waiter without touching `active`
|
|
537
|
+
next.grant() // hand the slot to the waiter without touching `active`
|
|
447
538
|
} else {
|
|
448
539
|
active--
|
|
449
540
|
}
|