@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.
@@ -1,87 +1,3 @@
1
- /**
2
- * composition.ts — the ONE seam an embedder wires the fabric through.
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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAC;AAE7F;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,yBAAyB,GAAG,SAAS,CAAC,CAAC;AAE/E;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,OAAO,KACX,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C;AAID,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAGlE;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAGvD;AAED,wBAAgB,aAAa,IAAI,UAAU,GAAG,IAAI,CAEjD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,yBAAyB,GAAG,IAAI,CAO3F;AAED,8EAA8E;AAC9E,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,CAExD;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,mBAAmB,CASzD"}
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"}
@@ -1,76 +1,2 @@
1
- /**
2
- * composition.ts — the ONE seam an embedder wires the fabric through.
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';
@@ -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. Best-effort: a launch that cannot be journalled still
140
- * runs, and a reset then costs exactly what it cost before the journal.
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: the journal only changes when a launch of THIS
171
- * instance starts or settles, and those are rows this instance wrote.
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;IAW9C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAXvB;;;;OAIG;IACH,OAAO,CAAC,cAAc,CAAqB;IAC3C,gFAAgF;IAChF,OAAO,CAAC,SAAS,CAAS;gBAGP,OAAO,EAAE,iBAAiB,EAC1B,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC;IAG1C;;;;;;;;;;;;;;OAcG;IACG,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvC,8DAA8D;IAC9D,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzC;;;;;;;;;;;OAWG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IA8CzC;;;;;OAKG;YACW,SAAS;CAQxB"}
1
+ {"version":3,"file":"fenced-work.d.ts","sourceRoot":"","sources":["../src/fenced-work.ts"],"names":[],"mappings":"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"}
@@ -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. Best-effort: a launch that cannot be journalled still
68
- * runs, and a reset then costs exactly what it cost before the journal.
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 (e) {
87
- console.warn('[nimbus] resident launch journal write failed:', errorMessage(e));
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
- try {
103
- await this.storage.delete(`${FENCED_WORK_KEY_PREFIX}${pid}`);
104
- await this.storage.sync();
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
- catch (e) {
107
- console.warn('[nimbus] resident launch journal delete failed:', errorMessage(e));
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: the journal only changes when a launch of THIS
120
- * instance starts or settles, and those are rows this instance wrote.
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.host.waitUntil(this.host.redrive(record, record.attempt + 1)
164
- .catch((e) => {
165
- this.host.onRedriveFailed?.(record, e);
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
- try {
178
- await this.storage.delete(key);
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
- }
@@ -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 facet boots from. */
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":"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"}
1
+ {"version":3,"file":"process-fabric.d.ts","sourceRoot":"","sources":["../src/process-fabric.ts"],"names":[],"mappings":"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"}
@@ -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;AAS7B,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;AAwRzF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAWhE"}
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"}
@@ -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 { slot, ...facet } = processes(this.ctx, this.env).spawn(this.disk, supervisor, params);
117
+ const { name, ...facet } = processes(this.ctx, this.env).spawn(this.disk, supervisor, params);
118
118
  return {
119
119
  ...facet,
120
- describe: () => `facet '${residentFacetName(slot)}' (pid ${params.pid})`
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();
@@ -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;AAQD,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"}
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"}
@@ -106,7 +106,8 @@ export class TurnBudget {
106
106
  this.chunkEnded = undefined;
107
107
  }
108
108
  }
109
- function withResolvers() {
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 };