@nimbus-sh/fabric 0.2.0 → 0.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/README.md +169 -283
- package/dist/composition.d.ts +2 -86
- package/dist/composition.d.ts.map +1 -1
- package/dist/composition.js +2 -76
- package/dist/fenced-work.d.ts +33 -4
- package/dist/fenced-work.d.ts.map +1 -1
- package/dist/fenced-work.js +81 -28
- package/dist/process-fabric.d.ts +31 -1
- package/dist/process-fabric.d.ts.map +1 -1
- package/dist/process-fabric.js +19 -0
- package/dist/process-host.d.ts.map +1 -1
- package/dist/process-host.js +11 -4
- package/dist/turn-budget.d.ts +5 -0
- package/dist/turn-budget.d.ts.map +1 -1
- package/dist/turn-budget.js +2 -1
- package/dist/workerd-facet-host.d.ts +44 -8
- package/dist/workerd-facet-host.d.ts.map +1 -1
- package/dist/workerd-facet-host.js +80 -10
- package/package.json +3 -3
- package/src/composition.ts +16 -127
- package/src/fenced-work.ts +81 -30
- package/src/process-fabric.ts +37 -1
- package/src/process-host.ts +13 -6
- package/src/turn-budget.ts +4 -3
- package/src/workerd-facet-host.ts +93 -16
package/dist/composition.d.ts
CHANGED
|
@@ -1,87 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* The fabric mints supervisor bindings and assembles staged boots for the
|
|
5
|
-
* programs it hosts, but the entrypoint class that answers those bindings and
|
|
6
|
-
* the artifact sources a stage names both belong to the embedder. The
|
|
7
|
-
* embedder states them once, in its composition root, with one call:
|
|
8
|
-
*
|
|
9
|
-
* composeFabric({
|
|
10
|
-
* supervisorEntrypoint: 'SupervisorRPC',
|
|
11
|
-
* stagedBootAssembler: (env, stage) => assembleConfig(env, stage),
|
|
12
|
-
* });
|
|
13
|
-
*
|
|
14
|
-
* First-write-wins, like every holder in this module: the composition root's
|
|
15
|
-
* module scope runs once per isolate, before any request.
|
|
16
|
-
*
|
|
17
|
-
* `ctx.exports` is runtime state, not composition: workerd mints it per
|
|
18
|
-
* instance, so the embedder captures it where the platform hands it over —
|
|
19
|
-
* the first fetch, or the DO constructor — with {@link adoptCtxExports}.
|
|
20
|
-
*
|
|
21
|
-
* This module stays a leaf (no fabric imports) so helpers (notably
|
|
22
|
-
* isolate-pool.ts) can read `ctx.exports` without transitively importing the
|
|
23
|
-
* Durable Object classes, which is what lets the pool be unit-tested in a
|
|
24
|
-
* plain Node/Bun process.
|
|
25
|
-
*/
|
|
26
|
-
/**
|
|
27
|
-
* One entry of `ctx.exports`: a top-level entrypoint's loopback factory, which
|
|
28
|
-
* mints a Service Binding stub for that entrypoint when called with props.
|
|
29
|
-
*
|
|
30
|
-
* The stub's RPC surface belongs to the entrypoint CLASS, which this leaf
|
|
31
|
-
* cannot see — `Cloudflare.Exports` is derived from the embedder's own main
|
|
32
|
-
* module, so for a library it evaluates to `{}`. A caller that knows the class
|
|
33
|
-
* names the surface it expects (`factory<MySupervisorRpc>({ props })`); one
|
|
34
|
-
* that does not gets `unknown` and has to narrow, same as
|
|
35
|
-
* `DurableObjectNamespace<T>` and `RpcStub<T>` in @cloudflare/workers-types.
|
|
36
|
-
*/
|
|
37
|
-
export type EntrypointLoopbackFactory = <Stub = unknown>(options: {
|
|
38
|
-
props: object;
|
|
39
|
-
}) => Stub;
|
|
40
|
-
/**
|
|
41
|
-
* `ctx.exports` itself — one factory per top-level entrypoint export, keyed by
|
|
42
|
-
* export name. Absent names read as undefined, which is how a caller finds out
|
|
43
|
-
* the embedder's entry module does not re-export the class it needs.
|
|
44
|
-
*/
|
|
45
|
-
export type CtxExports = Record<string, EntrypointLoopbackFactory | undefined>;
|
|
46
|
-
/**
|
|
47
|
-
* Assemble a complete Worker Loader config from a staged-artifact spec. The
|
|
48
|
-
* embedder supplies this: a stage names artifact sources only the embedder
|
|
49
|
-
* knows how to fetch (Nimbus's largest staged artifact is a ~23 MB module map
|
|
50
|
-
* from ASSETS), and the assembler runs inside the loader's cache-miss callback
|
|
51
|
-
* so those sources are materialized only while the facet actually loads.
|
|
52
|
-
* `env` is whichever hosting actor's env the facet is opened with.
|
|
53
|
-
*/
|
|
54
|
-
export type StagedBootAssembler = (env: unknown, stage: unknown) => Promise<object>;
|
|
55
|
-
/** What an embedder states about itself, once, in its composition root. */
|
|
56
|
-
export interface FabricComposition {
|
|
57
|
-
/**
|
|
58
|
-
* The ctx.exports name of the embedder's supervisor WorkerEntrypoint. The
|
|
59
|
-
* fabric mints one supervisor binding per hosted program from it
|
|
60
|
-
* (`env.SUPERVISOR` inside the facet).
|
|
61
|
-
*/
|
|
62
|
-
supervisorEntrypoint: string;
|
|
63
|
-
/** Only embedders that use 'staged' boot specs supply one. */
|
|
64
|
-
stagedBootAssembler?: StagedBootAssembler;
|
|
65
|
-
}
|
|
66
|
-
/** Register the embedder's composition. First-write-wins. */
|
|
67
|
-
export declare function composeFabric(composition: FabricComposition): void;
|
|
68
|
-
/**
|
|
69
|
-
* Capture `ctx.exports` for the helpers that mint loopback bindings. The
|
|
70
|
-
* embedder calls this where the platform hands the bag over — the first
|
|
71
|
-
* fetch, or the DO constructor. First-write-wins.
|
|
72
|
-
*/
|
|
73
|
-
export declare function adoptCtxExports(value: CtxExports): void;
|
|
74
|
-
export declare function getCtxExports(): CtxExports | null;
|
|
75
|
-
/**
|
|
76
|
-
* Resolve the composed supervisor entrypoint on an exports object —
|
|
77
|
-
* `exportsObj` when given (a WorkerEntrypoint reads its own ctx.exports),
|
|
78
|
-
* the adopted ctx.exports otherwise. Calling the result with props mints one
|
|
79
|
-
* supervisor binding (`env.SUPERVISOR`) for one hosted program. Null when
|
|
80
|
-
* either half is missing; the caller decides whether that degrades or throws.
|
|
81
|
-
*/
|
|
82
|
-
export declare function supervisorEntrypoint(exportsObj?: unknown): EntrypointLoopbackFactory | null;
|
|
83
|
-
/** The composed name, for error messages that point at the missing export. */
|
|
84
|
-
export declare function supervisorEntrypointName(): string | null;
|
|
85
|
-
/** The composed assembler; a 'staged' boot spec cannot assemble without one. */
|
|
86
|
-
export declare function stagedBootAssembler(): StagedBootAssembler;
|
|
1
|
+
export { adoptCtxExports, composeFabric, getCtxExports, stagedBootAssembler, supervisorEntrypoint, supervisorEntrypointName, } from '@nimbus-sh/platform/composition.js';
|
|
2
|
+
export type { CtxExports, EntrypointLoopbackFactory, FabricComposition, StagedBootAssembler, } from '@nimbus-sh/platform/composition.js';
|
|
87
3
|
//# sourceMappingURL=composition.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../src/composition.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"composition.d.ts","sourceRoot":"","sources":["../src/composition.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,oCAAoC,CAAC;AAE5C,YAAY,EACV,UAAU,EACV,yBAAyB,EACzB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,oCAAoC,CAAC"}
|
package/dist/composition.js
CHANGED
|
@@ -1,76 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* The fabric mints supervisor bindings and assembles staged boots for the
|
|
5
|
-
* programs it hosts, but the entrypoint class that answers those bindings and
|
|
6
|
-
* the artifact sources a stage names both belong to the embedder. The
|
|
7
|
-
* embedder states them once, in its composition root, with one call:
|
|
8
|
-
*
|
|
9
|
-
* composeFabric({
|
|
10
|
-
* supervisorEntrypoint: 'SupervisorRPC',
|
|
11
|
-
* stagedBootAssembler: (env, stage) => assembleConfig(env, stage),
|
|
12
|
-
* });
|
|
13
|
-
*
|
|
14
|
-
* First-write-wins, like every holder in this module: the composition root's
|
|
15
|
-
* module scope runs once per isolate, before any request.
|
|
16
|
-
*
|
|
17
|
-
* `ctx.exports` is runtime state, not composition: workerd mints it per
|
|
18
|
-
* instance, so the embedder captures it where the platform hands it over —
|
|
19
|
-
* the first fetch, or the DO constructor — with {@link adoptCtxExports}.
|
|
20
|
-
*
|
|
21
|
-
* This module stays a leaf (no fabric imports) so helpers (notably
|
|
22
|
-
* isolate-pool.ts) can read `ctx.exports` without transitively importing the
|
|
23
|
-
* Durable Object classes, which is what lets the pool be unit-tested in a
|
|
24
|
-
* plain Node/Bun process.
|
|
25
|
-
*/
|
|
26
|
-
let _composition = null;
|
|
27
|
-
/** Register the embedder's composition. First-write-wins. */
|
|
28
|
-
export function composeFabric(composition) {
|
|
29
|
-
if (_composition)
|
|
30
|
-
return;
|
|
31
|
-
_composition = composition;
|
|
32
|
-
}
|
|
33
|
-
let _ctxExports = null;
|
|
34
|
-
/**
|
|
35
|
-
* Capture `ctx.exports` for the helpers that mint loopback bindings. The
|
|
36
|
-
* embedder calls this where the platform hands the bag over — the first
|
|
37
|
-
* fetch, or the DO constructor. First-write-wins.
|
|
38
|
-
*/
|
|
39
|
-
export function adoptCtxExports(value) {
|
|
40
|
-
if (_ctxExports)
|
|
41
|
-
return;
|
|
42
|
-
_ctxExports = value;
|
|
43
|
-
}
|
|
44
|
-
export function getCtxExports() {
|
|
45
|
-
return _ctxExports;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Resolve the composed supervisor entrypoint on an exports object —
|
|
49
|
-
* `exportsObj` when given (a WorkerEntrypoint reads its own ctx.exports),
|
|
50
|
-
* the adopted ctx.exports otherwise. Calling the result with props mints one
|
|
51
|
-
* supervisor binding (`env.SUPERVISOR`) for one hosted program. Null when
|
|
52
|
-
* either half is missing; the caller decides whether that degrades or throws.
|
|
53
|
-
*/
|
|
54
|
-
export function supervisorEntrypoint(exportsObj) {
|
|
55
|
-
const exports = exportsObj ?? _ctxExports;
|
|
56
|
-
const name = _composition?.supervisorEntrypoint;
|
|
57
|
-
if (!name)
|
|
58
|
-
return null;
|
|
59
|
-
if ((typeof exports !== 'object' && typeof exports !== 'function') || exports === null)
|
|
60
|
-
return null;
|
|
61
|
-
const factory = exports[name];
|
|
62
|
-
return typeof factory === 'function' ? factory : null;
|
|
63
|
-
}
|
|
64
|
-
/** The composed name, for error messages that point at the missing export. */
|
|
65
|
-
export function supervisorEntrypointName() {
|
|
66
|
-
return _composition?.supervisorEntrypoint ?? null;
|
|
67
|
-
}
|
|
68
|
-
/** The composed assembler; a 'staged' boot spec cannot assemble without one. */
|
|
69
|
-
export function stagedBootAssembler() {
|
|
70
|
-
const assembler = _composition?.stagedBootAssembler;
|
|
71
|
-
if (!assembler) {
|
|
72
|
-
throw new Error('fabric: no staged-boot assembler composed; a \'staged\' boot spec '
|
|
73
|
-
+ 'cannot be assembled without one (composeFabric)');
|
|
74
|
-
}
|
|
75
|
-
return assembler;
|
|
76
|
-
}
|
|
1
|
+
// Core and fabric share one holder without introducing a dependency cycle.
|
|
2
|
+
export { adoptCtxExports, composeFabric, getCtxExports, stagedBootAssembler, supervisorEntrypoint, supervisorEntrypointName, } from '@nimbus-sh/platform/composition.js';
|
package/dist/fenced-work.d.ts
CHANGED
|
@@ -131,13 +131,16 @@ export declare class FencedWork<R extends FencedWorkRecord> {
|
|
|
131
131
|
* paying a storage delete for pids that never had a row.
|
|
132
132
|
*/
|
|
133
133
|
private journalledPids;
|
|
134
|
+
/** Re-drives in flight, by journal key — recovery's un-awaited ones and a
|
|
135
|
+
* caller-driven one share the same drive for the same row. */
|
|
136
|
+
private drives;
|
|
134
137
|
/** Whether this instance has already read the journal a reset leaves behind. */
|
|
135
138
|
private recovered;
|
|
136
139
|
constructor(storage: FencedWorkStorage, host: FencedWorkHost<R>);
|
|
137
140
|
/**
|
|
138
141
|
* Record a launch as in flight, so an instance that replaces this one knows
|
|
139
|
-
* it never finished.
|
|
140
|
-
*
|
|
142
|
+
* it never finished. A launch that cannot be journalled does not start: the
|
|
143
|
+
* rejection reaches the caller, which reports it like any launch failure.
|
|
141
144
|
*
|
|
142
145
|
* Synced, not merely put: `await put()` resolves before durability, and the
|
|
143
146
|
* reset this journal exists for destroys every write its turn still had
|
|
@@ -158,6 +161,28 @@ export declare class FencedWork<R extends FencedWorkRecord> {
|
|
|
158
161
|
* resurrect a process the user watched end.
|
|
159
162
|
*/
|
|
160
163
|
release(pid: number): Promise<void>;
|
|
164
|
+
/**
|
|
165
|
+
* Drop every row `predicate` claims, live-pid bookkeeping included, synced
|
|
166
|
+
* like {@link release}. The one bulk delete the journal admits: an owner
|
|
167
|
+
* that removes its durable application is owed no recovery, however many
|
|
168
|
+
* generations back its rows were written.
|
|
169
|
+
*/
|
|
170
|
+
purgeWhere(predicate: (record: R) => boolean): Promise<number>;
|
|
171
|
+
/**
|
|
172
|
+
* Every journal row, storage-true — the rows this instance wrote and the
|
|
173
|
+
* ones a previous instance left behind. The one read surface a request-
|
|
174
|
+
* driven recovery needs to find the durable launch a port belongs to.
|
|
175
|
+
*/
|
|
176
|
+
rows(): Promise<Map<string, R>>;
|
|
177
|
+
/**
|
|
178
|
+
* Re-drive one journal row — the awaited sibling of recovery's un-awaited
|
|
179
|
+
* re-drives, for a caller that must know whether the launch actually came
|
|
180
|
+
* back. Single-flight per row: a request-driven drive and recovery's own
|
|
181
|
+
* never boot the same launch twice. Resolves true only when the re-drive
|
|
182
|
+
* itself FAILED and the failure was reported; a settled drive supersedes
|
|
183
|
+
* the row the same way recovery's does.
|
|
184
|
+
*/
|
|
185
|
+
drive(key: string, record: R): Promise<boolean>;
|
|
161
186
|
/**
|
|
162
187
|
* Re-drive the launches a previous instance was building when it was reset.
|
|
163
188
|
*
|
|
@@ -167,8 +192,12 @@ export declare class FencedWork<R extends FencedWorkRecord> {
|
|
|
167
192
|
* that replaces this one. So the first turn after a reset is already this
|
|
168
193
|
* one.
|
|
169
194
|
*
|
|
170
|
-
* Runs once per instance
|
|
171
|
-
*
|
|
195
|
+
* Runs once per instance — re-calls in the same instance are no-ops — and
|
|
196
|
+
* re-drives every row whose pid is `> 0` and at or below `generationBase()`
|
|
197
|
+
* with `attempt < FENCED_WORK_MAX_ATTEMPT`; the rest are abandoned. What the
|
|
198
|
+
* re-drive resolver receives is the journalled recipe and nothing else:
|
|
199
|
+
* env and credentials are never written to storage, so the resolver's
|
|
200
|
+
* embedder re-resolves them rather than reading them back.
|
|
172
201
|
*/
|
|
173
202
|
recoverInterrupted(): Promise<void>;
|
|
174
203
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fenced-work.d.ts","sourceRoot":"","sources":["../src/fenced-work.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD,gFAAgF;AAChF,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB;;4DAEwD;IACxD,KAAK,EAAE,UAAU,GAAG,SAAS,CAAC;CAC/B;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,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,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,2DAA2D;AAC3D,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,gBAAgB;IACxD;;;;;;OAMG;IACH,cAAc,IAAI,MAAM,CAAC;IACzB;;;;;;;;OAQG;IACH,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3C;;;;;OAKG;IACH,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,mDAAmD;IACnD,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAC5B,yEAAyE;IACzE,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAC9B,kCAAkC;IAClC,eAAe,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,qBAAa,UAAU,CAAC,CAAC,SAAS,gBAAgB;
|
|
1
|
+
{"version":3,"file":"fenced-work.d.ts","sourceRoot":"","sources":["../src/fenced-work.ts"],"names":[],"mappings":"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"}
|
package/dist/fenced-work.js
CHANGED
|
@@ -56,6 +56,9 @@ export class FencedWork {
|
|
|
56
56
|
* paying a storage delete for pids that never had a row.
|
|
57
57
|
*/
|
|
58
58
|
journalledPids = new Set();
|
|
59
|
+
/** Re-drives in flight, by journal key — recovery's un-awaited ones and a
|
|
60
|
+
* caller-driven one share the same drive for the same row. */
|
|
61
|
+
drives = new Map();
|
|
59
62
|
/** Whether this instance has already read the journal a reset leaves behind. */
|
|
60
63
|
recovered = false;
|
|
61
64
|
constructor(storage, host) {
|
|
@@ -64,8 +67,8 @@ export class FencedWork {
|
|
|
64
67
|
}
|
|
65
68
|
/**
|
|
66
69
|
* Record a launch as in flight, so an instance that replaces this one knows
|
|
67
|
-
* it never finished.
|
|
68
|
-
*
|
|
70
|
+
* it never finished. A launch that cannot be journalled does not start: the
|
|
71
|
+
* rejection reaches the caller, which reports it like any launch failure.
|
|
69
72
|
*
|
|
70
73
|
* Synced, not merely put: `await put()` resolves before durability, and the
|
|
71
74
|
* reset this journal exists for destroys every write its turn still had
|
|
@@ -78,13 +81,13 @@ export class FencedWork {
|
|
|
78
81
|
* losing its row costs a retype, not a recovery.
|
|
79
82
|
*/
|
|
80
83
|
async journal(record) {
|
|
84
|
+
this.journalledPids.add(record.pid);
|
|
81
85
|
try {
|
|
82
|
-
this.journalledPids.add(record.pid);
|
|
83
86
|
await this.storage.put(`${FENCED_WORK_KEY_PREFIX}${record.pid}`, record);
|
|
84
87
|
await this.storage.sync();
|
|
85
88
|
}
|
|
86
|
-
catch (
|
|
87
|
-
|
|
89
|
+
catch (cause) {
|
|
90
|
+
throw new Error('resident launch journal write failed', { cause });
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
93
|
/** True while this instance holds a journal row for `pid`. */
|
|
@@ -99,13 +102,69 @@ export class FencedWork {
|
|
|
99
102
|
async release(pid) {
|
|
100
103
|
if (!this.journalledPids.delete(pid))
|
|
101
104
|
return;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
await this.storage.delete(`${FENCED_WORK_KEY_PREFIX}${pid}`);
|
|
106
|
+
await this.storage.sync();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Drop every row `predicate` claims, live-pid bookkeeping included, synced
|
|
110
|
+
* like {@link release}. The one bulk delete the journal admits: an owner
|
|
111
|
+
* that removes its durable application is owed no recovery, however many
|
|
112
|
+
* generations back its rows were written.
|
|
113
|
+
*/
|
|
114
|
+
async purgeWhere(predicate) {
|
|
115
|
+
const rows = await this.storage.list({ prefix: FENCED_WORK_KEY_PREFIX });
|
|
116
|
+
let purged = 0;
|
|
117
|
+
for (const [key, record] of rows) {
|
|
118
|
+
if (!predicate(record))
|
|
119
|
+
continue;
|
|
120
|
+
this.journalledPids.delete(record.pid);
|
|
121
|
+
await this.storage.delete(key);
|
|
122
|
+
purged += 1;
|
|
105
123
|
}
|
|
106
|
-
|
|
107
|
-
|
|
124
|
+
if (purged > 0)
|
|
125
|
+
await this.storage.sync();
|
|
126
|
+
return purged;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Every journal row, storage-true — the rows this instance wrote and the
|
|
130
|
+
* ones a previous instance left behind. The one read surface a request-
|
|
131
|
+
* driven recovery needs to find the durable launch a port belongs to.
|
|
132
|
+
*/
|
|
133
|
+
async rows() {
|
|
134
|
+
return this.storage.list({ prefix: FENCED_WORK_KEY_PREFIX });
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Re-drive one journal row — the awaited sibling of recovery's un-awaited
|
|
138
|
+
* re-drives, for a caller that must know whether the launch actually came
|
|
139
|
+
* back. Single-flight per row: a request-driven drive and recovery's own
|
|
140
|
+
* never boot the same launch twice. Resolves true only when the re-drive
|
|
141
|
+
* itself FAILED and the failure was reported; a settled drive supersedes
|
|
142
|
+
* the row the same way recovery's does.
|
|
143
|
+
*/
|
|
144
|
+
drive(key, record) {
|
|
145
|
+
let inflight = this.drives.get(key);
|
|
146
|
+
if (inflight === undefined) {
|
|
147
|
+
inflight = (async () => {
|
|
148
|
+
let failed = false;
|
|
149
|
+
try {
|
|
150
|
+
await this.host.redrive(record, record.attempt + 1);
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
this.host.onRedriveFailed?.(record, e);
|
|
154
|
+
failed = true;
|
|
155
|
+
}
|
|
156
|
+
// A reported failure is settled business — supersede either way, so
|
|
157
|
+
// the row never re-surfaces on the next recovery.
|
|
158
|
+
await this.supersede(key);
|
|
159
|
+
return failed;
|
|
160
|
+
})();
|
|
161
|
+
this.drives.set(key, inflight);
|
|
162
|
+
inflight.finally(() => {
|
|
163
|
+
if (this.drives.get(key) === inflight)
|
|
164
|
+
this.drives.delete(key);
|
|
165
|
+
});
|
|
108
166
|
}
|
|
167
|
+
return inflight;
|
|
109
168
|
}
|
|
110
169
|
/**
|
|
111
170
|
* Re-drive the launches a previous instance was building when it was reset.
|
|
@@ -116,8 +175,12 @@ export class FencedWork {
|
|
|
116
175
|
* that replaces this one. So the first turn after a reset is already this
|
|
117
176
|
* one.
|
|
118
177
|
*
|
|
119
|
-
* Runs once per instance
|
|
120
|
-
*
|
|
178
|
+
* Runs once per instance — re-calls in the same instance are no-ops — and
|
|
179
|
+
* re-drives every row whose pid is `> 0` and at or below `generationBase()`
|
|
180
|
+
* with `attempt < FENCED_WORK_MAX_ATTEMPT`; the rest are abandoned. What the
|
|
181
|
+
* re-drive resolver receives is the journalled recipe and nothing else:
|
|
182
|
+
* env and credentials are never written to storage, so the resolver's
|
|
183
|
+
* embedder re-resolves them rather than reading them back.
|
|
121
184
|
*/
|
|
122
185
|
async recoverInterrupted() {
|
|
123
186
|
if (this.recovered)
|
|
@@ -159,12 +222,10 @@ export class FencedWork {
|
|
|
159
222
|
// Not awaited: this call is running inside the alarm that granted the
|
|
160
223
|
// turn, and the launch it starts asks for turns of its own through that
|
|
161
224
|
// same alarm — awaiting it here would be waiting on an alarm that cannot
|
|
162
|
-
// be scheduled until this one returns.
|
|
163
|
-
this
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
})
|
|
167
|
-
.then(() => this.supersede(key)));
|
|
225
|
+
// be scheduled until this one returns. `drive` single-flights it: a
|
|
226
|
+
// request that arrives mid-launch waits on this same drive rather than
|
|
227
|
+
// booting a second process.
|
|
228
|
+
this.host.waitUntil(this.drive(key, record));
|
|
168
229
|
}
|
|
169
230
|
}
|
|
170
231
|
/**
|
|
@@ -174,15 +235,7 @@ export class FencedWork {
|
|
|
174
235
|
* a previous generation the terminal hook will never fire for.
|
|
175
236
|
*/
|
|
176
237
|
async supersede(key) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
await this.storage.sync();
|
|
180
|
-
}
|
|
181
|
-
catch (e) {
|
|
182
|
-
console.warn('[nimbus] resident launch journal supersede failed:', errorMessage(e));
|
|
183
|
-
}
|
|
238
|
+
await this.storage.delete(key);
|
|
239
|
+
await this.storage.sync();
|
|
184
240
|
}
|
|
185
241
|
}
|
|
186
|
-
function errorMessage(error) {
|
|
187
|
-
return error instanceof Error ? error.message : String(error);
|
|
188
|
-
}
|
package/dist/process-fabric.d.ts
CHANGED
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
*/
|
|
76
76
|
import { z } from 'zod/v4';
|
|
77
77
|
import type { RouteableFacetTarget } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
78
|
+
import type { ServiceStub } from './vendor/types.js';
|
|
78
79
|
/**
|
|
79
80
|
* The class every generated resident runner exports. One name for every
|
|
80
81
|
* runtime: the fabric names it unconditionally, so nothing about which program
|
|
@@ -107,6 +108,8 @@ export declare const ResidentCodeSpecSchema: z.ZodObject<{
|
|
|
107
108
|
}, z.core.$strip>]>>;
|
|
108
109
|
vfsWasmModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
109
110
|
vfsTextModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
111
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
112
|
+
globalOutbound: z.ZodOptional<z.ZodNullable<z.ZodCustom<ServiceStub, ServiceStub>>>;
|
|
110
113
|
}, z.core.$strip>;
|
|
111
114
|
export type ResidentCodeSpec = z.infer<typeof ResidentCodeSpecSchema>;
|
|
112
115
|
/**
|
|
@@ -132,6 +135,8 @@ export declare function residentBootSpecSchema<Stage extends z.ZodType>(stageSch
|
|
|
132
135
|
}, z.core.$strip>]>>;
|
|
133
136
|
vfsWasmModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
134
137
|
vfsTextModules: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
138
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
139
|
+
globalOutbound: z.ZodOptional<z.ZodNullable<z.ZodCustom<ServiceStub, ServiceStub>>>;
|
|
135
140
|
}, z.core.$strip>;
|
|
136
141
|
}, z.core.$strip>], "kind">;
|
|
137
142
|
export type ResidentBootSpec = {
|
|
@@ -201,6 +206,11 @@ export interface ResidentDiskReader {
|
|
|
201
206
|
* path, verifying each generated image against the digest its own path claims.
|
|
202
207
|
* Runs inside the loader's cache-miss callback, so the bytes exist only for
|
|
203
208
|
* the duration of the load.
|
|
209
|
+
*
|
|
210
|
+
* The spec's isolation posture rides along verbatim: an explicit `env` is
|
|
211
|
+
* the isolate's whole env (loopback stubs by reference, never cloned or
|
|
212
|
+
* re-minted). An absent env stays absent
|
|
213
|
+
* so the worker config can tell "embedder takes the env" from the default.
|
|
204
214
|
*/
|
|
205
215
|
export declare function residentLoaderConfig(spec: ResidentCodeSpec, disk: ResidentDiskReader): Promise<Record<string, unknown>>;
|
|
206
216
|
/**
|
|
@@ -225,6 +235,17 @@ export interface ProcessHostParams {
|
|
|
225
235
|
writerId: string;
|
|
226
236
|
/** Forwarded verbatim to the runner's startProcess. */
|
|
227
237
|
startArgs: unknown;
|
|
238
|
+
/**
|
|
239
|
+
* Set only by the coordinator's durable-application path: an explicit facet
|
|
240
|
+
* name (`app-slot-<n>`) allocated from DO storage, plus the release split
|
|
241
|
+
* that keeps its SQLite across aborts. Absent, the host allocates an
|
|
242
|
+
* ephemeral `proc-slot-<n>` name from its in-memory free list and deletes
|
|
243
|
+
* the store on release.
|
|
244
|
+
*/
|
|
245
|
+
facet?: {
|
|
246
|
+
name: string;
|
|
247
|
+
durable: boolean;
|
|
248
|
+
};
|
|
228
249
|
}
|
|
229
250
|
/**
|
|
230
251
|
* One resident process, as its coordinator sees it. Identical in meaning on
|
|
@@ -483,10 +504,19 @@ export interface ResidentProcessSpawn {
|
|
|
483
504
|
pid: number;
|
|
484
505
|
/** Keyed dynamic-worker identity (`nimbus-process:${doId}:${pid}`). */
|
|
485
506
|
workerKey: string;
|
|
486
|
-
/** What the
|
|
507
|
+
/** What the process boots from. */
|
|
487
508
|
boot: ResidentBootSpec;
|
|
488
509
|
/** Forwarded verbatim to the runner's startProcess. */
|
|
489
510
|
startArgs?: unknown;
|
|
511
|
+
/**
|
|
512
|
+
* A durable application's explicit facet name (`app-slot-<n>`) and the
|
|
513
|
+
* release split that keeps its SQLite. Coordinator-allocated; absent for an
|
|
514
|
+
* ephemeral process, which takes a `proc-slot-<n>` name from the book.
|
|
515
|
+
*/
|
|
516
|
+
facet?: {
|
|
517
|
+
name: string;
|
|
518
|
+
durable: boolean;
|
|
519
|
+
};
|
|
490
520
|
/**
|
|
491
521
|
* Called before any concrete host capability can expose this writer.
|
|
492
522
|
* A spawn must not proceed unless the supervisor accepts the authority.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-fabric.d.ts","sourceRoot":"","sources":["../src/process-fabric.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAC3B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;
|
|
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,17 @@ 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
|
+
* The isolate's exact `env`: one entry per binding the embedder minted,
|
|
120
|
+
* carried by reference so loopback stubs survive untouched. Defined —
|
|
121
|
+
* even as `{}` — means the embedder takes the whole env and no
|
|
122
|
+
* SUPERVISOR binding is injected; absent keeps the default (a SUPERVISOR
|
|
123
|
+
* minted from the composed supervisor entrypoint).
|
|
124
|
+
*/
|
|
125
|
+
env: z.record(z.string(), z.unknown()).optional(),
|
|
126
|
+
/** Absent inherits outbound, null denies it, a binding mediates it by reference. */
|
|
127
|
+
globalOutbound: z.custom((value) => value !== null && (typeof value === 'object' || typeof value === 'function')
|
|
128
|
+
&& 'fetch' in value && typeof value.fetch === 'function').nullable().optional(),
|
|
118
129
|
});
|
|
119
130
|
/**
|
|
120
131
|
* The boot-spec union, with the staged arm's payload validated by the
|
|
@@ -191,6 +202,11 @@ export function facetImagePathDigest(path) {
|
|
|
191
202
|
* path, verifying each generated image against the digest its own path claims.
|
|
192
203
|
* Runs inside the loader's cache-miss callback, so the bytes exist only for
|
|
193
204
|
* the duration of the load.
|
|
205
|
+
*
|
|
206
|
+
* The spec's isolation posture rides along verbatim: an explicit `env` is
|
|
207
|
+
* the isolate's whole env (loopback stubs by reference, never cloned or
|
|
208
|
+
* re-minted). An absent env stays absent
|
|
209
|
+
* so the worker config can tell "embedder takes the env" from the default.
|
|
194
210
|
*/
|
|
195
211
|
export async function residentLoaderConfig(spec, disk) {
|
|
196
212
|
const resolved = {};
|
|
@@ -208,6 +224,8 @@ export async function residentLoaderConfig(spec, disk) {
|
|
|
208
224
|
compatibilityFlags: spec.compatibilityFlags,
|
|
209
225
|
mainModule: spec.mainModule,
|
|
210
226
|
modules: { ...spec.modules, ...resolved },
|
|
227
|
+
...(spec.env !== undefined ? { env: spec.env } : {}),
|
|
228
|
+
...(spec.globalOutbound !== undefined ? { globalOutbound: spec.globalOutbound } : {}),
|
|
211
229
|
};
|
|
212
230
|
}
|
|
213
231
|
/**
|
|
@@ -331,6 +349,7 @@ export class ProcessFabric {
|
|
|
331
349
|
boot: spawn.boot,
|
|
332
350
|
writerId,
|
|
333
351
|
startArgs: spawn.startArgs,
|
|
352
|
+
...(spawn.facet !== undefined ? { facet: spawn.facet } : {}),
|
|
334
353
|
});
|
|
335
354
|
}
|
|
336
355
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-host.d.ts","sourceRoot":"","sources":["../src/process-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAKH,OAAO,EAGL,KAAK,WAAW,EAIhB,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"process-host.d.ts","sourceRoot":"","sources":["../src/process-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAKH,OAAO,EAGL,KAAK,WAAW,EAIhB,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAQ7B,4DAA4D;AAC5D,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,MAAM,kBAAkB,GAC7B,WAAW,CAIb;AA8ED,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;CAC7B;AA2BD;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,8BAA8B,CAAC;AACvE,eAAO,MAAM,kCAAkC,yCAAyC,CAAC;AAgSzF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAWhE"}
|
package/dist/process-host.js
CHANGED
|
@@ -67,8 +67,8 @@ import { disposeRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
|
|
|
67
67
|
import { isTransientDoReset } from '@nimbus-sh/platform/oom-classify.js';
|
|
68
68
|
import { PEER_RETRY_BACKOFF_MS, PEER_TRANSIENT_RESET_RETRIES } from './fanout.js';
|
|
69
69
|
import { DYNAMIC_WORKER_CODE_LIMIT_BYTES } from './budgets.js';
|
|
70
|
-
import { processes, residentFacetName, } from './workerd-facet-host.js';
|
|
71
70
|
import { BindingError } from './vendor/errors.js';
|
|
71
|
+
import { processes, } from './workerd-facet-host.js';
|
|
72
72
|
/**
|
|
73
73
|
* The substrate for this deployment, resolved once. The mode arrives already
|
|
74
74
|
* decided — the embedder owns the config var that picks it, and refuses an
|
|
@@ -114,10 +114,10 @@ class FacetProcessHost {
|
|
|
114
114
|
pid: params.pid,
|
|
115
115
|
writerId: params.writerId,
|
|
116
116
|
};
|
|
117
|
-
const {
|
|
117
|
+
const { name, ...facet } = processes(this.ctx, this.env).spawn(this.disk, supervisor, params);
|
|
118
118
|
return {
|
|
119
119
|
...facet,
|
|
120
|
-
describe: () => `facet '${
|
|
120
|
+
describe: () => `facet '${name}' (pid ${params.pid})`
|
|
121
121
|
+ ` of session ${this.coordDoId.slice(-12)}`
|
|
122
122
|
+ `; ${describeImageDelivery(this.imageDelivery)}`,
|
|
123
123
|
};
|
|
@@ -235,8 +235,15 @@ class PeerProcessHost {
|
|
|
235
235
|
return processes(this.ctx, this.env).run({ doId: this.coordDoId, pid: params.pid, writerId: params.writerId }, params, consume);
|
|
236
236
|
}
|
|
237
237
|
async open(params) {
|
|
238
|
+
if (params.facet) {
|
|
239
|
+
// A durable application's facet must be a child of the COORDINATOR's
|
|
240
|
+
// Durable Object — its `app-slot-<n>` row and retained SQLite live in
|
|
241
|
+
// that DO's storage. A sibling host would own storage the coordinator's
|
|
242
|
+
// durable-slot book and removeDurableApp cannot reach.
|
|
243
|
+
throw new Error('Nimbus: a durable spawn must be facet-hosted on its own coordinator; '
|
|
244
|
+
+ 'the peer substrate cannot serve one');
|
|
245
|
+
}
|
|
238
246
|
const placement = await this._place(params.pid);
|
|
239
|
-
this.tokensInUse.set(params.pid, placement.isolateToken);
|
|
240
247
|
// Minted per open, held only by this coordinator and the peer that hosts
|
|
241
248
|
// the process. The workerKey is derivable from a pid; this is not.
|
|
242
249
|
const webSocketCapability = crypto.randomUUID();
|
package/dist/turn-budget.d.ts
CHANGED
|
@@ -97,6 +97,11 @@ export declare class TurnBudget {
|
|
|
97
97
|
*/
|
|
98
98
|
settle(): void;
|
|
99
99
|
}
|
|
100
|
+
/** `Promise.withResolvers` for the runtime the project targets. */
|
|
101
|
+
export declare function withResolvers<T = void>(): {
|
|
102
|
+
promise: Promise<T>;
|
|
103
|
+
resolve: (value: T | PromiseLike<T>) => void;
|
|
104
|
+
};
|
|
100
105
|
/** What {@link PacedWork} needs from the Durable Object hosting it. */
|
|
101
106
|
export interface PacedWorkHost {
|
|
102
107
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn-budget.d.ts","sourceRoot":"","sources":["../src/turn-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,UAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,qBAAa,UAAU;IAkBnB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAnB/B,qEAAqE;IACrE,MAAM,SAAK;IACX,iDAAiD;IACjD,KAAK,SAAK;IAEV,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,UAAU,CAA8D;IAEhF;;;;;;;OAOG;gBAEgB,SAAS,EAAE,aAAa,EACxB,aAAa,GAAE,MAA6B,EAC5C,WAAW,CAAC,GAAE,MAAM,IAAI,aAAA;IAG3C;;;;;;OAMG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAczC;;;;OAIG;IACH,MAAM,IAAI,IAAI;CAIf;
|
|
1
|
+
{"version":3,"file":"turn-budget.d.ts","sourceRoot":"","sources":["../src/turn-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAIH,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B;;;;;;;OAOG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,UAAY,CAAC;AAE9C;;;;;;;;;GASG;AACH,qBAAa,UAAU;IAkBnB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAnB/B,qEAAqE;IACrE,MAAM,SAAK;IACX,iDAAiD;IACjD,KAAK,SAAK;IAEV,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,UAAU,CAA8D;IAEhF;;;;;;;OAOG;gBAEgB,SAAS,EAAE,aAAa,EACxB,aAAa,GAAE,MAA6B,EAC5C,WAAW,CAAC,GAAE,MAAM,IAAI,aAAA;IAG3C;;;;;;OAMG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAczC;;;;OAIG;IACH,MAAM,IAAI,IAAI;CAIf;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,CAAC,GAAG,IAAI,KAAK;IAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAAC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;CAAE,CAI/G;AAED,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED;;;GAGG;AACH,qBAAa,SAAU,YAAW,aAAa;IAkB3C,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAlBvB;;;;;;;;;OASG;IACH,OAAO,CAAC,OAAO,CAAgE;IAE/E;;;OAGG;gBAEgB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,aAAa;IAGtC;;;;;;;;;OASG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAWlD;;;;;;;OAOG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAa3B,0DAA0D;IAC1D,IAAI,UAAU,IAAI,OAAO,CAExB;CACF;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAMtD"}
|
package/dist/turn-budget.js
CHANGED
|
@@ -106,7 +106,8 @@ export class TurnBudget {
|
|
|
106
106
|
this.chunkEnded = undefined;
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
/** `Promise.withResolvers` for the runtime the project targets. */
|
|
110
|
+
export function withResolvers() {
|
|
110
111
|
let resolve;
|
|
111
112
|
const promise = new Promise((r) => { resolve = r; });
|
|
112
113
|
return { promise, resolve };
|