@nimbus-sh/fabric 0.8.0 → 0.9.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/README.md +26 -11
- package/dist/budgets.d.ts +50 -29
- package/dist/budgets.d.ts.map +1 -1
- package/dist/budgets.js +88 -40
- package/dist/do-calls.d.ts +20 -0
- package/dist/do-calls.d.ts.map +1 -1
- package/dist/do-calls.js +24 -11
- package/dist/fanout.d.ts +40 -32
- package/dist/fanout.d.ts.map +1 -1
- package/dist/fanout.js +46 -50
- package/dist/host-wasm.d.ts +29 -0
- package/dist/host-wasm.d.ts.map +1 -0
- package/dist/host-wasm.js +31 -0
- package/dist/image-store.d.ts +1 -1
- package/dist/image-store.d.ts.map +1 -1
- package/dist/image-store.js +33 -1
- package/dist/isolate-pool.d.ts +31 -23
- package/dist/isolate-pool.d.ts.map +1 -1
- package/dist/isolate-pool.js +68 -46
- package/dist/process-fabric.d.ts +26 -11
- package/dist/process-fabric.d.ts.map +1 -1
- package/dist/process-fabric.js +44 -0
- package/dist/timers.d.ts +12 -0
- package/dist/timers.d.ts.map +1 -1
- package/dist/timers.js +44 -9
- package/dist/vendor/types.d.ts +11 -5
- package/dist/vendor/types.d.ts.map +1 -1
- package/dist/workerd-facet-host.d.ts.map +1 -1
- package/dist/workerd-facet-host.js +30 -30
- package/package.json +4 -4
- package/src/budgets.ts +96 -49
- package/src/do-calls.ts +45 -11
- package/src/fanout.ts +60 -53
- package/src/host-wasm.ts +41 -0
- package/src/image-store.ts +29 -2
- package/src/isolate-pool.ts +98 -66
- package/src/process-fabric.ts +55 -13
- package/src/timers.ts +45 -9
- package/src/vendor/types.ts +11 -5
- package/src/workerd-facet-host.ts +32 -31
package/dist/isolate-pool.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* running 67 npm tarball extractions (cold-start dominates). We pin
|
|
8
8
|
* each job to `slot = cursor % concurrency` and use stable loader
|
|
9
9
|
* IDs `nfp:${fnHash}:slot-${i}:g${generation}`, so a pool of
|
|
10
|
-
* concurrency=
|
|
10
|
+
* concurrency=N keeps at most N warm isolates rather than one per job.
|
|
11
11
|
* 2. **Nimbus defaults**: compatibilityDate = CF_COMPAT_DATE (matches
|
|
12
|
-
* the supervisor worker), compatibilityFlags =
|
|
12
|
+
* the supervisor worker), compatibilityFlags = GUEST_COMPAT_FLAGS,
|
|
13
13
|
* globalOutbound = undefined (inherit parent network so the facet can
|
|
14
14
|
* reach https://registry.npmjs.org without a proxy binding).
|
|
15
15
|
* 3. **Supervisor autoinjection**. The pool grabs the embedder's
|
|
@@ -23,16 +23,24 @@
|
|
|
23
23
|
* The vendored directory contains only the upstream serialization, error,
|
|
24
24
|
* and binding types used by this implementation.
|
|
25
25
|
*/
|
|
26
|
-
import { CF_COMPAT_DATE } from '@nimbus-sh/core/constants.js';
|
|
26
|
+
import { CF_COMPAT_DATE, GUEST_COMPAT_FLAGS } from '@nimbus-sh/core/constants.js';
|
|
27
27
|
import { supervisorEntrypoint } from './composition.js';
|
|
28
28
|
import { supervisorBindingProps, supervisorLoaderKey } from './supervisor-props.js';
|
|
29
29
|
import { disposeRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
|
|
30
30
|
import { serializeFunction, hashSource } from './vendor/serialize.js';
|
|
31
|
-
import { beginLoaderFetch,
|
|
31
|
+
import { beginLoaderFetch, withDynamicWorkerCapNamed } from './budgets.js';
|
|
32
32
|
import { assertModuleMapWithinCodeLimit } from './budgets.js';
|
|
33
33
|
import { recordFailure, setLastFacetId, getLastRpcFrame } from '@nimbus-sh/platform/oom-discriminator.js';
|
|
34
34
|
import { classifyError } from '@nimbus-sh/platform/oom-classify.js';
|
|
35
35
|
import { BindingError, ExecutionError, RetryExhaustedError, TimeoutError, } from './vendor/errors.js';
|
|
36
|
+
import { hostWasmIdentity } from './host-wasm.js';
|
|
37
|
+
/**
|
|
38
|
+
* How long one call waits, in all, for the platform to admit it after
|
|
39
|
+
* "Dynamic worker concurrency limit exceeded" (doubling from 50 ms, at most
|
|
40
|
+
* 2 s a wait). A deployed Durable Object admitted the refused batch after a
|
|
41
|
+
* 6 s pause; 15 s bounds a call that would never be admitted.
|
|
42
|
+
*/
|
|
43
|
+
const CAP_REFUSAL_WAIT_MS = 15_000;
|
|
36
44
|
/**
|
|
37
45
|
* esbuild runtime helpers re-declared at the top of every generated facet
|
|
38
46
|
* module. esbuild emits `__name(fn, "fn")` wrappers around every named
|
|
@@ -100,7 +108,7 @@ export function assembleLoaderWorkerModuleSource(options) {
|
|
|
100
108
|
* Typical use:
|
|
101
109
|
*
|
|
102
110
|
* const pool = new IsolatePool(env, ctx, {
|
|
103
|
-
* concurrency:
|
|
111
|
+
* concurrency: 2,
|
|
104
112
|
* tag: 'npm-install',
|
|
105
113
|
* });
|
|
106
114
|
* const results = await pool.map(
|
|
@@ -139,11 +147,11 @@ export class IsolatePool {
|
|
|
139
147
|
* across pool dispatches (cache-key stability).
|
|
140
148
|
*/
|
|
141
149
|
wasmModules;
|
|
142
|
-
/** Hash of
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
150
|
+
/** Hash of every constructor-time wasm module, folded into the loader
|
|
151
|
+
* cache key so changes invalidate warm slots: a compiled module by the
|
|
152
|
+
* identity its host described, bytes by name + length + first/last
|
|
153
|
+
* byte. Hashing the FULL bytes would be O(20+ MiB) per dispatch and is
|
|
154
|
+
* unnecessary — they are pinned at deploy time. */
|
|
147
155
|
wasmHash;
|
|
148
156
|
/**
|
|
149
157
|
* Short prefix of the owning DO's id, baked into the loader.get()
|
|
@@ -175,7 +183,7 @@ export class IsolatePool {
|
|
|
175
183
|
}
|
|
176
184
|
this.loader = loader;
|
|
177
185
|
this.ctx = ctx;
|
|
178
|
-
this.concurrency = Math.max(1, opts?.concurrency ??
|
|
186
|
+
this.concurrency = Math.max(1, opts?.concurrency ?? 1);
|
|
179
187
|
this.defaultTimeoutMs = opts?.timeoutMs ?? 60_000;
|
|
180
188
|
this.defaultRetries = Math.max(0, opts?.retries ?? 0);
|
|
181
189
|
this.tag = opts?.tag ?? 'facet';
|
|
@@ -193,15 +201,34 @@ export class IsolatePool {
|
|
|
193
201
|
// 'esbuild_wasm') are rejected loudly because the generated worker
|
|
194
202
|
// would otherwise have duplicate imports. Order is preserved.
|
|
195
203
|
const wasmEntries = [];
|
|
204
|
+
const fingerprints = [];
|
|
196
205
|
const seenIds = new Set();
|
|
197
206
|
if (opts?.wasmModules) {
|
|
198
|
-
for (const [name,
|
|
199
|
-
|
|
207
|
+
for (const [name, wasm] of Object.entries(opts.wasmModules)) {
|
|
208
|
+
let fingerprint;
|
|
209
|
+
if (wasm instanceof ArrayBuffer) {
|
|
210
|
+
// Name + length + first/last byte: hashing 20+ MiB of wasm per
|
|
211
|
+
// dispatch would be wasteful, and these bytes change only with
|
|
212
|
+
// the deployed bundle.
|
|
213
|
+
const u = new Uint8Array(wasm);
|
|
214
|
+
const len = u.byteLength;
|
|
215
|
+
fingerprint = `${name}:${len}:${len > 0 ? u[0] : 0}:${len > 0 ? u[len - 1] : 0}`;
|
|
216
|
+
}
|
|
217
|
+
else if (wasm instanceof WebAssembly.Module) {
|
|
218
|
+
const identity = hostWasmIdentity(wasm);
|
|
219
|
+
if (!identity) {
|
|
220
|
+
throw new BindingError(`IsolatePool: wasmModules['${name}'] is a WebAssembly.Module nobody described; ` +
|
|
221
|
+
'pass it through describeHostWasm (@nimbus-sh/fabric/host-wasm.js) so warm slots ' +
|
|
222
|
+
'can be keyed by it and the code limit can count it.');
|
|
223
|
+
}
|
|
224
|
+
fingerprint = `${name}:host:${identity.id}:${identity.bytes}`;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
200
227
|
// Reached only when a caller broke the declared option type, so the
|
|
201
|
-
// value is whatever it really was rather than the
|
|
202
|
-
const got =
|
|
203
|
-
throw new BindingError(`IsolatePool: wasmModules['${name}'] must be ArrayBuffer ` +
|
|
204
|
-
`(got ${got || typeof
|
|
228
|
+
// value is whatever it really was rather than the union here.
|
|
229
|
+
const got = wasm?.constructor?.name;
|
|
230
|
+
throw new BindingError(`IsolatePool: wasmModules['${name}'] must be an ArrayBuffer or a WebAssembly.Module ` +
|
|
231
|
+
`(got ${got || typeof wasm}).`);
|
|
205
232
|
}
|
|
206
233
|
const id = name.replace(/[^A-Za-z0-9_]/g, '_').replace(/^[^A-Za-z_]/, '_');
|
|
207
234
|
if (seenIds.has(id)) {
|
|
@@ -209,29 +236,12 @@ export class IsolatePool {
|
|
|
209
236
|
`identifier-sanitisation (id='${id}'). Pick distinct module names.`);
|
|
210
237
|
}
|
|
211
238
|
seenIds.add(id);
|
|
212
|
-
wasmEntries.push({ name, id,
|
|
239
|
+
wasmEntries.push({ name, id, wasm });
|
|
240
|
+
fingerprints.push(fingerprint);
|
|
213
241
|
}
|
|
214
242
|
}
|
|
215
243
|
this.wasmModules = wasmEntries;
|
|
216
|
-
|
|
217
|
-
// Hashing 20+ MiB of wasm per dispatch would be wasteful; this
|
|
218
|
-
// fingerprint is bytes-stable for a given deployed bundle and only
|
|
219
|
-
// changes when the wasm itself changes (deploy-time event).
|
|
220
|
-
if (wasmEntries.length === 0) {
|
|
221
|
-
this.wasmHash = '0';
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
const fp = wasmEntries
|
|
225
|
-
.map((w) => {
|
|
226
|
-
const u = new Uint8Array(w.bytes);
|
|
227
|
-
const len = u.byteLength;
|
|
228
|
-
const first = len > 0 ? u[0] : 0;
|
|
229
|
-
const last = len > 0 ? u[len - 1] : 0;
|
|
230
|
-
return `${w.name}:${len}:${first}:${last}`;
|
|
231
|
-
})
|
|
232
|
-
.join('|');
|
|
233
|
-
this.wasmHash = hashSource(fp);
|
|
234
|
-
}
|
|
244
|
+
this.wasmHash = fingerprints.length === 0 ? '0' : hashSource(fingerprints.join('|'));
|
|
235
245
|
const bindings = { ...(opts?.extraBindings ?? {}) };
|
|
236
246
|
this.supervisorKey = 's-none';
|
|
237
247
|
if (!opts?.omitSupervisor) {
|
|
@@ -311,7 +321,7 @@ export class IsolatePool {
|
|
|
311
321
|
`id='${id}') collides with another per-call key. Pick distinct names.`);
|
|
312
322
|
}
|
|
313
323
|
seen.add(id);
|
|
314
|
-
out.push({ name, id, bytes });
|
|
324
|
+
out.push({ name, id, wasm: bytes });
|
|
315
325
|
}
|
|
316
326
|
return out;
|
|
317
327
|
}
|
|
@@ -347,7 +357,7 @@ export class IsolatePool {
|
|
|
347
357
|
return '0';
|
|
348
358
|
const parts = [];
|
|
349
359
|
for (const w of entries) {
|
|
350
|
-
const u = new Uint8Array(w.
|
|
360
|
+
const u = new Uint8Array(w.wasm);
|
|
351
361
|
const len = u.byteLength;
|
|
352
362
|
// djb2 over the bytes. Faster than crypto.subtle.digest at small
|
|
353
363
|
// sizes, deterministic, and good enough for cache-key
|
|
@@ -374,7 +384,7 @@ export class IsolatePool {
|
|
|
374
384
|
#buildCode(fnSource, perCallWasmEntries) {
|
|
375
385
|
const workerOpts = {
|
|
376
386
|
compatibilityDate: CF_COMPAT_DATE,
|
|
377
|
-
compatibilityFlags: [
|
|
387
|
+
compatibilityFlags: [...GUEST_COMPAT_FLAGS],
|
|
378
388
|
// Inherit parent network so the facet can reach registry.npmjs.org.
|
|
379
389
|
globalOutbound: undefined,
|
|
380
390
|
env: this.bindings,
|
|
@@ -418,7 +428,7 @@ export class IsolatePool {
|
|
|
418
428
|
// (matters only for human-readable diffs; workerd doesn't care).
|
|
419
429
|
const modules = { 'worker.js': moduleSource };
|
|
420
430
|
for (const w of allWasmEntries) {
|
|
421
|
-
modules[w.name] = { wasm: w.
|
|
431
|
+
modules[w.name] = { wasm: w.wasm };
|
|
422
432
|
}
|
|
423
433
|
assertModuleMapWithinCodeLimit(modules);
|
|
424
434
|
return {
|
|
@@ -518,11 +528,10 @@ export class IsolatePool {
|
|
|
518
528
|
// binding stub once the whole pool is done, which does NOT
|
|
519
529
|
// invalidate any in-flight slot's entrypoint reference.
|
|
520
530
|
const stub = this.loader.get(id, async () => code);
|
|
521
|
-
recordLoaderId(this.ctx, id);
|
|
522
531
|
const entrypoint = stub.getEntrypoint();
|
|
523
532
|
// Direct property call, awaited by this frame — bracketed, never
|
|
524
533
|
// wrapped. See beginLoaderFetch for the measured DO-poisoning hazard.
|
|
525
|
-
const endFetch = beginLoaderFetch(this.ctx);
|
|
534
|
+
const endFetch = beginLoaderFetch(this.ctx, id);
|
|
526
535
|
try {
|
|
527
536
|
return await invoke(entrypoint, attempt);
|
|
528
537
|
}
|
|
@@ -539,6 +548,8 @@ export class IsolatePool {
|
|
|
539
548
|
const maxAttempts = 1 + resilience.retries;
|
|
540
549
|
let lastError;
|
|
541
550
|
let retriedCloneRefusal = false;
|
|
551
|
+
let capRefusals = 0;
|
|
552
|
+
let capWaitedMs = 0;
|
|
542
553
|
let attempt = 0;
|
|
543
554
|
while (attempt < maxAttempts) {
|
|
544
555
|
try {
|
|
@@ -624,6 +635,17 @@ export class IsolatePool {
|
|
|
624
635
|
// reverse direction. This refresh targets the stale-loader case.
|
|
625
636
|
continue;
|
|
626
637
|
}
|
|
638
|
+
if (cause === 'dynamic_worker_cap' && capWaitedMs < CAP_REFUSAL_WAIT_MS) {
|
|
639
|
+
// The platform refused to start this call: it still counts a
|
|
640
|
+
// worker this Durable Object's ledger has already given back (a
|
|
641
|
+
// fan-out's workers stay counted for a moment after their calls
|
|
642
|
+
// return). Nothing ran, so the call waits, as the platform asks,
|
|
643
|
+
// and is sent again; it does not spend an attempt.
|
|
644
|
+
const delay = Math.min(CAP_REFUSAL_WAIT_MS - capWaitedMs, 50 * 2 ** capRefusals++, 2000);
|
|
645
|
+
capWaitedMs += delay;
|
|
646
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
627
649
|
if (attempt < maxAttempts - 1) {
|
|
628
650
|
// 100 * 2^attempt, capped at 2s so retries don't compound waiting.
|
|
629
651
|
const delay = Math.min(2000, 100 * Math.pow(2, attempt));
|
|
@@ -632,9 +654,9 @@ export class IsolatePool {
|
|
|
632
654
|
attempt++;
|
|
633
655
|
}
|
|
634
656
|
}
|
|
635
|
-
// On the way out only, so retries do not stack the annotation: a
|
|
636
|
-
// carries the ledger — which
|
|
637
|
-
//
|
|
657
|
+
// On the way out only, so retries do not stack the annotation: a limit
|
|
658
|
+
// hit carries the ledger — which workers were in flight — instead of the
|
|
659
|
+
// platform's bare message.
|
|
638
660
|
const named = withDynamicWorkerCapNamed(this.ctx, lastError);
|
|
639
661
|
if (maxAttempts > 1) {
|
|
640
662
|
throw new RetryExhaustedError(maxAttempts, named);
|
package/dist/process-fabric.d.ts
CHANGED
|
@@ -109,6 +109,7 @@ export declare const ResidentCodeSpecSchema: z.ZodObject<{
|
|
|
109
109
|
}, z.core.$strip>]>>;
|
|
110
110
|
vfsWasmModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
111
111
|
vfsTextModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
112
|
+
vfsCommonJsPacks: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
112
113
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
113
114
|
globalOutbound: z.ZodOptional<z.ZodNullable<z.ZodCustom<ServiceStub, ServiceStub>>>;
|
|
114
115
|
}, z.core.$strip>;
|
|
@@ -136,6 +137,7 @@ export declare function residentBootSpecSchema<Stage extends z.ZodType>(stageSch
|
|
|
136
137
|
}, z.core.$strip>]>>;
|
|
137
138
|
vfsWasmModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
138
139
|
vfsTextModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
140
|
+
vfsCommonJsPacks: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
139
141
|
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
140
142
|
globalOutbound: z.ZodOptional<z.ZodNullable<z.ZodCustom<ServiceStub, ServiceStub>>>;
|
|
141
143
|
}, z.core.$strip>;
|
|
@@ -214,6 +216,20 @@ export interface ResidentDiskReader {
|
|
|
214
216
|
* so the worker config can tell "embedder takes the env" from the default.
|
|
215
217
|
*/
|
|
216
218
|
export declare function residentLoaderConfig(spec: ResidentCodeSpec, disk: ResidentDiskReader): Promise<Record<string, unknown>>;
|
|
219
|
+
/**
|
|
220
|
+
* One image holding many `{ cjs }` modules: a JSON index of `[name, length]`
|
|
221
|
+
* rows, a newline, and the module texts back to back. Lengths are UTF-16 code
|
|
222
|
+
* units, the unit the decoded text is sliced in, so decoding copies nothing:
|
|
223
|
+
* each module is a slice of the one string read.
|
|
224
|
+
*
|
|
225
|
+
* Encoded as its parts, in order, never joined: the image store encodes them
|
|
226
|
+
* straight into the image's bytes, and a joined copy would be a second full
|
|
227
|
+
* copy of the program's code on the coordinator.
|
|
228
|
+
*/
|
|
229
|
+
export declare function encodeCommonJsPack(modules: Record<string, string>): string[];
|
|
230
|
+
export declare function decodeCommonJsPack(pack: string): Record<string, {
|
|
231
|
+
cjs: string;
|
|
232
|
+
}>;
|
|
217
233
|
/**
|
|
218
234
|
* The identity a resident process's SUPERVISOR binding is minted for. Always
|
|
219
235
|
* the COORDINATOR's — a process hosted somewhere else still reads and writes
|
|
@@ -323,17 +339,14 @@ export interface ProcessImageDelivery {
|
|
|
323
339
|
* across one. A peer-hosted process can only ever receive an image
|
|
324
340
|
* through `moduleCeilingBytes` below, or by streaming it.
|
|
325
341
|
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* workerd
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
*
|
|
334
|
-
* destination of a DIFFERENT class had all 500 seeded files readable from the
|
|
335
|
-
* destination's CONSTRUCTOR. No compat-date gate. So calling it is a
|
|
336
|
-
* lockfile-and-types problem, not a platform one.
|
|
342
|
+
* Present in production, and since the pins moved to
|
|
343
|
+
* `@cloudflare/workers-types` 5.20260928.1 and workerd 1.20260926.1 also in
|
|
344
|
+
* the type checker and `wrangler dev` (DurableObjectFacets.clone;
|
|
345
|
+
* src/workerd/api/actor-state.h). Before that the types declared no `clone`
|
|
346
|
+
* and the pinned workerd 1.20260603.1 lacked it, while on a live Worker the
|
|
347
|
+
* binding enumerated `["abort","clone","constructor","delete","get"]` and a
|
|
348
|
+
* clone into a destination of a DIFFERENT class had all 500 seeded files
|
|
349
|
+
* readable from the destination's CONSTRUCTOR. No compat-date gate.
|
|
337
350
|
*
|
|
338
351
|
* The hazard that comes with it, measured rather than assumed: ANY `src`
|
|
339
352
|
* that does not resolve to a populated facet — a typo, a name not created
|
|
@@ -384,6 +397,8 @@ export interface OneShotCodeSpec {
|
|
|
384
397
|
mainModule: string;
|
|
385
398
|
modules: Record<string, string | {
|
|
386
399
|
wasm: ArrayBuffer;
|
|
400
|
+
} | {
|
|
401
|
+
cjs: string;
|
|
387
402
|
}>;
|
|
388
403
|
}
|
|
389
404
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-fabric.d.ts","sourceRoot":"","sources":["../src/process-fabric.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,kBAAkB,CAAC;AAEtD;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB
|
|
1
|
+
{"version":3,"file":"process-fabric.d.ts","sourceRoot":"","sources":["../src/process-fabric.ts"],"names":[],"mappings":"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"}
|
package/dist/process-fabric.js
CHANGED
|
@@ -115,6 +115,14 @@ export const ResidentCodeSpecSchema = z.object({
|
|
|
115
115
|
* image store below and the spec names it.
|
|
116
116
|
*/
|
|
117
117
|
vfsTextModules: z.record(z.string(), z.string()).optional(),
|
|
118
|
+
/**
|
|
119
|
+
* VFS paths of generated CommonJS PACKS (encodeCommonJsPack): each image
|
|
120
|
+
* carries many `{ cjs }` modules, and the map gains every one of them at
|
|
121
|
+
* load. A node process's module cells travel this way — one module per
|
|
122
|
+
* file, so the guest's registry compiles only what the program requires,
|
|
123
|
+
* and one image per launch, so the boot spec names a path and not thousands.
|
|
124
|
+
*/
|
|
125
|
+
vfsCommonJsPacks: z.array(z.string()).optional(),
|
|
118
126
|
/**
|
|
119
127
|
* The isolate's exact `env`: one entry per binding the embedder minted,
|
|
120
128
|
* carried by reference so loopback stubs survive untouched. Defined —
|
|
@@ -229,6 +237,9 @@ export async function residentLoaderConfig(spec, disk) {
|
|
|
229
237
|
for (const [moduleName, path] of Object.entries(spec.vfsTextModules ?? {})) {
|
|
230
238
|
resolved[moduleName] = await readFacetImage(disk, path);
|
|
231
239
|
}
|
|
240
|
+
for (const path of spec.vfsCommonJsPacks ?? []) {
|
|
241
|
+
Object.assign(resolved, decodeCommonJsPack(await readFacetImage(disk, path)));
|
|
242
|
+
}
|
|
232
243
|
return {
|
|
233
244
|
compatibilityDate: spec.compatibilityDate,
|
|
234
245
|
compatibilityFlags: spec.compatibilityFlags,
|
|
@@ -238,6 +249,39 @@ export async function residentLoaderConfig(spec, disk) {
|
|
|
238
249
|
...(spec.globalOutbound !== undefined ? { globalOutbound: spec.globalOutbound } : {}),
|
|
239
250
|
};
|
|
240
251
|
}
|
|
252
|
+
/**
|
|
253
|
+
* One image holding many `{ cjs }` modules: a JSON index of `[name, length]`
|
|
254
|
+
* rows, a newline, and the module texts back to back. Lengths are UTF-16 code
|
|
255
|
+
* units, the unit the decoded text is sliced in, so decoding copies nothing:
|
|
256
|
+
* each module is a slice of the one string read.
|
|
257
|
+
*
|
|
258
|
+
* Encoded as its parts, in order, never joined: the image store encodes them
|
|
259
|
+
* straight into the image's bytes, and a joined copy would be a second full
|
|
260
|
+
* copy of the program's code on the coordinator.
|
|
261
|
+
*/
|
|
262
|
+
export function encodeCommonJsPack(modules) {
|
|
263
|
+
const index = [];
|
|
264
|
+
const parts = [''];
|
|
265
|
+
for (const [name, text] of Object.entries(modules)) {
|
|
266
|
+
index.push([name, text.length]);
|
|
267
|
+
parts.push(text);
|
|
268
|
+
}
|
|
269
|
+
parts[0] = JSON.stringify(index) + '\n';
|
|
270
|
+
return parts;
|
|
271
|
+
}
|
|
272
|
+
export function decodeCommonJsPack(pack) {
|
|
273
|
+
const newline = pack.indexOf('\n');
|
|
274
|
+
const index = z.array(z.tuple([z.string(), z.number().int().nonnegative()])).parse(JSON.parse(pack.slice(0, newline)));
|
|
275
|
+
const modules = {};
|
|
276
|
+
let offset = newline + 1;
|
|
277
|
+
for (const [name, length] of index) {
|
|
278
|
+
modules[name] = { cjs: pack.slice(offset, offset + length) };
|
|
279
|
+
offset += length;
|
|
280
|
+
}
|
|
281
|
+
if (offset !== pack.length)
|
|
282
|
+
throw new Error(`Nimbus: CommonJS pack holds ${pack.length - offset} bytes its index does not name`);
|
|
283
|
+
return modules;
|
|
284
|
+
}
|
|
241
285
|
/**
|
|
242
286
|
* Read one content-addressed facet image and verify it against the digest its
|
|
243
287
|
* path claims. Content addressing is only a guarantee if the bytes are checked
|
package/dist/timers.d.ts
CHANGED
|
@@ -51,9 +51,12 @@ export declare const TIMER_REASONS_KEY = "w1_next_alarm_reasons";
|
|
|
51
51
|
* The host instance carrying the per-instance timer chain. The field lives on
|
|
52
52
|
* the embedder's DO instance so one chain serializes every timer-map
|
|
53
53
|
* read-modify-write for that instance (see {@link Timers.schedule}).
|
|
54
|
+
* `_timerEpoch` counts {@link Timers.reset}s: a schedule or dispatch writes
|
|
55
|
+
* only while the epoch it was requested in is still current.
|
|
54
56
|
*/
|
|
55
57
|
export interface TimerHost {
|
|
56
58
|
_timerChain?: Promise<unknown>;
|
|
59
|
+
_timerEpoch?: number;
|
|
57
60
|
}
|
|
58
61
|
/**
|
|
59
62
|
* What one timer handler may return: nothing, or a deadline this reason
|
|
@@ -113,6 +116,15 @@ export declare class Timers {
|
|
|
113
116
|
* subsystem's in-isolate setTimeout fallback continues to work).
|
|
114
117
|
*/
|
|
115
118
|
schedule(reason: string, whenMs: number): Promise<boolean>;
|
|
119
|
+
/**
|
|
120
|
+
* Void every timer of this instance: a schedule or dispatch already
|
|
121
|
+
* requested — still queued on the chain, or a dispatch whose handlers are
|
|
122
|
+
* running — writes no reason and arms no alarm from here on. For a
|
|
123
|
+
* deliberate end of the actor's state (a session destroy): call it before
|
|
124
|
+
* wiping storage and deleting the alarm, so nothing in flight writes the
|
|
125
|
+
* map back or re-arms after the wipe. Requests made after it proceed.
|
|
126
|
+
*/
|
|
127
|
+
reset(): void;
|
|
116
128
|
/**
|
|
117
129
|
* Multi-reason timer dispatcher. Called from the DO's `alarm()` handler
|
|
118
130
|
* with the embedder's handler map.
|
package/dist/timers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timers.d.ts","sourceRoot":"","sources":["../src/timers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD
|
|
1
|
+
{"version":3,"file":"timers.d.ts","sourceRoot":"","sources":["../src/timers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED,uEAAuE;AACvE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,0BAA0B,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAmBD;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,qEAAqE;AACrE,MAAM,MAAM,aAAa,GAAG,MAAM,CAChC,MAAM,EACN,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,KAAK,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CACzF,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,YAAY,GAAG,MAAM,CAEjE;AAED,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,IAAI,EAAE,SAAS,EACf,GAAG,EAAE,YAAY;IAGpC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkD1D;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI;IAIb;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CACN,QAAQ,EAAE,aAAa,EACvB,aAAa,CAAC,EAAE,MAAM,IAAI,EAC1B,SAAS,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC,IAAI,CAAC;CAajB"}
|
package/dist/timers.js
CHANGED
|
@@ -103,6 +103,7 @@ export class Timers {
|
|
|
103
103
|
arms.push({ reason, whenMs });
|
|
104
104
|
return Promise.resolve(true);
|
|
105
105
|
}
|
|
106
|
+
const epoch = host._timerEpoch ?? 0;
|
|
106
107
|
// Serialize every read-modify-write of the reasons map through one
|
|
107
108
|
// per-instance chain: two schedulers firing back-to-back from one activity
|
|
108
109
|
// hook would otherwise interleave their get→put cycles and silently drop
|
|
@@ -113,15 +114,21 @@ export class Timers {
|
|
|
113
114
|
if (typeof setAlarmFn !== 'function')
|
|
114
115
|
return false;
|
|
115
116
|
const existing = (await ctx.storage.get(TIMER_REASONS_KEY));
|
|
117
|
+
// Requested before a reset: it belongs to the timers the reset voided.
|
|
118
|
+
if ((host._timerEpoch ?? 0) !== epoch)
|
|
119
|
+
return false;
|
|
116
120
|
const map = { ...(existing || {}) };
|
|
117
121
|
// Earliest-deadline-first: only update if new request is sooner or
|
|
118
122
|
// this reason has no pending entry.
|
|
123
|
+
let written;
|
|
119
124
|
if (!(reason in map) || whenMs < map[reason]) {
|
|
120
125
|
map[reason] = whenMs;
|
|
121
|
-
|
|
126
|
+
written = ctx.storage.put(TIMER_REASONS_KEY, map);
|
|
122
127
|
}
|
|
123
|
-
|
|
124
|
-
|
|
128
|
+
// Issued in the turn the epoch was checked in: a reset after this
|
|
129
|
+
// point wipes and disarms after these, never before.
|
|
130
|
+
setAlarmFn.call(ctx.storage, Math.min(...Object.values(map)));
|
|
131
|
+
await written;
|
|
125
132
|
return true;
|
|
126
133
|
}
|
|
127
134
|
catch (e) {
|
|
@@ -133,6 +140,17 @@ export class Timers {
|
|
|
133
140
|
host._timerChain = chained;
|
|
134
141
|
return chained;
|
|
135
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Void every timer of this instance: a schedule or dispatch already
|
|
145
|
+
* requested — still queued on the chain, or a dispatch whose handlers are
|
|
146
|
+
* running — writes no reason and arms no alarm from here on. For a
|
|
147
|
+
* deliberate end of the actor's state (a session destroy): call it before
|
|
148
|
+
* wiping storage and deleting the alarm, so nothing in flight writes the
|
|
149
|
+
* map back or re-arms after the wipe. Requests made after it proceed.
|
|
150
|
+
*/
|
|
151
|
+
reset() {
|
|
152
|
+
this.host._timerEpoch = (this.host._timerEpoch ?? 0) + 1;
|
|
153
|
+
}
|
|
136
154
|
/**
|
|
137
155
|
* Multi-reason timer dispatcher. Called from the DO's `alarm()` handler
|
|
138
156
|
* with the embedder's handler map.
|
|
@@ -155,14 +173,22 @@ export class Timers {
|
|
|
155
173
|
*/
|
|
156
174
|
dispatch(handlers, onLegacyAlarm, alarmInfo) {
|
|
157
175
|
const { host, ctx } = this;
|
|
176
|
+
const epoch = host._timerEpoch ?? 0;
|
|
177
|
+
const current = () => (host._timerEpoch ?? 0) === epoch;
|
|
158
178
|
// Same serialization as schedule: the dispatcher's read→handlers→write
|
|
159
179
|
// cycle must not interleave with an activity-hook schedule.
|
|
160
|
-
const chained = (host._timerChain ?? Promise.resolve()).then(() => dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo), () => dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo));
|
|
180
|
+
const chained = (host._timerChain ?? Promise.resolve()).then(() => dispatchBody(ctx, current, handlers, onLegacyAlarm, alarmInfo), () => dispatchBody(ctx, current, handlers, onLegacyAlarm, alarmInfo));
|
|
161
181
|
host._timerChain = chained;
|
|
162
182
|
return chained;
|
|
163
183
|
}
|
|
164
184
|
}
|
|
165
|
-
|
|
185
|
+
/**
|
|
186
|
+
* One dispatch. `current` answers whether the epoch it was requested in still
|
|
187
|
+
* stands: once a reset voids it, no further handler runs and nothing is
|
|
188
|
+
* written or armed — the reset's caller is wiping this state, and a write
|
|
189
|
+
* after the wipe would outlive it.
|
|
190
|
+
*/
|
|
191
|
+
async function dispatchBody(ctx, current, handlers, onLegacyAlarm, alarmInfo) {
|
|
166
192
|
// Collect schedule requests made inside handler context (see
|
|
167
193
|
// dispatchArms) and fold them into the map below, so an in-dispatch arm
|
|
168
194
|
// neither deadlocks on the chain nor races the write.
|
|
@@ -170,6 +196,8 @@ async function dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo) {
|
|
|
170
196
|
try {
|
|
171
197
|
const now = Date.now();
|
|
172
198
|
const existing = (await ctx?.storage?.get?.(TIMER_REASONS_KEY));
|
|
199
|
+
if (!current())
|
|
200
|
+
return;
|
|
173
201
|
const map = { ...(existing || {}) };
|
|
174
202
|
const hadMap = Object.keys(map).length > 0;
|
|
175
203
|
if (!hadMap) {
|
|
@@ -185,6 +213,8 @@ async function dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo) {
|
|
|
185
213
|
fired.push(reason);
|
|
186
214
|
}
|
|
187
215
|
for (const reason of fired) {
|
|
216
|
+
if (!current())
|
|
217
|
+
return;
|
|
188
218
|
delete map[reason];
|
|
189
219
|
const handler = handlers[reason];
|
|
190
220
|
// Unknown reasons silently dropped (forward-compat).
|
|
@@ -201,20 +231,25 @@ async function dispatchBody(ctx, handlers, onLegacyAlarm, alarmInfo) {
|
|
|
201
231
|
}
|
|
202
232
|
}
|
|
203
233
|
}
|
|
234
|
+
// A reset while the handlers ran: the map read above is gone, and
|
|
235
|
+
// writing it back — or arming for it — would revive what was ended.
|
|
236
|
+
if (!current())
|
|
237
|
+
return;
|
|
204
238
|
// Fold the in-dispatch arms, earliest-deadline-first per reason.
|
|
205
239
|
for (const arm of arms) {
|
|
206
240
|
if (!(arm.reason in map) || arm.whenMs < map[arm.reason]) {
|
|
207
241
|
map[arm.reason] = arm.whenMs;
|
|
208
242
|
}
|
|
209
243
|
}
|
|
210
|
-
// Re-arm or clear
|
|
244
|
+
// Re-arm or clear, issued in the turn the epoch was checked in: a reset
|
|
245
|
+
// after this point wipes and disarms after these, never before.
|
|
211
246
|
const setAlarmFn = ctx?.storage?.setAlarm;
|
|
212
247
|
if (Object.keys(map).length > 0) {
|
|
213
|
-
|
|
214
|
-
const earliest = Math.min(...Object.values(map));
|
|
248
|
+
const written = ctx.storage.put(TIMER_REASONS_KEY, map);
|
|
215
249
|
if (typeof setAlarmFn === 'function') {
|
|
216
|
-
setAlarmFn.call(ctx.storage,
|
|
250
|
+
setAlarmFn.call(ctx.storage, Math.min(...Object.values(map)));
|
|
217
251
|
}
|
|
252
|
+
await written;
|
|
218
253
|
}
|
|
219
254
|
else if (hadMap) {
|
|
220
255
|
try {
|
package/dist/vendor/types.d.ts
CHANGED
|
@@ -17,13 +17,19 @@ export interface JsonModule {
|
|
|
17
17
|
json: unknown;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
-
* A
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* A WebAssembly module, importable from a facet by module name. Nimbus ships
|
|
21
|
+
* these on every path — sql.js for node:sqlite, the interpreter images for
|
|
22
|
+
* python and ruby — so the omission here was the type lagging the API, not a
|
|
23
|
+
* kind the loader lacks. The loader takes either the bytes, which it
|
|
24
|
+
* compiles, or a module the caller holds compiled already, whose compiled
|
|
25
|
+
* code the dynamic worker then shares (workerd src/workerd/api/
|
|
26
|
+
* worker-loader.c++, extractWasmModuleContent). A module handed over this
|
|
27
|
+
* way should be described with describeHostWasm (host-wasm.ts): the
|
|
28
|
+
* code-size budget and the loader cache key cannot read a Module's size or
|
|
29
|
+
* identity from JS.
|
|
24
30
|
*/
|
|
25
31
|
export interface WasmModule {
|
|
26
|
-
wasm: ArrayBuffer;
|
|
32
|
+
wasm: ArrayBuffer | WebAssembly.Module;
|
|
27
33
|
}
|
|
28
34
|
/** Plain string = type inferred from file extension (.js or .py). */
|
|
29
35
|
export type ModuleContent = string | JsModule | CjsModule | PyModule | TextModule | DataModule | JsonModule | WasmModule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/vendor/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAA;CAAE;AACxC,MAAM,WAAW,SAAS;IAAG,GAAG,EAAE,MAAM,CAAA;CAAE;AAC1C,MAAM,WAAW,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAA;CAAE;AACxC,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,MAAM,CAAA;CAAE;AAC5C,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,WAAW,CAAA;CAAE;AACjD,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,OAAO,CAAA;CAAE;AAC7C
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/vendor/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAA;CAAE;AACxC,MAAM,WAAW,SAAS;IAAG,GAAG,EAAE,MAAM,CAAA;CAAE;AAC1C,MAAM,WAAW,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAA;CAAE;AACxC,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,MAAM,CAAA;CAAE;AAC5C,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,WAAW,CAAA;CAAE;AACjD,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,OAAO,CAAA;CAAE;AAC7C;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IAAG,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC,MAAM,CAAA;CAAE;AAEtE,qEAAqE;AACrE,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,UAAU,GACV,UAAU,GACV,UAAU,GACV,UAAU,CAAC;AAEf,MAAM,WAAW,UAAU;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE9B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CAClE;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,IAAI,cAAc,CAAC;IAChC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC;CACvE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/C;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,GAAG,UAAU,CAAC;CAC/D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workerd-facet-host.d.ts","sourceRoot":"","sources":["../src/workerd-facet-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"workerd-facet-host.d.ts","sourceRoot":"","sources":["../src/workerd-facet-host.ts"],"names":[],"mappings":"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"}
|