better-effect 0.13.0 → 0.14.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 +337 -88
- package/dist/adapters/iti.d.mts +2 -3
- package/dist/adapters/iti.d.mts.map +1 -1
- package/dist/adapters/iti.mjs +2 -2
- package/dist/bun.d.mts +75 -23
- package/dist/bun.d.mts.map +1 -1
- package/dist/bun.mjs +170 -33
- package/dist/bun.mjs.map +1 -1
- package/dist/{context-BphfFUMO.mjs → context-DSGSqcJj.mjs} +12 -3
- package/dist/context-DSGSqcJj.mjs.map +1 -0
- package/dist/{current-request-DDKNyymv.mjs → current-request-Bc1YHL0w.mjs} +3 -2
- package/dist/{current-request-DDKNyymv.mjs.map → current-request-Bc1YHL0w.mjs.map} +1 -1
- package/dist/{current-request-CIU7AHhF.d.mts → current-request-Dm-QQ3Xr.d.mts} +4 -4
- package/dist/current-request-Dm-QQ3Xr.d.mts.map +1 -0
- package/dist/{effect-CPdV7Yv8.mjs → effect-W0IQXY8P.mjs} +21 -2
- package/dist/effect-W0IQXY8P.mjs.map +1 -0
- package/dist/{errors-BKcsQ0ip.d.mts → errors-CpHMzPiP.d.mts} +6 -2
- package/dist/errors-CpHMzPiP.d.mts.map +1 -0
- package/dist/hono.d.mts +63 -27
- package/dist/hono.d.mts.map +1 -1
- package/dist/hono.mjs +111 -45
- package/dist/hono.mjs.map +1 -1
- package/dist/{index-CCi07kUw.d.mts → index-3Bkl-sxS.d.mts} +7 -2
- package/dist/{index-CCi07kUw.d.mts.map → index-3Bkl-sxS.d.mts.map} +1 -1
- package/dist/{index-cXgyMa0v.d.mts → index-C2ed9Ilq.d.mts} +32 -7
- package/dist/index-C2ed9Ilq.d.mts.map +1 -0
- package/dist/{index-BodMy6rO.d.mts → index-ClNndn2-.d.mts} +93 -63
- package/dist/index-ClNndn2-.d.mts.map +1 -0
- package/dist/{index-DAKn0qHk.d.mts → index-Cy1VsU-u.d.mts} +2 -4
- package/dist/index-Cy1VsU-u.d.mts.map +1 -0
- package/dist/index.d.mts +6 -8
- package/dist/index.mjs +8 -9
- package/dist/index.mjs.map +1 -1
- package/dist/{internal-identity-CbXoZIfw.mjs → internal-identity-BuHBtP0k.mjs} +2 -2
- package/dist/{internal-identity-CbXoZIfw.mjs.map → internal-identity-BuHBtP0k.mjs.map} +1 -1
- package/dist/layer-2kjHVv4N.mjs +343 -0
- package/dist/layer-2kjHVv4N.mjs.map +1 -0
- package/dist/layer-C2OaqTmB.d.mts +987 -0
- package/dist/layer-C2OaqTmB.d.mts.map +1 -0
- package/dist/next.d.mts +102 -26
- package/dist/next.d.mts.map +1 -1
- package/dist/next.mjs +157 -40
- package/dist/next.mjs.map +1 -1
- package/dist/{node-context-DczjAcrX.d.mts → node-context-1YEjrU9g.d.mts} +2 -2
- package/dist/{node-context-DczjAcrX.d.mts.map → node-context-1YEjrU9g.d.mts.map} +1 -1
- package/dist/{node-context-Ca18oNSg.mjs → node-context-g3bCcY9t.mjs} +2 -2
- package/dist/{node-context-Ca18oNSg.mjs.map → node-context-g3bCcY9t.mjs.map} +1 -1
- package/dist/node.d.mts +11 -6
- package/dist/node.d.mts.map +1 -1
- package/dist/node.mjs +156 -10
- package/dist/node.mjs.map +1 -1
- package/dist/opentelemetry.d.mts +20 -2
- package/dist/opentelemetry.d.mts.map +1 -1
- package/dist/opentelemetry.mjs +423 -5
- package/dist/opentelemetry.mjs.map +1 -1
- package/dist/{registration-drnZk0SY.mjs → registration-Bwe2GbVu.mjs} +8 -2
- package/dist/registration-Bwe2GbVu.mjs.map +1 -0
- package/dist/runtime/explicit.d.mts +2 -2
- package/dist/runtime/explicit.mjs +1 -1
- package/dist/runtime/node.d.mts +3 -3
- package/dist/runtime/node.mjs +2 -2
- package/dist/runtime-qpcVwaz_.mjs +1680 -0
- package/dist/runtime-qpcVwaz_.mjs.map +1 -0
- package/dist/service-CO9VJQYa.mjs +74 -0
- package/dist/service-CO9VJQYa.mjs.map +1 -0
- package/dist/{signal-nfgFyNCk.mjs → signal-5Rg9bUUe.mjs} +2 -2
- package/dist/{signal-nfgFyNCk.mjs.map → signal-5Rg9bUUe.mjs.map} +1 -1
- package/dist/{standard-services-vnGIafkS.mjs → standard-services-B-iMM_W7.mjs} +38 -4
- package/dist/standard-services-B-iMM_W7.mjs.map +1 -0
- package/dist/standard-services.d.mts +4 -4
- package/dist/standard-services.mjs +3 -3
- package/dist/testing.d.mts +25 -7
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.mjs +51 -7
- package/dist/testing.mjs.map +1 -1
- package/dist/{types-oihI-ESv.d.mts → types-BFTqCCcw.d.mts} +32 -5
- package/dist/types-BFTqCCcw.d.mts.map +1 -0
- package/dist/web-effect-B9hp9jIe.mjs +380 -0
- package/dist/web-effect-B9hp9jIe.mjs.map +1 -0
- package/dist/web.d.mts +22 -13
- package/dist/web.d.mts.map +1 -1
- package/dist/web.mjs +1 -1
- package/package.json +10 -16
- package/dist/context-BphfFUMO.mjs.map +0 -1
- package/dist/current-request-CIU7AHhF.d.mts.map +0 -1
- package/dist/disposable-CP-Q2H3l.mjs +0 -57
- package/dist/disposable-CP-Q2H3l.mjs.map +0 -1
- package/dist/effect-CPdV7Yv8.mjs.map +0 -1
- package/dist/errors-BKcsQ0ip.d.mts.map +0 -1
- package/dist/index-AiKM71-u.d.mts +0 -376
- package/dist/index-AiKM71-u.d.mts.map +0 -1
- package/dist/index-BodMy6rO.d.mts.map +0 -1
- package/dist/index-DAKn0qHk.d.mts.map +0 -1
- package/dist/index-cXgyMa0v.d.mts.map +0 -1
- package/dist/layer-D16GKxuX.mjs +0 -269
- package/dist/layer-D16GKxuX.mjs.map +0 -1
- package/dist/observer-9nhbL7Pr.d.mts +0 -68
- package/dist/observer-9nhbL7Pr.d.mts.map +0 -1
- package/dist/outcome-DI8LPIQ4.d.mts +0 -387
- package/dist/outcome-DI8LPIQ4.d.mts.map +0 -1
- package/dist/program-metadata-BxjGkT17.mjs +0 -260
- package/dist/program-metadata-BxjGkT17.mjs.map +0 -1
- package/dist/registration-drnZk0SY.mjs.map +0 -1
- package/dist/runtime-C_55ofnL.mjs +0 -791
- package/dist/runtime-C_55ofnL.mjs.map +0 -1
- package/dist/standard-services-vnGIafkS.mjs.map +0 -1
- package/dist/types-oihI-ESv.d.mts.map +0 -1
- package/dist/web-Bq3MfMrj.mjs +0 -147
- package/dist/web-Bq3MfMrj.mjs.map +0 -1
|
@@ -0,0 +1,1680 @@
|
|
|
1
|
+
import { c as CircularDependencyError, f as captureServiceTag, i as LayerDisposeError, l as ServiceAcquisitionError, n as normalizeLayerRegistration, o as LayerRegistrationError, r as DuplicateServiceError, s as ServiceTagCollisionError, t as captureLayerRegistrationTag, u as ServiceNotFoundError } from "./registration-Bwe2GbVu.mjs";
|
|
2
|
+
import { _ as RuntimeExecutorNotConfiguredError, h as RuntimeContextNotConfiguredError, i as getRuntimeContext, l as makeRuntimeContext, n as currentRuntimeContext, t as activeRuntimeContextStorage, u as runRuntimeContext } from "./context-DSGSqcJj.mjs";
|
|
3
|
+
import { a as ResourceNotDisposableError, c as ScopeRuntimeNotConfiguredError, i as getDisposeFinalizer, n as getLayerEntries, o as ScopeCloseError, s as ScopeClosedError, u as defaultRuntimeContextStorage } from "./layer-2kjHVv4N.mjs";
|
|
4
|
+
import { t as assertServiceCompatibility } from "./internal-identity-BuHBtP0k.mjs";
|
|
5
|
+
import { n as linkAbortSignals } from "./signal-5Rg9bUUe.mjs";
|
|
6
|
+
import { Err } from "better-result";
|
|
7
|
+
//#region src/scope/runtime.ts
|
|
8
|
+
const scopeStorages = /* @__PURE__ */ new WeakMap();
|
|
9
|
+
/** Bridges the current Scope through async execution context. */
|
|
10
|
+
var ScopeRuntime = class {
|
|
11
|
+
/** Supply a Scope while invoking a callback. */
|
|
12
|
+
static run(scope, program, storage = scopeStorages.get(scope) ?? activeRuntimeContextStorage()) {
|
|
13
|
+
scopeStorages.set(scope, storage);
|
|
14
|
+
const current = getRuntimeContext(storage);
|
|
15
|
+
const context = makeRuntimeContext(current?.resolver, scope, current?.resolutionPath ?? [], current?.signal, current);
|
|
16
|
+
return runRuntimeContext(storage, context, program);
|
|
17
|
+
}
|
|
18
|
+
/** Return the Scope active in the current execution context. */
|
|
19
|
+
static current() {
|
|
20
|
+
let context;
|
|
21
|
+
try {
|
|
22
|
+
context = currentRuntimeContext();
|
|
23
|
+
} catch (cause) {
|
|
24
|
+
if (cause instanceof RuntimeContextNotConfiguredError) throw new ScopeRuntimeNotConfiguredError();
|
|
25
|
+
throw cause;
|
|
26
|
+
}
|
|
27
|
+
if (!context.scope) throw new ScopeRuntimeNotConfiguredError();
|
|
28
|
+
return context.scope;
|
|
29
|
+
}
|
|
30
|
+
/** Associate a Runtime-owned Scope with its context storage. */
|
|
31
|
+
static bind(scope, storage) {
|
|
32
|
+
scopeStorages.set(scope, storage);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/scope/internal.ts
|
|
37
|
+
const scopeImplementations = /* @__PURE__ */ new WeakMap();
|
|
38
|
+
/** Register the private implementation hook used by lifecycle primitives. */
|
|
39
|
+
const registerScopeImplementation = (scope, implementation) => {
|
|
40
|
+
scopeImplementations.set(scope, implementation);
|
|
41
|
+
};
|
|
42
|
+
/** Register work that must run before attached child Scopes close. */
|
|
43
|
+
const addScopePreFinalizer = (scope, finalizer) => {
|
|
44
|
+
const implementation = scopeImplementations.get(scope);
|
|
45
|
+
if (!implementation) throw new TypeError("Scope does not support internal pre-finalizers");
|
|
46
|
+
implementation.addPreFinalizer(finalizer);
|
|
47
|
+
};
|
|
48
|
+
const notifyCleanupFailure = async (observer, diagnostic) => {
|
|
49
|
+
if (!observer) return;
|
|
50
|
+
try {
|
|
51
|
+
await observer(diagnostic);
|
|
52
|
+
} catch {}
|
|
53
|
+
};
|
|
54
|
+
const runScoped = async (scope, program, options) => {
|
|
55
|
+
let value;
|
|
56
|
+
let programFailed = false;
|
|
57
|
+
let programFailure;
|
|
58
|
+
try {
|
|
59
|
+
const run = () => ScopeRuntime.run(scope, program, options.contextStorage);
|
|
60
|
+
value = await (options.context && options.contextStorage ? runRuntimeContext(options.contextStorage, options.context, run) : run());
|
|
61
|
+
} catch (cause) {
|
|
62
|
+
programFailed = true;
|
|
63
|
+
programFailure = cause;
|
|
64
|
+
}
|
|
65
|
+
let outcome;
|
|
66
|
+
let outcomeStatus;
|
|
67
|
+
if (programFailed) {
|
|
68
|
+
outcome = {
|
|
69
|
+
status: "failure",
|
|
70
|
+
cause: programFailure
|
|
71
|
+
};
|
|
72
|
+
outcomeStatus = "failure";
|
|
73
|
+
} else try {
|
|
74
|
+
outcome = options.classify(value);
|
|
75
|
+
outcomeStatus = outcome.status;
|
|
76
|
+
} catch (cause) {
|
|
77
|
+
programFailed = true;
|
|
78
|
+
programFailure = cause;
|
|
79
|
+
outcome = {
|
|
80
|
+
status: "failure",
|
|
81
|
+
cause
|
|
82
|
+
};
|
|
83
|
+
outcomeStatus = "failure";
|
|
84
|
+
}
|
|
85
|
+
options.onOutcome?.(outcome);
|
|
86
|
+
let cleanupFailed = false;
|
|
87
|
+
let cleanupFailure;
|
|
88
|
+
try {
|
|
89
|
+
await scope.close(outcome);
|
|
90
|
+
} catch (cause) {
|
|
91
|
+
cleanupFailed = true;
|
|
92
|
+
cleanupFailure = cause;
|
|
93
|
+
}
|
|
94
|
+
if (cleanupFailed) {
|
|
95
|
+
const error = cleanupFailure instanceof ScopeCloseError ? cleanupFailure : new ScopeCloseError([cleanupFailure]);
|
|
96
|
+
await notifyCleanupFailure(options.onCleanupFailure, {
|
|
97
|
+
outcome,
|
|
98
|
+
error
|
|
99
|
+
});
|
|
100
|
+
cleanupFailure = error;
|
|
101
|
+
}
|
|
102
|
+
if (programFailed) throw programFailure;
|
|
103
|
+
if (outcomeStatus === "failure") return value;
|
|
104
|
+
if (cleanupFailed) throw cleanupFailure;
|
|
105
|
+
return value;
|
|
106
|
+
};
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/scope/scope.ts
|
|
109
|
+
const SCOPE_SUCCESS$1 = Object.freeze({ status: "success" });
|
|
110
|
+
var ScopeImpl = class ScopeImpl {
|
|
111
|
+
parent;
|
|
112
|
+
children = /* @__PURE__ */ new Set();
|
|
113
|
+
finalizers = [];
|
|
114
|
+
preFinalizers = [];
|
|
115
|
+
closePromise;
|
|
116
|
+
closeOutcome;
|
|
117
|
+
constructor(parent) {
|
|
118
|
+
this.parent = parent;
|
|
119
|
+
registerScopeImplementation(this, { addPreFinalizer: (finalizer) => this.addPreFinalizer(finalizer) });
|
|
120
|
+
}
|
|
121
|
+
fork() {
|
|
122
|
+
this.assertOpen();
|
|
123
|
+
const child = new ScopeImpl(this);
|
|
124
|
+
this.children.add(child);
|
|
125
|
+
return child;
|
|
126
|
+
}
|
|
127
|
+
addFinalizer(finalizer) {
|
|
128
|
+
this.assertOpen();
|
|
129
|
+
this.finalizers.push(finalizer);
|
|
130
|
+
}
|
|
131
|
+
async acquire(acquire, release) {
|
|
132
|
+
this.assertOpen();
|
|
133
|
+
const resource = await acquire();
|
|
134
|
+
try {
|
|
135
|
+
this.addFinalizer((outcome) => release(resource, outcome));
|
|
136
|
+
return resource;
|
|
137
|
+
} catch (scopeFailure) {
|
|
138
|
+
try {
|
|
139
|
+
await release(resource, this.closeOutcome ?? SCOPE_SUCCESS$1);
|
|
140
|
+
} catch (releaseFailure) {
|
|
141
|
+
throw new AggregateError([scopeFailure, releaseFailure], "Scope closed while acquiring a resource and immediate cleanup also failed");
|
|
142
|
+
}
|
|
143
|
+
throw scopeFailure;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async add(resource) {
|
|
147
|
+
const finalizer = getDisposeFinalizer(resource);
|
|
148
|
+
if (!finalizer) throw new ResourceNotDisposableError();
|
|
149
|
+
try {
|
|
150
|
+
this.addFinalizer(finalizer);
|
|
151
|
+
return resource;
|
|
152
|
+
} catch (scopeFailure) {
|
|
153
|
+
try {
|
|
154
|
+
await finalizer(this.closeOutcome ?? SCOPE_SUCCESS$1);
|
|
155
|
+
} catch (releaseFailure) {
|
|
156
|
+
throw new AggregateError([scopeFailure, releaseFailure], "Scope closed while adding a disposable resource and cleanup also failed");
|
|
157
|
+
}
|
|
158
|
+
throw scopeFailure;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
close(outcome = SCOPE_SUCCESS$1) {
|
|
162
|
+
if (this.closePromise) return this.closePromise;
|
|
163
|
+
this.closeOutcome = outcome;
|
|
164
|
+
this.closePromise = Promise.resolve().then(() => this.closeWithRuntime(outcome));
|
|
165
|
+
return this.closePromise;
|
|
166
|
+
}
|
|
167
|
+
async closeWithRuntime(outcome) {
|
|
168
|
+
let started = false;
|
|
169
|
+
try {
|
|
170
|
+
await ScopeRuntime.run(this, () => {
|
|
171
|
+
started = true;
|
|
172
|
+
return this.closeInternal(outcome);
|
|
173
|
+
});
|
|
174
|
+
} catch (storageFailure) {
|
|
175
|
+
if (started) throw storageFailure;
|
|
176
|
+
try {
|
|
177
|
+
await this.closeInternal(outcome);
|
|
178
|
+
} catch (cleanupFailure) {
|
|
179
|
+
const cleanupCauses = cleanupFailure instanceof ScopeCloseError ? cleanupFailure.causes : [cleanupFailure];
|
|
180
|
+
throw new ScopeCloseError([storageFailure, ...cleanupCauses]);
|
|
181
|
+
}
|
|
182
|
+
throw storageFailure;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async closeInternal(outcome) {
|
|
186
|
+
const failures = [];
|
|
187
|
+
const preFinalizers = this.preFinalizers.splice(0);
|
|
188
|
+
for (let index = preFinalizers.length - 1; index >= 0; index--) {
|
|
189
|
+
const finalizer = preFinalizers[index];
|
|
190
|
+
if (!finalizer) continue;
|
|
191
|
+
try {
|
|
192
|
+
await finalizer(outcome);
|
|
193
|
+
} catch (cause) {
|
|
194
|
+
failures.push(cause);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const children = [...this.children];
|
|
198
|
+
this.children.clear();
|
|
199
|
+
for (let index = children.length - 1; index >= 0; index--) {
|
|
200
|
+
const child = children[index];
|
|
201
|
+
if (!child) continue;
|
|
202
|
+
try {
|
|
203
|
+
await child.close(outcome);
|
|
204
|
+
} catch (cause) {
|
|
205
|
+
if (cause instanceof ScopeCloseError) failures.push(...cause.causes);
|
|
206
|
+
else failures.push(cause);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (let index = this.finalizers.length - 1; index >= 0; index--) {
|
|
210
|
+
const finalizer = this.finalizers[index];
|
|
211
|
+
if (!finalizer) continue;
|
|
212
|
+
try {
|
|
213
|
+
await finalizer(outcome);
|
|
214
|
+
} catch (cause) {
|
|
215
|
+
failures.push(cause);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
this.finalizers.length = 0;
|
|
219
|
+
this.detach();
|
|
220
|
+
if (failures.length > 0) throw new ScopeCloseError(failures);
|
|
221
|
+
}
|
|
222
|
+
detach() {
|
|
223
|
+
const parent = this.parent;
|
|
224
|
+
if (!parent) return;
|
|
225
|
+
parent.children.delete(this);
|
|
226
|
+
this.parent = void 0;
|
|
227
|
+
}
|
|
228
|
+
assertOpen() {
|
|
229
|
+
if (this.closePromise) throw new ScopeClosedError();
|
|
230
|
+
}
|
|
231
|
+
addPreFinalizer(finalizer) {
|
|
232
|
+
this.assertOpen();
|
|
233
|
+
this.preFinalizers.push(finalizer);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const Scope = {
|
|
237
|
+
/** Create an owned, initially open Scope. */
|
|
238
|
+
make() {
|
|
239
|
+
return new ScopeImpl();
|
|
240
|
+
},
|
|
241
|
+
/** Return the non-owning Scope available in the current execution context. */
|
|
242
|
+
current() {
|
|
243
|
+
return ScopeRuntime.current();
|
|
244
|
+
},
|
|
245
|
+
/** Run a callback with an existing Scope supplied as the current context. */
|
|
246
|
+
provide(scope, program) {
|
|
247
|
+
return ScopeRuntime.run(scope, program);
|
|
248
|
+
},
|
|
249
|
+
/** Resolve the current Scope through `yield* Scope` inside an Effect. */
|
|
250
|
+
*[Symbol.iterator]() {
|
|
251
|
+
return ScopeRuntime.current();
|
|
252
|
+
},
|
|
253
|
+
/**
|
|
254
|
+
* Run a program in a newly owned Scope.
|
|
255
|
+
*
|
|
256
|
+
* Scope is independent from `better-result`, so returned values—including
|
|
257
|
+
* `Result.err`—close this Scope with a successful outcome. Result-aware
|
|
258
|
+
* outcome classification belongs to `Runtime.run`.
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```ts
|
|
262
|
+
* await Scope.run(async (scope) => {
|
|
263
|
+
* const connection = await scope.acquire(connect, (connection) => connection.close())
|
|
264
|
+
* return connection.query()
|
|
265
|
+
* })
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
run(program) {
|
|
269
|
+
const scope = new ScopeImpl();
|
|
270
|
+
return runScoped(scope, () => program(scope), { classify: () => SCOPE_SUCCESS$1 });
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/layer/map-layer-backend.ts
|
|
275
|
+
/** Native map-backed Layer backend used by Runtime when no adapter is supplied. */
|
|
276
|
+
var MapLayerBackend = class {
|
|
277
|
+
providers = /* @__PURE__ */ new Map();
|
|
278
|
+
instances = /* @__PURE__ */ new Map();
|
|
279
|
+
pending = /* @__PURE__ */ new Map();
|
|
280
|
+
/** Register a provider, rejecting duplicate or colliding Service tags. */
|
|
281
|
+
register(registration) {
|
|
282
|
+
const normalized = normalizeLayerRegistration(registration);
|
|
283
|
+
const tag = captureLayerRegistrationTag(normalized);
|
|
284
|
+
const existing = this.providers.get(tag);
|
|
285
|
+
if (existing) {
|
|
286
|
+
if (existing.service !== normalized.service) throw new ServiceTagCollisionError(existing.service, normalized.service);
|
|
287
|
+
throw new DuplicateServiceError(normalized.service);
|
|
288
|
+
}
|
|
289
|
+
this.providers.set(tag, normalized);
|
|
290
|
+
}
|
|
291
|
+
/** Resolve and cache a provider instance by Service tag. */
|
|
292
|
+
async resolve(token) {
|
|
293
|
+
const tag = captureServiceTag(token);
|
|
294
|
+
const provider = this.providers.get(tag);
|
|
295
|
+
if (!provider) throw new ServiceNotFoundError(token);
|
|
296
|
+
const validate = (instance) => {
|
|
297
|
+
assertServiceCompatibility(token, provider.service, instance);
|
|
298
|
+
return instance;
|
|
299
|
+
};
|
|
300
|
+
const cached = this.instances.get(tag);
|
|
301
|
+
if (cached !== void 0) return validate(cached);
|
|
302
|
+
const pending = this.pending.get(tag);
|
|
303
|
+
if (pending) return validate(await pending);
|
|
304
|
+
const acquisition = Promise.resolve().then(() => provider.acquire()).then((instance) => {
|
|
305
|
+
validate(instance);
|
|
306
|
+
this.instances.set(tag, instance);
|
|
307
|
+
return instance;
|
|
308
|
+
}).finally(() => {
|
|
309
|
+
this.pending.delete(tag);
|
|
310
|
+
});
|
|
311
|
+
this.pending.set(tag, acquisition);
|
|
312
|
+
return validate(await acquisition);
|
|
313
|
+
}
|
|
314
|
+
/** Clear pending acquisitions, cached instances, and provider registrations. */
|
|
315
|
+
async disposeAll(options) {
|
|
316
|
+
const acquisitions = [...this.pending.values()];
|
|
317
|
+
if (acquisitions.length > 0) {
|
|
318
|
+
const observePending = options?.onPendingAcquisitions;
|
|
319
|
+
if (observePending) await observePending(acquisitions);
|
|
320
|
+
await Promise.allSettled(acquisitions);
|
|
321
|
+
}
|
|
322
|
+
this.instances.clear();
|
|
323
|
+
this.pending.clear();
|
|
324
|
+
this.providers.clear();
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/effect/program-metadata.ts
|
|
329
|
+
const programNames = /* @__PURE__ */ new WeakMap();
|
|
330
|
+
/** Return the private diagnostic name attached to a callable Program. */
|
|
331
|
+
const getProgramName = (program) => programNames.get(program);
|
|
332
|
+
/** Attach a name without changing the callable Program facade. */
|
|
333
|
+
const setProgramName = (program, name) => {
|
|
334
|
+
programNames.set(program, name);
|
|
335
|
+
return program;
|
|
336
|
+
};
|
|
337
|
+
/** Copy a source Program's name to a lazily-created derived Program. */
|
|
338
|
+
const inheritProgramName = (source, derived) => {
|
|
339
|
+
const name = getProgramName(source);
|
|
340
|
+
return name === void 0 ? derived : setProgramName(derived, name);
|
|
341
|
+
};
|
|
342
|
+
//#endregion
|
|
343
|
+
//#region src/runtime/observer.ts
|
|
344
|
+
/** Compose best-effort Runtime observers into one observer. */
|
|
345
|
+
const RuntimeObserver = { compose: (...observers) => ({
|
|
346
|
+
onServiceResolve: (event) => {
|
|
347
|
+
notifyRuntimeObservers(observers, (observer) => observer.onServiceResolve, event);
|
|
348
|
+
},
|
|
349
|
+
onServiceAcquire: (event) => {
|
|
350
|
+
notifyRuntimeObservers(observers, (observer) => observer.onServiceAcquire, event);
|
|
351
|
+
},
|
|
352
|
+
onExecutionStart: (event) => {
|
|
353
|
+
notifyRuntimeObservers(observers, (observer) => observer.onExecutionStart, event);
|
|
354
|
+
},
|
|
355
|
+
onExecutionEnd: (event) => {
|
|
356
|
+
notifyRuntimeObservers(observers, (observer) => observer.onExecutionEnd, event);
|
|
357
|
+
},
|
|
358
|
+
onTaskStart: (event) => {
|
|
359
|
+
notifyRuntimeObservers(observers, (observer) => observer.onTaskStart, event);
|
|
360
|
+
},
|
|
361
|
+
onTaskEnd: (event) => {
|
|
362
|
+
notifyRuntimeObservers(observers, (observer) => observer.onTaskEnd, event);
|
|
363
|
+
},
|
|
364
|
+
onResourceRelease: (event) => {
|
|
365
|
+
notifyRuntimeObservers(observers, (observer) => observer.onResourceRelease, event);
|
|
366
|
+
},
|
|
367
|
+
onLifecycleStart: (event) => {
|
|
368
|
+
notifyRuntimeObservers(observers, (observer) => observer.onLifecycleStart, event);
|
|
369
|
+
},
|
|
370
|
+
onLifecycleEnd: (event) => {
|
|
371
|
+
notifyRuntimeObservers(observers, (observer) => observer.onLifecycleEnd, event);
|
|
372
|
+
},
|
|
373
|
+
onLifecycleRelease: (event) => {
|
|
374
|
+
notifyRuntimeObservers(observers, (observer) => observer.onLifecycleRelease, event);
|
|
375
|
+
},
|
|
376
|
+
onShutdown: (event) => {
|
|
377
|
+
notifyRuntimeObservers(observers, (observer) => observer.onShutdown, event);
|
|
378
|
+
}
|
|
379
|
+
}) };
|
|
380
|
+
const notifyRuntimeObservers = (observers, select, event) => {
|
|
381
|
+
for (const observer of observers) {
|
|
382
|
+
const callback = select(observer);
|
|
383
|
+
if (!callback) continue;
|
|
384
|
+
try {
|
|
385
|
+
Promise.resolve(callback(event)).catch(() => {});
|
|
386
|
+
} catch {}
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/runtime/outcome.ts
|
|
391
|
+
/** Classify only a nominal better-result Err as a failed Runtime outcome. */
|
|
392
|
+
const classifyRuntimeOutcome = (value) => {
|
|
393
|
+
if (value instanceof Err) return {
|
|
394
|
+
status: "failure",
|
|
395
|
+
cause: value.error
|
|
396
|
+
};
|
|
397
|
+
return { status: "success" };
|
|
398
|
+
};
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/runtime/task.ts
|
|
401
|
+
const taskSupervisors = /* @__PURE__ */ new WeakMap();
|
|
402
|
+
/** Associate one Runtime task supervisor with a Runtime-owned Scope. */
|
|
403
|
+
const bindRuntimeTaskSupervisor = (scope, supervisor) => {
|
|
404
|
+
taskSupervisors.set(scope, supervisor);
|
|
405
|
+
};
|
|
406
|
+
/** Return the supervisor associated with the current Runtime-owned Scope. */
|
|
407
|
+
const getRuntimeTaskSupervisor = (scope) => taskSupervisors.get(scope);
|
|
408
|
+
const defaultInterruptionReason = () => {
|
|
409
|
+
const reason = /* @__PURE__ */ new Error("Scoped task interrupted");
|
|
410
|
+
reason.name = "ScopedTaskInterruptedError";
|
|
411
|
+
return reason;
|
|
412
|
+
};
|
|
413
|
+
const signalReason = (signal) => signal.reason === void 0 ? defaultInterruptionReason() : signal.reason;
|
|
414
|
+
const taskStateFromExit = (exit) => exit.status;
|
|
415
|
+
const copyMetadata = (metadata) => {
|
|
416
|
+
const copy = {
|
|
417
|
+
taskId: metadata.taskId,
|
|
418
|
+
startedAt: metadata.startedAt
|
|
419
|
+
};
|
|
420
|
+
if (metadata.parentExecutionId !== void 0) copy.parentExecutionId = metadata.parentExecutionId;
|
|
421
|
+
if (metadata.name !== void 0) copy.name = metadata.name;
|
|
422
|
+
return Object.freeze(copy);
|
|
423
|
+
};
|
|
424
|
+
var ScopedTaskHandle = class {
|
|
425
|
+
controller;
|
|
426
|
+
handleState;
|
|
427
|
+
constructor(controller) {
|
|
428
|
+
this.controller = controller;
|
|
429
|
+
let resolveCompletion;
|
|
430
|
+
const completion = new Promise((resolve) => {
|
|
431
|
+
resolveCompletion = resolve;
|
|
432
|
+
});
|
|
433
|
+
this.handleState = {
|
|
434
|
+
completion,
|
|
435
|
+
resolveCompletion,
|
|
436
|
+
currentState: "running",
|
|
437
|
+
terminalResult: void 0,
|
|
438
|
+
interruptionReasonValue: void 0
|
|
439
|
+
};
|
|
440
|
+
Object.freeze(this);
|
|
441
|
+
}
|
|
442
|
+
get state() {
|
|
443
|
+
return this.handleState.currentState;
|
|
444
|
+
}
|
|
445
|
+
await() {
|
|
446
|
+
return this.handleState.completion.then((exit) => {
|
|
447
|
+
if (exit.status === "succeeded" || exit.status === "failed") return this.handleState.terminalResult;
|
|
448
|
+
if (exit.status === "defected") throw exit.cause;
|
|
449
|
+
throw exit.reason;
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
awaitExit() {
|
|
453
|
+
return this.handleState.completion;
|
|
454
|
+
}
|
|
455
|
+
interrupt(reason) {
|
|
456
|
+
if (this.handleState.currentState !== "running") return this.handleState.completion.then(() => void 0);
|
|
457
|
+
const interruption = reason === void 0 ? this.handleState.interruptionReasonValue ?? defaultInterruptionReason() : reason;
|
|
458
|
+
this.handleState.interruptionReasonValue = interruption;
|
|
459
|
+
if (!this.controller.signal.aborted) this.controller.abort(interruption);
|
|
460
|
+
return this.handleState.completion.then(() => void 0);
|
|
461
|
+
}
|
|
462
|
+
markInterrupted(reason) {
|
|
463
|
+
if (this.handleState.currentState === "running" && this.handleState.interruptionReasonValue === void 0) this.handleState.interruptionReasonValue = reason;
|
|
464
|
+
}
|
|
465
|
+
isInterrupted() {
|
|
466
|
+
return this.handleState.interruptionReasonValue !== void 0 || this.controller.signal.aborted;
|
|
467
|
+
}
|
|
468
|
+
interruptionReason(signal) {
|
|
469
|
+
return this.handleState.interruptionReasonValue ?? signalReason(signal);
|
|
470
|
+
}
|
|
471
|
+
settle(exit, result) {
|
|
472
|
+
if (this.handleState.currentState !== "running") return;
|
|
473
|
+
const terminalExit = Object.freeze(exit);
|
|
474
|
+
this.handleState.currentState = taskStateFromExit(terminalExit);
|
|
475
|
+
this.handleState.terminalResult = result;
|
|
476
|
+
this.handleState.resolveCompletion(terminalExit);
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
const withCleanupFailure = (value, cleanupFailure) => {
|
|
480
|
+
if (cleanupFailure === void 0) return Object.freeze({ ...value });
|
|
481
|
+
return Object.freeze({
|
|
482
|
+
...value,
|
|
483
|
+
cleanupFailure
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
/** Runtime-owned supervisor for Scope child tasks. */
|
|
487
|
+
var RuntimeTaskSupervisor = class {
|
|
488
|
+
createTaskId;
|
|
489
|
+
now;
|
|
490
|
+
observers;
|
|
491
|
+
onCleanupFailure;
|
|
492
|
+
tasks = /* @__PURE__ */ new Map();
|
|
493
|
+
constructor(createTaskId, now, observers, onCleanupFailure) {
|
|
494
|
+
this.createTaskId = createTaskId;
|
|
495
|
+
this.now = now;
|
|
496
|
+
this.observers = observers;
|
|
497
|
+
this.onCleanupFailure = onCleanupFailure;
|
|
498
|
+
}
|
|
499
|
+
bindScope(scope) {
|
|
500
|
+
bindRuntimeTaskSupervisor(scope, this);
|
|
501
|
+
}
|
|
502
|
+
inspect() {
|
|
503
|
+
return Object.freeze([...this.tasks.values()].map(({ metadata }) => Object.freeze({
|
|
504
|
+
...copyMetadata(metadata),
|
|
505
|
+
state: "running"
|
|
506
|
+
})));
|
|
507
|
+
}
|
|
508
|
+
forkScoped(program, context) {
|
|
509
|
+
if (!context.resolver || !context.scope) throw new RuntimeContextNotConfiguredError();
|
|
510
|
+
const parentScope = context.scope;
|
|
511
|
+
const childScope = parentScope.fork();
|
|
512
|
+
const controller = new AbortController();
|
|
513
|
+
const signalLink = linkAbortSignals(context.signal, controller.signal);
|
|
514
|
+
const handle = new ScopedTaskHandle(controller);
|
|
515
|
+
const metadata = this.makeMetadata(program, context);
|
|
516
|
+
const record = {
|
|
517
|
+
metadata,
|
|
518
|
+
childScope,
|
|
519
|
+
signalLink,
|
|
520
|
+
handle,
|
|
521
|
+
program,
|
|
522
|
+
context,
|
|
523
|
+
storage: activeRuntimeContextStorage()
|
|
524
|
+
};
|
|
525
|
+
const onAbort = () => handle.markInterrupted(signalReason(signalLink.signal));
|
|
526
|
+
try {
|
|
527
|
+
addScopePreFinalizer(parentScope, (outcome) => handle.interrupt(outcome.status === "failure" ? outcome.cause : void 0));
|
|
528
|
+
this.bindScope(childScope);
|
|
529
|
+
this.tasks.set(metadata.taskId, record);
|
|
530
|
+
signalLink.signal.addEventListener("abort", onAbort, { once: true });
|
|
531
|
+
if (signalLink.signal.aborted) onAbort();
|
|
532
|
+
this.notifyStart(metadata);
|
|
533
|
+
} catch (cause) {
|
|
534
|
+
signalLink.signal.removeEventListener("abort", onAbort);
|
|
535
|
+
signalLink.dispose();
|
|
536
|
+
childScope.close({
|
|
537
|
+
status: "failure",
|
|
538
|
+
cause
|
|
539
|
+
}).catch(() => {});
|
|
540
|
+
throw cause;
|
|
541
|
+
}
|
|
542
|
+
this.runTask(record, onAbort);
|
|
543
|
+
return handle;
|
|
544
|
+
}
|
|
545
|
+
makeMetadata(program, context) {
|
|
546
|
+
const metadata = {
|
|
547
|
+
taskId: this.createTaskId(),
|
|
548
|
+
startedAt: this.now()
|
|
549
|
+
};
|
|
550
|
+
const name = getProgramName(program);
|
|
551
|
+
if (name !== void 0) metadata.name = name;
|
|
552
|
+
if (context.executionId !== void 0) metadata.parentExecutionId = context.executionId;
|
|
553
|
+
return Object.freeze(metadata);
|
|
554
|
+
}
|
|
555
|
+
notifyStart(metadata) {
|
|
556
|
+
const event = {
|
|
557
|
+
...copyMetadata(metadata),
|
|
558
|
+
state: "running"
|
|
559
|
+
};
|
|
560
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onTaskStart, Object.freeze(event));
|
|
561
|
+
}
|
|
562
|
+
notifyEnd(metadata, exit) {
|
|
563
|
+
const event = {
|
|
564
|
+
...copyMetadata(metadata),
|
|
565
|
+
state: exit.status
|
|
566
|
+
};
|
|
567
|
+
if (exit.cleanupFailure !== void 0) event.cleanupFailure = exit.cleanupFailure;
|
|
568
|
+
const frozenEvent = Object.freeze(event);
|
|
569
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onTaskEnd, frozenEvent);
|
|
570
|
+
}
|
|
571
|
+
async runTask(record, onAbort) {
|
|
572
|
+
let cleanupFailure;
|
|
573
|
+
const onChildCleanupFailure = async (diagnostic) => {
|
|
574
|
+
cleanupFailure = diagnostic.error;
|
|
575
|
+
if (this.onCleanupFailure !== void 0) await this.onCleanupFailure(diagnostic);
|
|
576
|
+
};
|
|
577
|
+
const taskContext = makeRuntimeContext(record.context.resolver, record.childScope, [], record.signalLink.signal, record.context, record.context.executionId, record.context.executor);
|
|
578
|
+
const classify = (value) => record.handle.isInterrupted() ? {
|
|
579
|
+
status: "failure",
|
|
580
|
+
cause: record.handle.interruptionReason(record.signalLink.signal)
|
|
581
|
+
} : classifyRuntimeOutcome(value);
|
|
582
|
+
let exit;
|
|
583
|
+
let result;
|
|
584
|
+
try {
|
|
585
|
+
if (record.handle.isInterrupted()) {
|
|
586
|
+
await record.childScope.close({
|
|
587
|
+
status: "failure",
|
|
588
|
+
cause: record.handle.interruptionReason(record.signalLink.signal)
|
|
589
|
+
});
|
|
590
|
+
exit = withCleanupFailure({
|
|
591
|
+
status: "interrupted",
|
|
592
|
+
reason: record.handle.interruptionReason(record.signalLink.signal)
|
|
593
|
+
}, cleanupFailure);
|
|
594
|
+
} else {
|
|
595
|
+
const childResult = await runScoped(record.childScope, () => record.program(), {
|
|
596
|
+
classify,
|
|
597
|
+
onCleanupFailure: onChildCleanupFailure,
|
|
598
|
+
contextStorage: record.storage,
|
|
599
|
+
context: taskContext
|
|
600
|
+
});
|
|
601
|
+
result = childResult;
|
|
602
|
+
if (record.handle.isInterrupted()) exit = withCleanupFailure({
|
|
603
|
+
status: "interrupted",
|
|
604
|
+
reason: record.handle.interruptionReason(record.signalLink.signal)
|
|
605
|
+
}, cleanupFailure);
|
|
606
|
+
else if (childResult instanceof Err) exit = withCleanupFailure({
|
|
607
|
+
status: "failed",
|
|
608
|
+
error: childResult.error
|
|
609
|
+
}, cleanupFailure);
|
|
610
|
+
else exit = withCleanupFailure({
|
|
611
|
+
status: "succeeded",
|
|
612
|
+
value: childResult.value
|
|
613
|
+
}, cleanupFailure);
|
|
614
|
+
}
|
|
615
|
+
} catch (cause) {
|
|
616
|
+
exit = record.handle.isInterrupted() ? withCleanupFailure({
|
|
617
|
+
status: "interrupted",
|
|
618
|
+
reason: record.handle.interruptionReason(record.signalLink.signal)
|
|
619
|
+
}, cleanupFailure) : withCleanupFailure({
|
|
620
|
+
status: "defected",
|
|
621
|
+
cause
|
|
622
|
+
}, cleanupFailure);
|
|
623
|
+
}
|
|
624
|
+
record.signalLink.signal.removeEventListener("abort", onAbort);
|
|
625
|
+
record.signalLink.dispose();
|
|
626
|
+
record.handle.settle(exit, result);
|
|
627
|
+
this.tasks.delete(record.metadata.taskId);
|
|
628
|
+
this.notifyEnd(record.metadata, exit);
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
//#endregion
|
|
632
|
+
//#region src/runtime/executor.ts
|
|
633
|
+
const castRuntimeExecutor = (value) => {
|
|
634
|
+
return value;
|
|
635
|
+
};
|
|
636
|
+
const currentExecutor = () => {
|
|
637
|
+
let context;
|
|
638
|
+
try {
|
|
639
|
+
context = currentRuntimeContext();
|
|
640
|
+
} catch (cause) {
|
|
641
|
+
if (cause instanceof RuntimeContextNotConfiguredError) throw new RuntimeExecutorNotConfiguredError();
|
|
642
|
+
throw cause;
|
|
643
|
+
}
|
|
644
|
+
if (context.executor === void 0) throw new RuntimeExecutorNotConfiguredError();
|
|
645
|
+
return castRuntimeExecutor(context.executor);
|
|
646
|
+
};
|
|
647
|
+
/** Erase a Runtime executor's concrete environment for contextual storage. */
|
|
648
|
+
const eraseRuntimeExecutor = (executor) => castRuntimeExecutor(executor);
|
|
649
|
+
/** Create one stable executor view from an existing Runtime execution handle. */
|
|
650
|
+
const createRuntimeExecutor = (executor) => Object.freeze({ ...executor });
|
|
651
|
+
/** Build the yieldable contextual executor request used by `Runtime.executor()`. */
|
|
652
|
+
const makeRuntimeExecutorRequest = () => {
|
|
653
|
+
return {
|
|
654
|
+
*[Symbol.iterator]() {
|
|
655
|
+
return currentExecutor();
|
|
656
|
+
},
|
|
657
|
+
async *[Symbol.asyncIterator]() {
|
|
658
|
+
return currentExecutor();
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
};
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/layer/resolution.ts
|
|
664
|
+
const findCycleStart = (path, tag) => path.findIndex((current) => captureServiceTag(current) === tag);
|
|
665
|
+
const shouldPreserve = (cause) => cause instanceof CircularDependencyError || cause instanceof ServiceAcquisitionError || cause instanceof ServiceNotFoundError || cause instanceof ServiceTagCollisionError;
|
|
666
|
+
/** Wrap a backend with Runtime-local resolution paths and acquisition errors. */
|
|
667
|
+
const createResolutionResolver = (resolver, storage = defaultRuntimeContextStorage, observers = []) => {
|
|
668
|
+
const wrapped = { async resolve(token) {
|
|
669
|
+
const tag = captureServiceTag(token);
|
|
670
|
+
const context = getRuntimeContext(storage);
|
|
671
|
+
const path = context?.resolutionPath ?? [];
|
|
672
|
+
const cycleStart = findCycleStart(path, tag);
|
|
673
|
+
const resolutionPath = [...path, token];
|
|
674
|
+
const notifyResolve = (outcome) => {
|
|
675
|
+
const event = {
|
|
676
|
+
service: token,
|
|
677
|
+
resolutionPath,
|
|
678
|
+
outcome
|
|
679
|
+
};
|
|
680
|
+
if (context?.executionId !== void 0) Object.assign(event, { executionId: context.executionId });
|
|
681
|
+
notifyRuntimeObservers(observers, (observer) => observer.onServiceResolve, event);
|
|
682
|
+
};
|
|
683
|
+
if (cycleStart >= 0) {
|
|
684
|
+
const error = new CircularDependencyError([...path.slice(cycleStart), token]);
|
|
685
|
+
notifyResolve({
|
|
686
|
+
status: "failure",
|
|
687
|
+
cause: error
|
|
688
|
+
});
|
|
689
|
+
throw error;
|
|
690
|
+
}
|
|
691
|
+
const nextContext = makeRuntimeContext(wrapped, context?.scope, resolutionPath, context?.signal, context);
|
|
692
|
+
return await runRuntimeContext(storage, nextContext, async () => {
|
|
693
|
+
try {
|
|
694
|
+
const instance = await resolver.resolve(token);
|
|
695
|
+
notifyResolve({ status: "success" });
|
|
696
|
+
return instance;
|
|
697
|
+
} catch (cause) {
|
|
698
|
+
if (shouldPreserve(cause)) {
|
|
699
|
+
notifyResolve({
|
|
700
|
+
status: "failure",
|
|
701
|
+
cause
|
|
702
|
+
});
|
|
703
|
+
throw cause;
|
|
704
|
+
}
|
|
705
|
+
const error = new ServiceAcquisitionError(token, resolutionPath, cause);
|
|
706
|
+
notifyResolve({
|
|
707
|
+
status: "failure",
|
|
708
|
+
cause: error
|
|
709
|
+
});
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
} };
|
|
714
|
+
return wrapped;
|
|
715
|
+
};
|
|
716
|
+
//#endregion
|
|
717
|
+
//#region src/runtime/execution.ts
|
|
718
|
+
const hostMonotonicNow = () => {
|
|
719
|
+
if (typeof globalThis.performance?.now === "function") {
|
|
720
|
+
const value = globalThis.performance.now();
|
|
721
|
+
if (Number.isFinite(value)) return value;
|
|
722
|
+
}
|
|
723
|
+
const wallClock = Date.now();
|
|
724
|
+
return Number.isFinite(wallClock) ? wallClock : 0;
|
|
725
|
+
};
|
|
726
|
+
const makeMonotonicClock = () => {
|
|
727
|
+
let previous = 0;
|
|
728
|
+
return () => {
|
|
729
|
+
previous = Math.max(previous, hostMonotonicNow());
|
|
730
|
+
return previous;
|
|
731
|
+
};
|
|
732
|
+
};
|
|
733
|
+
let fallbackExecutionSequence = 0;
|
|
734
|
+
const makeExecutionIdGenerator = () => () => {
|
|
735
|
+
if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
|
|
736
|
+
fallbackExecutionSequence += 1;
|
|
737
|
+
return `execution-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}-${fallbackExecutionSequence.toString(36)}`;
|
|
738
|
+
};
|
|
739
|
+
/** Create isolated execution capabilities for one Runtime handle. */
|
|
740
|
+
const makeRuntimeExecutionDependencies = (overrides = {}) => ({
|
|
741
|
+
createExecutionId: overrides.createExecutionId ?? makeExecutionIdGenerator(),
|
|
742
|
+
now: overrides.now ?? makeMonotonicClock()
|
|
743
|
+
});
|
|
744
|
+
//#endregion
|
|
745
|
+
//#region src/layer/runtime.ts
|
|
746
|
+
const SCOPE_SUCCESS = Object.freeze({ status: "success" });
|
|
747
|
+
var RuntimeHandleDisposedError = class extends Error {
|
|
748
|
+
constructor() {
|
|
749
|
+
super("Cannot run a program using a disposed Layer");
|
|
750
|
+
this.name = "RuntimeHandleDisposedError";
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
const normalizeDisposeCauses = (cause) => {
|
|
754
|
+
if (cause instanceof AggregateError) return [...cause.errors];
|
|
755
|
+
return [cause];
|
|
756
|
+
};
|
|
757
|
+
const isScopeOutcome = (input) => input !== void 0 && "status" in input;
|
|
758
|
+
const validateDisposeOptions = (options) => {
|
|
759
|
+
const { gracePeriod, abortAfterGracePeriod } = options;
|
|
760
|
+
if (gracePeriod !== void 0 && (!Number.isFinite(gracePeriod) || gracePeriod < 0)) throw new RangeError("Runtime dispose gracePeriod must be a finite non-negative number");
|
|
761
|
+
if (abortAfterGracePeriod !== void 0 && typeof abortAfterGracePeriod !== "boolean") throw new TypeError("Runtime dispose abortAfterGracePeriod must be a boolean");
|
|
762
|
+
};
|
|
763
|
+
const copyExecutionAttributes = (attributes) => {
|
|
764
|
+
if (attributes === void 0) return;
|
|
765
|
+
if (typeof attributes !== "object" || attributes === null) throw new TypeError("Runtime execution attributes must be an object");
|
|
766
|
+
return Object.freeze({ ...attributes });
|
|
767
|
+
};
|
|
768
|
+
const prepareExecution = (program, options, dependencies, runtimeSignal, shutdownSignal, additionalSignal) => ({
|
|
769
|
+
metadata: makeExecutionMetadata(program, options?.attributes, dependencies),
|
|
770
|
+
signalLink: linkAbortSignals(runtimeSignal, options?.signal, additionalSignal, shutdownSignal)
|
|
771
|
+
});
|
|
772
|
+
const makeExecutionMetadata = (program, attributes, dependencies) => {
|
|
773
|
+
const name = getProgramName(program);
|
|
774
|
+
const copiedAttributes = copyExecutionAttributes(attributes);
|
|
775
|
+
const metadata = {
|
|
776
|
+
executionId: dependencies.createExecutionId(),
|
|
777
|
+
startedAt: dependencies.now()
|
|
778
|
+
};
|
|
779
|
+
if (name !== void 0) metadata.name = name;
|
|
780
|
+
if (copiedAttributes !== void 0) metadata.attributes = copiedAttributes;
|
|
781
|
+
return Object.freeze(metadata);
|
|
782
|
+
};
|
|
783
|
+
const executionDuration = (metadata, dependencies) => {
|
|
784
|
+
try {
|
|
785
|
+
const elapsed = dependencies.now() - metadata.startedAt;
|
|
786
|
+
return Number.isFinite(elapsed) ? Math.max(0, elapsed) : 0;
|
|
787
|
+
} catch {
|
|
788
|
+
return 0;
|
|
789
|
+
}
|
|
790
|
+
};
|
|
791
|
+
const readRuntimeTimestamp = (dependencies) => {
|
|
792
|
+
try {
|
|
793
|
+
const timestamp = dependencies.now();
|
|
794
|
+
return Number.isFinite(timestamp) ? timestamp : void 0;
|
|
795
|
+
} catch {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
const makeShutdownReason = (outcome) => {
|
|
800
|
+
if (outcome.status === "failure") return {
|
|
801
|
+
kind: "main-settled",
|
|
802
|
+
cause: outcome.cause
|
|
803
|
+
};
|
|
804
|
+
return { kind: "main-settled" };
|
|
805
|
+
};
|
|
806
|
+
const notifyShutdownFailure = async (observer, diagnostic) => {
|
|
807
|
+
if (!observer) return;
|
|
808
|
+
try {
|
|
809
|
+
await observer(diagnostic);
|
|
810
|
+
} catch {}
|
|
811
|
+
};
|
|
812
|
+
const releaseLayerResource = async (service, release, outcome, observers, executionId) => {
|
|
813
|
+
try {
|
|
814
|
+
await release(outcome);
|
|
815
|
+
const event = {
|
|
816
|
+
service,
|
|
817
|
+
outcome
|
|
818
|
+
};
|
|
819
|
+
if (executionId !== void 0) Object.assign(event, { executionId });
|
|
820
|
+
notifyRuntimeObservers(observers, (observer) => observer.onResourceRelease, event);
|
|
821
|
+
} catch (cause) {
|
|
822
|
+
const event = {
|
|
823
|
+
service,
|
|
824
|
+
outcome,
|
|
825
|
+
error: cause
|
|
826
|
+
};
|
|
827
|
+
if (executionId !== void 0) Object.assign(event, { executionId });
|
|
828
|
+
notifyRuntimeObservers(observers, (observer) => observer.onResourceRelease, event);
|
|
829
|
+
throw cause;
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
const lifecycleEventMetadata = (entry) => ({
|
|
833
|
+
kind: "lifecycle",
|
|
834
|
+
lifecycleId: entry.id
|
|
835
|
+
});
|
|
836
|
+
const releaseLifecycleResource = async (entry, resource, outcome, observers) => {
|
|
837
|
+
const metadata = lifecycleEventMetadata(entry);
|
|
838
|
+
try {
|
|
839
|
+
await entry.release(resource, outcome);
|
|
840
|
+
const event = {
|
|
841
|
+
...metadata,
|
|
842
|
+
outcome
|
|
843
|
+
};
|
|
844
|
+
notifyRuntimeObservers(observers, (observer) => observer.onLifecycleRelease, event);
|
|
845
|
+
} catch (cause) {
|
|
846
|
+
const event = {
|
|
847
|
+
...metadata,
|
|
848
|
+
outcome,
|
|
849
|
+
error: cause
|
|
850
|
+
};
|
|
851
|
+
notifyRuntimeObservers(observers, (observer) => observer.onLifecycleRelease, event);
|
|
852
|
+
throw cause;
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
const bindProviderToScope = (provider, scope, contextStorage, resolver, observers, executor, ownerExecutionId, beforeAcquire, onAcquired) => ({
|
|
856
|
+
service: provider.service,
|
|
857
|
+
serviceTag: provider.serviceTag,
|
|
858
|
+
acquire: () => {
|
|
859
|
+
beforeAcquire?.();
|
|
860
|
+
const current = getRuntimeContext(contextStorage);
|
|
861
|
+
const executionId = current?.executionId ?? ownerExecutionId;
|
|
862
|
+
const context = makeRuntimeContext(resolver, scope, current?.resolutionPath ?? [], current?.signal, current, void 0, executor);
|
|
863
|
+
return runRuntimeContext(contextStorage, context, () => ScopeRuntime.run(scope, async () => {
|
|
864
|
+
const resolutionPath = current?.resolutionPath ?? [provider.service];
|
|
865
|
+
try {
|
|
866
|
+
const instance = provider.acquireWithRelease ? (await scope.acquire(provider.acquireWithRelease, (acquired, outcome) => releaseLayerResource(provider.service, acquired.release, outcome, observers, ownerExecutionId))).instance : provider.release ? await scope.acquire(() => provider.acquire(), (resource, outcome) => releaseLayerResource(provider.service, (releaseOutcome) => provider.release(resource, releaseOutcome), outcome, observers, ownerExecutionId)) : await provider.acquire();
|
|
867
|
+
onAcquired?.(instance);
|
|
868
|
+
const event = {
|
|
869
|
+
service: provider.service,
|
|
870
|
+
resolutionPath,
|
|
871
|
+
outcome: SCOPE_SUCCESS
|
|
872
|
+
};
|
|
873
|
+
if (executionId !== void 0) Object.assign(event, { executionId });
|
|
874
|
+
notifyRuntimeObservers(observers, (observer) => observer.onServiceAcquire, event);
|
|
875
|
+
return instance;
|
|
876
|
+
} catch (cause) {
|
|
877
|
+
const event = {
|
|
878
|
+
service: provider.service,
|
|
879
|
+
resolutionPath,
|
|
880
|
+
outcome: {
|
|
881
|
+
status: "failure",
|
|
882
|
+
cause
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
if (executionId !== void 0) Object.assign(event, { executionId });
|
|
886
|
+
notifyRuntimeObservers(observers, (observer) => observer.onServiceAcquire, event);
|
|
887
|
+
throw cause;
|
|
888
|
+
}
|
|
889
|
+
}, contextStorage));
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
/** Resolve request-local providers first, then fall back to the Runtime root. */
|
|
893
|
+
var ExecutionLayerBackend = class {
|
|
894
|
+
local;
|
|
895
|
+
root;
|
|
896
|
+
localTags = /* @__PURE__ */ new Set();
|
|
897
|
+
constructor(local, root) {
|
|
898
|
+
this.local = local;
|
|
899
|
+
this.root = root;
|
|
900
|
+
}
|
|
901
|
+
register(registration) {
|
|
902
|
+
this.localTags.add(captureLayerRegistrationTag(registration));
|
|
903
|
+
this.local.register(registration);
|
|
904
|
+
}
|
|
905
|
+
async resolve(token) {
|
|
906
|
+
const tag = captureServiceTag(token);
|
|
907
|
+
if (this.localTags.has(tag)) return await this.local.resolve(token);
|
|
908
|
+
return await this.root.resolve(token);
|
|
909
|
+
}
|
|
910
|
+
async disposeAll() {
|
|
911
|
+
await this.local.disposeAll();
|
|
912
|
+
this.localTags.clear();
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
var RuntimeHandleImpl = class {
|
|
916
|
+
backend;
|
|
917
|
+
resolver;
|
|
918
|
+
rootScope;
|
|
919
|
+
onCleanupFailure;
|
|
920
|
+
contextStorage;
|
|
921
|
+
signal;
|
|
922
|
+
observers;
|
|
923
|
+
services;
|
|
924
|
+
executionDependencies;
|
|
925
|
+
executor;
|
|
926
|
+
lifecycleEntries;
|
|
927
|
+
disposePromise;
|
|
928
|
+
warmupPromise;
|
|
929
|
+
executions = /* @__PURE__ */ new Set();
|
|
930
|
+
rootQuiesces = [];
|
|
931
|
+
serviceTags;
|
|
932
|
+
shutdownController = new AbortController();
|
|
933
|
+
taskSupervisor;
|
|
934
|
+
state = "active";
|
|
935
|
+
warmupState = "idle";
|
|
936
|
+
constructor(backend, resolver, rootScope, onCleanupFailure, contextStorage, signal, observers, services, serviceTags, executionDependencies, executor, lifecycleEntries) {
|
|
937
|
+
this.backend = backend;
|
|
938
|
+
this.resolver = resolver;
|
|
939
|
+
this.rootScope = rootScope;
|
|
940
|
+
this.onCleanupFailure = onCleanupFailure;
|
|
941
|
+
this.contextStorage = contextStorage;
|
|
942
|
+
this.signal = signal;
|
|
943
|
+
this.observers = observers;
|
|
944
|
+
this.services = services;
|
|
945
|
+
this.executionDependencies = executionDependencies;
|
|
946
|
+
this.executor = executor;
|
|
947
|
+
this.lifecycleEntries = lifecycleEntries;
|
|
948
|
+
this.serviceTags = Object.freeze([...serviceTags]);
|
|
949
|
+
this.taskSupervisor = new RuntimeTaskSupervisor(() => this.executionDependencies.createExecutionId(), () => this.executionDependencies.now(), this.observers, this.onCleanupFailure);
|
|
950
|
+
this.taskSupervisor.bindScope(this.rootScope);
|
|
951
|
+
}
|
|
952
|
+
registerRootQuiesce(provider, instance) {
|
|
953
|
+
if (provider.quiesce === void 0) return;
|
|
954
|
+
this.rootQuiesces.push({
|
|
955
|
+
started: false,
|
|
956
|
+
quiesce: (reason) => provider.quiesce(instance, reason)
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
registerLifecycleQuiesce(entry, instance) {
|
|
960
|
+
if (entry.quiesce === void 0) return;
|
|
961
|
+
this.rootQuiesces.push({
|
|
962
|
+
started: false,
|
|
963
|
+
quiesce: (reason) => entry.quiesce(instance, reason)
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
assertRootAcquisitionAllowed() {
|
|
967
|
+
this.assertActive();
|
|
968
|
+
}
|
|
969
|
+
emitShutdown(event) {
|
|
970
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onShutdown, event);
|
|
971
|
+
}
|
|
972
|
+
async quiesceResources(reason) {
|
|
973
|
+
const failures = [];
|
|
974
|
+
for (;;) {
|
|
975
|
+
const registration = [...this.rootQuiesces].reverse().find((candidate) => !candidate.started);
|
|
976
|
+
if (registration === void 0) return failures;
|
|
977
|
+
registration.started = true;
|
|
978
|
+
try {
|
|
979
|
+
await registration.quiesce(reason);
|
|
980
|
+
} catch (cause) {
|
|
981
|
+
failures.push(cause);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
inspect() {
|
|
986
|
+
const tasks = this.taskSupervisor.inspect();
|
|
987
|
+
const executions = Object.freeze([...this.executions].map(({ metadata }) => {
|
|
988
|
+
const inspection = {
|
|
989
|
+
executionId: metadata.executionId,
|
|
990
|
+
startedAt: metadata.startedAt
|
|
991
|
+
};
|
|
992
|
+
if (metadata.name !== void 0) inspection.name = metadata.name;
|
|
993
|
+
return Object.freeze(inspection);
|
|
994
|
+
}));
|
|
995
|
+
return Object.freeze({
|
|
996
|
+
state: this.state,
|
|
997
|
+
warmup: this.warmupState,
|
|
998
|
+
activeExecutions: executions.length,
|
|
999
|
+
executions,
|
|
1000
|
+
activeTasks: tasks.length,
|
|
1001
|
+
tasks,
|
|
1002
|
+
services: Object.freeze([...this.serviceTags]),
|
|
1003
|
+
shutdownSignalAborted: this.shutdownController.signal.aborted
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
run(program, options) {
|
|
1007
|
+
this.assertActive();
|
|
1008
|
+
let prepared;
|
|
1009
|
+
try {
|
|
1010
|
+
prepared = prepareExecution(program, options, this.executionDependencies, this.signal, this.shutdownController.signal);
|
|
1011
|
+
} catch (cause) {
|
|
1012
|
+
return Promise.reject(cause);
|
|
1013
|
+
}
|
|
1014
|
+
try {
|
|
1015
|
+
this.assertActive();
|
|
1016
|
+
} catch (cause) {
|
|
1017
|
+
prepared.signalLink.dispose();
|
|
1018
|
+
throw cause;
|
|
1019
|
+
}
|
|
1020
|
+
let executionScope;
|
|
1021
|
+
try {
|
|
1022
|
+
executionScope = this.rootScope.fork();
|
|
1023
|
+
this.taskSupervisor.bindScope(executionScope);
|
|
1024
|
+
} catch (cause) {
|
|
1025
|
+
prepared.signalLink.dispose();
|
|
1026
|
+
throw cause;
|
|
1027
|
+
}
|
|
1028
|
+
return this.startExecution(prepared.signalLink, prepared.metadata, () => this.runExecution(executionScope, program, this.resolver, prepared.signalLink.signal, prepared.metadata));
|
|
1029
|
+
}
|
|
1030
|
+
runWith(layer, program, options) {
|
|
1031
|
+
this.assertActive();
|
|
1032
|
+
let prepared;
|
|
1033
|
+
try {
|
|
1034
|
+
prepared = prepareExecution(program, options, this.executionDependencies, this.signal, this.shutdownController.signal);
|
|
1035
|
+
} catch (cause) {
|
|
1036
|
+
return Promise.reject(cause);
|
|
1037
|
+
}
|
|
1038
|
+
try {
|
|
1039
|
+
this.assertActive();
|
|
1040
|
+
} catch (cause) {
|
|
1041
|
+
prepared.signalLink.dispose();
|
|
1042
|
+
throw cause;
|
|
1043
|
+
}
|
|
1044
|
+
const localBackend = new MapLayerBackend();
|
|
1045
|
+
const backend = new ExecutionLayerBackend(localBackend, this.backend);
|
|
1046
|
+
const resolver = createResolutionResolver(backend, this.contextStorage, this.observers);
|
|
1047
|
+
let executionScope;
|
|
1048
|
+
try {
|
|
1049
|
+
executionScope = this.rootScope.fork();
|
|
1050
|
+
this.taskSupervisor.bindScope(executionScope);
|
|
1051
|
+
} catch (cause) {
|
|
1052
|
+
prepared.signalLink.dispose();
|
|
1053
|
+
throw cause;
|
|
1054
|
+
}
|
|
1055
|
+
return this.startExecution(prepared.signalLink, prepared.metadata, async () => {
|
|
1056
|
+
try {
|
|
1057
|
+
return await this.runExecution(executionScope, async () => {
|
|
1058
|
+
for (const provider of layer.providers) backend.register(bindProviderToScope(provider, executionScope, this.contextStorage, resolver, this.observers, this.executor, prepared.metadata.executionId));
|
|
1059
|
+
return await program();
|
|
1060
|
+
}, resolver, prepared.signalLink.signal, prepared.metadata);
|
|
1061
|
+
} finally {
|
|
1062
|
+
await localBackend.disposeAll();
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
runWithManaged(layer, program, options) {
|
|
1067
|
+
this.assertActive();
|
|
1068
|
+
const managedController = new AbortController();
|
|
1069
|
+
let prepared;
|
|
1070
|
+
prepared = prepareExecution(program, options, this.executionDependencies, this.signal, this.shutdownController.signal, managedController.signal);
|
|
1071
|
+
try {
|
|
1072
|
+
this.assertActive();
|
|
1073
|
+
} catch (cause) {
|
|
1074
|
+
prepared.signalLink.dispose();
|
|
1075
|
+
throw cause;
|
|
1076
|
+
}
|
|
1077
|
+
const localBackend = new MapLayerBackend();
|
|
1078
|
+
const backend = new ExecutionLayerBackend(localBackend, this.backend);
|
|
1079
|
+
const resolver = createResolutionResolver(backend, this.contextStorage, this.observers);
|
|
1080
|
+
let executionScope;
|
|
1081
|
+
try {
|
|
1082
|
+
executionScope = this.rootScope.fork();
|
|
1083
|
+
} catch (cause) {
|
|
1084
|
+
prepared.signalLink.dispose();
|
|
1085
|
+
throw cause;
|
|
1086
|
+
}
|
|
1087
|
+
this.taskSupervisor.bindScope(executionScope);
|
|
1088
|
+
const executionContext = makeRuntimeContext(resolver, executionScope, [], prepared.signalLink.signal, void 0, prepared.metadata.executionId, this.executor);
|
|
1089
|
+
let plan;
|
|
1090
|
+
let pendingCancel;
|
|
1091
|
+
let cancelPromise;
|
|
1092
|
+
let executionPromise;
|
|
1093
|
+
let settled = false;
|
|
1094
|
+
let resolveReadiness;
|
|
1095
|
+
let rejectReadiness;
|
|
1096
|
+
const readiness = new Promise((resolve, reject) => {
|
|
1097
|
+
resolveReadiness = resolve;
|
|
1098
|
+
rejectReadiness = reject;
|
|
1099
|
+
});
|
|
1100
|
+
readiness.catch(() => void 0);
|
|
1101
|
+
const runInExecution = (operation) => {
|
|
1102
|
+
if (settled) return Promise.reject(new RuntimeHandleDisposedError());
|
|
1103
|
+
return Promise.resolve(runRuntimeContext(this.contextStorage, executionContext, () => ScopeRuntime.run(executionScope, operation, this.contextStorage)));
|
|
1104
|
+
};
|
|
1105
|
+
const cancel = (reason) => {
|
|
1106
|
+
if (cancelPromise !== void 0) return cancelPromise;
|
|
1107
|
+
cancelPromise = (async () => {
|
|
1108
|
+
pendingCancel = reason === void 0 ? /* @__PURE__ */ new Error("Managed execution cancelled") : reason;
|
|
1109
|
+
if (!managedController.signal.aborted) managedController.abort(pendingCancel);
|
|
1110
|
+
const currentPlan = plan;
|
|
1111
|
+
if (currentPlan?.cancel !== void 0) try {
|
|
1112
|
+
await currentPlan.cancel(pendingCancel);
|
|
1113
|
+
} catch {}
|
|
1114
|
+
if (executionPromise !== void 0) await executionPromise.catch(() => void 0);
|
|
1115
|
+
})();
|
|
1116
|
+
return cancelPromise;
|
|
1117
|
+
};
|
|
1118
|
+
const run = async () => {
|
|
1119
|
+
try {
|
|
1120
|
+
for (const provider of layer.providers) backend.register(bindProviderToScope(provider, executionScope, this.contextStorage, resolver, this.observers, this.executor, prepared.metadata.executionId));
|
|
1121
|
+
const next = await program();
|
|
1122
|
+
plan = next;
|
|
1123
|
+
if (pendingCancel !== void 0) {
|
|
1124
|
+
rejectReadiness(pendingCancel);
|
|
1125
|
+
await next.cancel?.(pendingCancel);
|
|
1126
|
+
throw pendingCancel;
|
|
1127
|
+
}
|
|
1128
|
+
try {
|
|
1129
|
+
resolveReadiness(await next.readiness);
|
|
1130
|
+
} catch (cause) {
|
|
1131
|
+
rejectReadiness(cause);
|
|
1132
|
+
await next.cancel?.(cause);
|
|
1133
|
+
throw cause;
|
|
1134
|
+
}
|
|
1135
|
+
return await next.completion;
|
|
1136
|
+
} catch (cause) {
|
|
1137
|
+
rejectReadiness(cause);
|
|
1138
|
+
throw cause;
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
executionPromise = this.startExecution(prepared.signalLink, prepared.metadata, async () => {
|
|
1142
|
+
try {
|
|
1143
|
+
return await this.runExecution(executionScope, run, resolver, prepared.signalLink.signal, prepared.metadata, executionContext);
|
|
1144
|
+
} finally {
|
|
1145
|
+
await localBackend.disposeAll();
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
executionPromise.then(() => {
|
|
1149
|
+
settled = true;
|
|
1150
|
+
}, () => {
|
|
1151
|
+
settled = true;
|
|
1152
|
+
});
|
|
1153
|
+
executionPromise.catch(() => void 0);
|
|
1154
|
+
const signalListener = () => {
|
|
1155
|
+
cancel(prepared.signalLink.signal.reason);
|
|
1156
|
+
};
|
|
1157
|
+
if (!prepared.signalLink.signal.aborted) prepared.signalLink.signal.addEventListener("abort", signalListener, { once: true });
|
|
1158
|
+
executionPromise.then(() => prepared.signalLink.signal.removeEventListener("abort", signalListener), () => prepared.signalLink.signal.removeEventListener("abort", signalListener));
|
|
1159
|
+
const completion = executionPromise.then(() => void 0);
|
|
1160
|
+
completion.catch(() => void 0);
|
|
1161
|
+
return Object.freeze({
|
|
1162
|
+
readiness,
|
|
1163
|
+
completion,
|
|
1164
|
+
signal: prepared.signalLink.signal,
|
|
1165
|
+
run: runInExecution,
|
|
1166
|
+
cancel
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
warmup() {
|
|
1170
|
+
this.assertActive();
|
|
1171
|
+
if (this.warmupPromise) return this.warmupPromise;
|
|
1172
|
+
this.warmupState = "running";
|
|
1173
|
+
let warmup;
|
|
1174
|
+
try {
|
|
1175
|
+
warmup = runRuntimeContext(this.contextStorage, makeRuntimeContext(this.resolver, this.rootScope, [], this.signal, void 0, void 0, this.executor), async () => {
|
|
1176
|
+
for (const service of this.services) await this.resolver.resolve(service);
|
|
1177
|
+
});
|
|
1178
|
+
} catch (cause) {
|
|
1179
|
+
this.warmupState = "failed";
|
|
1180
|
+
return Promise.reject(cause);
|
|
1181
|
+
}
|
|
1182
|
+
this.warmupPromise = warmup;
|
|
1183
|
+
warmup.then(() => {
|
|
1184
|
+
if (this.warmupPromise === warmup) {
|
|
1185
|
+
this.warmupState = "completed";
|
|
1186
|
+
this.warmupPromise = void 0;
|
|
1187
|
+
}
|
|
1188
|
+
}, () => {
|
|
1189
|
+
if (this.warmupPromise === warmup) {
|
|
1190
|
+
this.warmupState = "failed";
|
|
1191
|
+
this.warmupPromise = void 0;
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1194
|
+
return warmup;
|
|
1195
|
+
}
|
|
1196
|
+
activateLifecycle() {
|
|
1197
|
+
this.assertActive();
|
|
1198
|
+
if (this.lifecycleEntries.length === 0) return Promise.resolve();
|
|
1199
|
+
return runRuntimeContext(this.contextStorage, makeRuntimeContext(this.resolver, this.rootScope, [], this.signal, void 0, void 0, this.executor), () => ScopeRuntime.run(this.rootScope, async () => {
|
|
1200
|
+
for (const entry of this.lifecycleEntries) {
|
|
1201
|
+
const metadata = lifecycleEventMetadata(entry);
|
|
1202
|
+
const startEvent = Object.freeze({ ...metadata });
|
|
1203
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onLifecycleStart, startEvent);
|
|
1204
|
+
try {
|
|
1205
|
+
const resource = await this.rootScope.acquire(() => entry.acquire(), (resource, outcome) => releaseLifecycleResource(entry, resource, outcome, this.observers));
|
|
1206
|
+
this.registerLifecycleQuiesce(entry, resource);
|
|
1207
|
+
const endEvent = Object.freeze({
|
|
1208
|
+
...metadata,
|
|
1209
|
+
outcome: SCOPE_SUCCESS
|
|
1210
|
+
});
|
|
1211
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onLifecycleEnd, endEvent);
|
|
1212
|
+
} catch (cause) {
|
|
1213
|
+
const endEvent = Object.freeze({
|
|
1214
|
+
...metadata,
|
|
1215
|
+
outcome: {
|
|
1216
|
+
status: "failure",
|
|
1217
|
+
cause
|
|
1218
|
+
},
|
|
1219
|
+
error: cause
|
|
1220
|
+
});
|
|
1221
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onLifecycleEnd, endEvent);
|
|
1222
|
+
throw cause;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}));
|
|
1226
|
+
}
|
|
1227
|
+
startExecution(signalLink, metadata, run) {
|
|
1228
|
+
let resolveExecution;
|
|
1229
|
+
let rejectExecution;
|
|
1230
|
+
const execution = new Promise((resolve, reject) => {
|
|
1231
|
+
resolveExecution = resolve;
|
|
1232
|
+
rejectExecution = reject;
|
|
1233
|
+
});
|
|
1234
|
+
const activeExecution = {
|
|
1235
|
+
promise: execution,
|
|
1236
|
+
metadata
|
|
1237
|
+
};
|
|
1238
|
+
this.executions.add(activeExecution);
|
|
1239
|
+
execution.then(() => {
|
|
1240
|
+
this.executions.delete(activeExecution);
|
|
1241
|
+
signalLink.dispose();
|
|
1242
|
+
}, () => {
|
|
1243
|
+
this.executions.delete(activeExecution);
|
|
1244
|
+
signalLink.dispose();
|
|
1245
|
+
});
|
|
1246
|
+
try {
|
|
1247
|
+
run(metadata).then((value) => {
|
|
1248
|
+
resolveExecution(value);
|
|
1249
|
+
}, (cause) => {
|
|
1250
|
+
rejectExecution(cause);
|
|
1251
|
+
});
|
|
1252
|
+
} catch (cause) {
|
|
1253
|
+
rejectExecution(cause);
|
|
1254
|
+
}
|
|
1255
|
+
return execution;
|
|
1256
|
+
}
|
|
1257
|
+
runExecution(executionScope, program, resolver = this.resolver, signal = this.shutdownController.signal, metadata, executionContext) {
|
|
1258
|
+
let outcome;
|
|
1259
|
+
let cleanupFailure;
|
|
1260
|
+
const onOutcome = (determinedOutcome) => {
|
|
1261
|
+
outcome = determinedOutcome;
|
|
1262
|
+
};
|
|
1263
|
+
const onExecutionCleanupFailure = async (diagnostic) => {
|
|
1264
|
+
cleanupFailure = diagnostic.error;
|
|
1265
|
+
if (this.onCleanupFailure !== void 0) await this.onCleanupFailure(diagnostic);
|
|
1266
|
+
};
|
|
1267
|
+
const runOptions = {
|
|
1268
|
+
classify: classifyRuntimeOutcome,
|
|
1269
|
+
onOutcome,
|
|
1270
|
+
onCleanupFailure: onExecutionCleanupFailure
|
|
1271
|
+
};
|
|
1272
|
+
const startEvent = Object.freeze({
|
|
1273
|
+
...metadata,
|
|
1274
|
+
scope: executionScope
|
|
1275
|
+
});
|
|
1276
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onExecutionStart, startEvent);
|
|
1277
|
+
let ended = false;
|
|
1278
|
+
const notifyEnd = (finalOutcome) => {
|
|
1279
|
+
if (ended) return;
|
|
1280
|
+
ended = true;
|
|
1281
|
+
const endEvent = {
|
|
1282
|
+
...metadata,
|
|
1283
|
+
scope: executionScope,
|
|
1284
|
+
outcome: finalOutcome,
|
|
1285
|
+
durationMs: executionDuration(metadata, this.executionDependencies)
|
|
1286
|
+
};
|
|
1287
|
+
if (cleanupFailure !== void 0) Object.assign(endEvent, { cleanupFailure });
|
|
1288
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onExecutionEnd, Object.freeze(endEvent));
|
|
1289
|
+
};
|
|
1290
|
+
let execution;
|
|
1291
|
+
try {
|
|
1292
|
+
execution = runScoped(executionScope, program, {
|
|
1293
|
+
...runOptions,
|
|
1294
|
+
contextStorage: this.contextStorage,
|
|
1295
|
+
context: executionContext ?? makeRuntimeContext(resolver, executionScope, [], signal, void 0, metadata.executionId, this.executor)
|
|
1296
|
+
});
|
|
1297
|
+
} catch (cause) {
|
|
1298
|
+
notifyEnd({
|
|
1299
|
+
status: "failure",
|
|
1300
|
+
cause
|
|
1301
|
+
});
|
|
1302
|
+
return Promise.reject(cause);
|
|
1303
|
+
}
|
|
1304
|
+
return execution.then((value) => {
|
|
1305
|
+
notifyEnd(outcome ?? { status: "success" });
|
|
1306
|
+
return value;
|
|
1307
|
+
}, (cause) => {
|
|
1308
|
+
notifyEnd(outcome ?? {
|
|
1309
|
+
status: "failure",
|
|
1310
|
+
cause
|
|
1311
|
+
});
|
|
1312
|
+
throw cause;
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
dispose(input) {
|
|
1316
|
+
if (this.disposePromise) return this.disposePromise;
|
|
1317
|
+
const outcome = isScopeOutcome(input) || input === void 0 ? input ?? SCOPE_SUCCESS : SCOPE_SUCCESS;
|
|
1318
|
+
const options = isScopeOutcome(input) || input === void 0 ? {} : input;
|
|
1319
|
+
validateDisposeOptions(options);
|
|
1320
|
+
const reason = Object.freeze(options.reason ?? (isScopeOutcome(input) ? makeShutdownReason(input) : { kind: "dispose" }));
|
|
1321
|
+
this.state = "quiescing";
|
|
1322
|
+
let resolveDispose;
|
|
1323
|
+
let rejectDispose;
|
|
1324
|
+
const disposePromise = new Promise((resolve, reject) => {
|
|
1325
|
+
resolveDispose = resolve;
|
|
1326
|
+
rejectDispose = reject;
|
|
1327
|
+
});
|
|
1328
|
+
this.disposePromise = disposePromise;
|
|
1329
|
+
this.emitShutdown(Object.freeze({
|
|
1330
|
+
phase: "shutdown-requested",
|
|
1331
|
+
reason,
|
|
1332
|
+
activeExecutions: this.executions.size
|
|
1333
|
+
}));
|
|
1334
|
+
this.performDispose(outcome, options, reason).then(resolveDispose, rejectDispose);
|
|
1335
|
+
return disposePromise;
|
|
1336
|
+
}
|
|
1337
|
+
async performDispose(outcome, options, reason) {
|
|
1338
|
+
const failures = [];
|
|
1339
|
+
if (this.warmupPromise !== void 0) await Promise.allSettled([this.warmupPromise]);
|
|
1340
|
+
const quiesceStartedAt = readRuntimeTimestamp(this.executionDependencies);
|
|
1341
|
+
this.emitShutdown({
|
|
1342
|
+
phase: "quiesce-start",
|
|
1343
|
+
reason
|
|
1344
|
+
});
|
|
1345
|
+
failures.push(...await this.quiesceResources(reason));
|
|
1346
|
+
const quiesceEnd = {
|
|
1347
|
+
phase: "quiesce-end",
|
|
1348
|
+
reason
|
|
1349
|
+
};
|
|
1350
|
+
if (quiesceStartedAt !== void 0) Object.assign(quiesceEnd, { durationMs: Math.max(0, (readRuntimeTimestamp(this.executionDependencies) ?? quiesceStartedAt) - quiesceStartedAt) });
|
|
1351
|
+
this.emitShutdown(quiesceEnd);
|
|
1352
|
+
this.state = "draining";
|
|
1353
|
+
const executions = [...this.executions];
|
|
1354
|
+
this.emitShutdown({
|
|
1355
|
+
phase: "drain-start",
|
|
1356
|
+
reason,
|
|
1357
|
+
activeExecutions: executions.length
|
|
1358
|
+
});
|
|
1359
|
+
await this.waitForExecutions(executions, options, reason);
|
|
1360
|
+
failures.push(...await this.quiesceResources(reason));
|
|
1361
|
+
this.emitShutdown({
|
|
1362
|
+
phase: "drain-end",
|
|
1363
|
+
reason,
|
|
1364
|
+
activeExecutions: this.executions.size
|
|
1365
|
+
});
|
|
1366
|
+
this.state = "releasing";
|
|
1367
|
+
this.emitShutdown({
|
|
1368
|
+
phase: "release-start",
|
|
1369
|
+
reason
|
|
1370
|
+
});
|
|
1371
|
+
try {
|
|
1372
|
+
const signalLink = linkAbortSignals(this.signal, this.shutdownController.signal);
|
|
1373
|
+
try {
|
|
1374
|
+
await runRuntimeContext(this.contextStorage, makeRuntimeContext(this.resolver, this.rootScope, [], signalLink.signal, void 0, void 0, this.executor), () => this.rootScope.close(outcome));
|
|
1375
|
+
} finally {
|
|
1376
|
+
signalLink.dispose();
|
|
1377
|
+
}
|
|
1378
|
+
} catch (cause) {
|
|
1379
|
+
failures.push(cause);
|
|
1380
|
+
}
|
|
1381
|
+
try {
|
|
1382
|
+
await this.backend.disposeAll();
|
|
1383
|
+
} catch (cause) {
|
|
1384
|
+
failures.push(cause);
|
|
1385
|
+
}
|
|
1386
|
+
this.state = "disposed";
|
|
1387
|
+
this.emitShutdown({
|
|
1388
|
+
phase: "release-end",
|
|
1389
|
+
reason
|
|
1390
|
+
});
|
|
1391
|
+
if (failures.length > 0) {
|
|
1392
|
+
const error = new LayerDisposeError(failures.flatMap(normalizeDisposeCauses));
|
|
1393
|
+
this.emitShutdown({
|
|
1394
|
+
phase: "shutdown-failure",
|
|
1395
|
+
reason,
|
|
1396
|
+
error
|
|
1397
|
+
});
|
|
1398
|
+
await notifyShutdownFailure(this.onCleanupFailure, {
|
|
1399
|
+
outcome,
|
|
1400
|
+
error
|
|
1401
|
+
});
|
|
1402
|
+
throw error;
|
|
1403
|
+
}
|
|
1404
|
+
this.emitShutdown({
|
|
1405
|
+
phase: "shutdown-complete",
|
|
1406
|
+
reason
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
async waitForExecutions(executions, options, reason) {
|
|
1410
|
+
const settled = Promise.allSettled(executions.map((execution) => execution.promise));
|
|
1411
|
+
if (options.abortAfterGracePeriod !== true || executions.length === 0) {
|
|
1412
|
+
await settled;
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
const gracePeriod = options.gracePeriod ?? 0;
|
|
1416
|
+
let timer;
|
|
1417
|
+
const timedOut = await Promise.race([settled.then(() => false), new Promise((resolve) => {
|
|
1418
|
+
timer = setTimeout(() => resolve(true), gracePeriod);
|
|
1419
|
+
})]);
|
|
1420
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1421
|
+
if (timedOut && !this.shutdownController.signal.aborted) {
|
|
1422
|
+
this.state = "aborting";
|
|
1423
|
+
this.emitShutdown({
|
|
1424
|
+
phase: "abort-active",
|
|
1425
|
+
reason,
|
|
1426
|
+
activeExecutions: this.executions.size
|
|
1427
|
+
});
|
|
1428
|
+
this.shutdownController.abort(/* @__PURE__ */ new Error("Runtime shutdown grace period exceeded"));
|
|
1429
|
+
}
|
|
1430
|
+
await settled;
|
|
1431
|
+
}
|
|
1432
|
+
assertActive() {
|
|
1433
|
+
if (this.state !== "active") throw new RuntimeHandleDisposedError();
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
/** Build a Runtime handle for a complete Layer and register its providers. */
|
|
1437
|
+
const createRuntimeHandle = async (layer, backend, options = {}, executionOverrides = {}, providedExecutor) => {
|
|
1438
|
+
const entries = getLayerEntries(layer);
|
|
1439
|
+
const providers = entries.filter((entry) => !isLifecycleEntry(entry));
|
|
1440
|
+
const lifecycleEntries = entries.filter(isLifecycleEntry);
|
|
1441
|
+
const services = providers.map((provider) => provider.service);
|
|
1442
|
+
const serviceTags = providers.map((provider) => captureLayerRegistrationTag(provider));
|
|
1443
|
+
const rootScope = Scope.make();
|
|
1444
|
+
const contextStorage = options.contextStorage ?? defaultRuntimeContextStorage;
|
|
1445
|
+
const observers = options.observers ?? [];
|
|
1446
|
+
const resolver = createResolutionResolver(backend, contextStorage, observers);
|
|
1447
|
+
const executionDependencies = makeRuntimeExecutionDependencies(executionOverrides);
|
|
1448
|
+
ScopeRuntime.bind(rootScope, contextStorage);
|
|
1449
|
+
let handle;
|
|
1450
|
+
const pendingRootQuiesces = [];
|
|
1451
|
+
const executor = providedExecutor ?? createRuntimeExecutor({
|
|
1452
|
+
run: (program, runOptions) => handle.run(program, runOptions),
|
|
1453
|
+
runWith: (request, program, runOptions) => handle.runWith(request, program, runOptions),
|
|
1454
|
+
runWithManaged: (request, program, runOptions) => handle.runWithManaged(request, program, runOptions)
|
|
1455
|
+
});
|
|
1456
|
+
const contextExecutor = eraseRuntimeExecutor(executor);
|
|
1457
|
+
let current;
|
|
1458
|
+
try {
|
|
1459
|
+
for (const provider of providers) {
|
|
1460
|
+
current = provider;
|
|
1461
|
+
await backend.register(bindProviderToScope(provider, rootScope, contextStorage, resolver, observers, contextExecutor, void 0, () => handle?.assertRootAcquisitionAllowed(), (instance) => {
|
|
1462
|
+
if (handle === void 0) pendingRootQuiesces.push({
|
|
1463
|
+
provider,
|
|
1464
|
+
instance
|
|
1465
|
+
});
|
|
1466
|
+
else handle.registerRootQuiesce(provider, instance);
|
|
1467
|
+
}));
|
|
1468
|
+
}
|
|
1469
|
+
} catch (registrationCause) {
|
|
1470
|
+
const outcome = {
|
|
1471
|
+
status: "failure",
|
|
1472
|
+
cause: registrationCause
|
|
1473
|
+
};
|
|
1474
|
+
const cleanupCauses = [];
|
|
1475
|
+
try {
|
|
1476
|
+
await runRuntimeContext(contextStorage, makeRuntimeContext(resolver, rootScope, [], options.signal, void 0, void 0, contextExecutor), () => rootScope.close(outcome));
|
|
1477
|
+
} catch (cause) {
|
|
1478
|
+
cleanupCauses.push(cause);
|
|
1479
|
+
}
|
|
1480
|
+
try {
|
|
1481
|
+
await backend.disposeAll();
|
|
1482
|
+
} catch (cause) {
|
|
1483
|
+
cleanupCauses.push(cause);
|
|
1484
|
+
}
|
|
1485
|
+
if (cleanupCauses.length > 0) {
|
|
1486
|
+
const shutdownError = new LayerDisposeError(cleanupCauses.flatMap(normalizeDisposeCauses));
|
|
1487
|
+
await notifyShutdownFailure(options.onCleanupFailure, {
|
|
1488
|
+
outcome,
|
|
1489
|
+
error: shutdownError
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
const cleanupCause = cleanupCauses.length === 1 ? cleanupCauses[0] : cleanupCauses.length > 1 ? new LayerDisposeError(cleanupCauses.flatMap(normalizeDisposeCauses)) : void 0;
|
|
1493
|
+
throw new LayerRegistrationError(current?.service, registrationCause, cleanupCause);
|
|
1494
|
+
}
|
|
1495
|
+
handle = new RuntimeHandleImpl(backend, resolver, rootScope, options.onCleanupFailure, contextStorage, options.signal, observers, services, serviceTags, executionDependencies, contextExecutor, lifecycleEntries);
|
|
1496
|
+
for (const { provider, instance } of pendingRootQuiesces) handle.registerRootQuiesce(provider, instance);
|
|
1497
|
+
return handle;
|
|
1498
|
+
};
|
|
1499
|
+
const isLifecycleEntry = (entry) => "kind" in entry && entry.kind === "lifecycle";
|
|
1500
|
+
//#endregion
|
|
1501
|
+
//#region src/runtime/runtime.ts
|
|
1502
|
+
const isLayerBackend = (value) => value !== void 0 && "register" in value && "resolve" in value && "disposeAll" in value;
|
|
1503
|
+
const resolveRuntimeConfig = (backendOrOptions, legacyOptions) => {
|
|
1504
|
+
if (isLayerBackend(backendOrOptions)) return {
|
|
1505
|
+
backend: backendOrOptions,
|
|
1506
|
+
options: legacyOptions ?? {}
|
|
1507
|
+
};
|
|
1508
|
+
const options = backendOrOptions ?? {};
|
|
1509
|
+
return {
|
|
1510
|
+
backend: options.backend ?? new MapLayerBackend(),
|
|
1511
|
+
options
|
|
1512
|
+
};
|
|
1513
|
+
};
|
|
1514
|
+
/**
|
|
1515
|
+
* Long-lived execution environment backed by a complete Layer.
|
|
1516
|
+
*
|
|
1517
|
+
* A Runtime owns Layer resources until `dispose()` is called. Each `run()` is
|
|
1518
|
+
* isolated in a child Scope, while Layer-scoped resources remain shared.
|
|
1519
|
+
*
|
|
1520
|
+
* @example
|
|
1521
|
+
* ```ts
|
|
1522
|
+
* const runtime = await Runtime.make(AppLive)
|
|
1523
|
+
* const result = await runtime.run(loadUser('u1'))
|
|
1524
|
+
* await runtime.dispose()
|
|
1525
|
+
* ```
|
|
1526
|
+
*
|
|
1527
|
+
* @typeParam Provided The branded Service instances supplied by the Layer.
|
|
1528
|
+
*/
|
|
1529
|
+
var Runtime = class Runtime {
|
|
1530
|
+
handle;
|
|
1531
|
+
executor;
|
|
1532
|
+
constructor(handle, executor) {
|
|
1533
|
+
this.handle = handle;
|
|
1534
|
+
this.executor = executor;
|
|
1535
|
+
}
|
|
1536
|
+
static async make(layer, backendOrOptions, legacyOptions) {
|
|
1537
|
+
const { backend, options } = resolveRuntimeConfig(backendOrOptions, legacyOptions);
|
|
1538
|
+
let handle;
|
|
1539
|
+
const executor = createRuntimeExecutor({
|
|
1540
|
+
run: (program, runOptions) => handle.run(program, runOptions),
|
|
1541
|
+
runWith: (request, program, runOptions) => handle.runWith(request, program, runOptions),
|
|
1542
|
+
runWithManaged: (request, program, runOptions) => handle.runWithManaged(request, program, runOptions)
|
|
1543
|
+
});
|
|
1544
|
+
handle = await createRuntimeHandle(layer, backend, options, {}, executor);
|
|
1545
|
+
const runtime = new Runtime(handle, executor);
|
|
1546
|
+
try {
|
|
1547
|
+
if (options.warmup) await runtime.warmup();
|
|
1548
|
+
await handle.activateLifecycle();
|
|
1549
|
+
} catch (cause) {
|
|
1550
|
+
try {
|
|
1551
|
+
await runtime.disposeWithOutcome({
|
|
1552
|
+
status: "failure",
|
|
1553
|
+
cause
|
|
1554
|
+
});
|
|
1555
|
+
} catch {}
|
|
1556
|
+
throw cause;
|
|
1557
|
+
}
|
|
1558
|
+
return runtime;
|
|
1559
|
+
}
|
|
1560
|
+
static async run(layer, backendOrProgramOrOptions, programOrOptions, legacyOptions) {
|
|
1561
|
+
let program;
|
|
1562
|
+
let backendOrOptions;
|
|
1563
|
+
let options;
|
|
1564
|
+
if (typeof backendOrProgramOrOptions === "function") {
|
|
1565
|
+
program = backendOrProgramOrOptions;
|
|
1566
|
+
backendOrOptions = programOrOptions;
|
|
1567
|
+
options = void 0;
|
|
1568
|
+
} else {
|
|
1569
|
+
backendOrOptions = backendOrProgramOrOptions;
|
|
1570
|
+
program = programOrOptions;
|
|
1571
|
+
options = legacyOptions;
|
|
1572
|
+
}
|
|
1573
|
+
const config = resolveRuntimeConfig(backendOrOptions, options);
|
|
1574
|
+
let programOutcome;
|
|
1575
|
+
const outcomeObserver = { onExecutionEnd: ({ outcome }) => {
|
|
1576
|
+
programOutcome = outcome;
|
|
1577
|
+
} };
|
|
1578
|
+
const runtimeOptions = {
|
|
1579
|
+
...config.options,
|
|
1580
|
+
observers: [outcomeObserver, ...config.options.observers ?? []]
|
|
1581
|
+
};
|
|
1582
|
+
const runtime = await Runtime.make(layer, config.backend, runtimeOptions);
|
|
1583
|
+
let value;
|
|
1584
|
+
let executionFailed = false;
|
|
1585
|
+
let executionFailure;
|
|
1586
|
+
try {
|
|
1587
|
+
value = await runtime.runUnchecked(program, config.options);
|
|
1588
|
+
} catch (cause) {
|
|
1589
|
+
executionFailed = true;
|
|
1590
|
+
executionFailure = cause;
|
|
1591
|
+
}
|
|
1592
|
+
const outcome = programOutcome ?? {
|
|
1593
|
+
status: "failure",
|
|
1594
|
+
cause: executionFailure
|
|
1595
|
+
};
|
|
1596
|
+
try {
|
|
1597
|
+
await runtime.disposeWithOutcome(outcome);
|
|
1598
|
+
} catch (shutdownFailure) {
|
|
1599
|
+
if (!executionFailed && outcome.status === "success") throw shutdownFailure;
|
|
1600
|
+
}
|
|
1601
|
+
if (executionFailed) throw executionFailure;
|
|
1602
|
+
return value;
|
|
1603
|
+
}
|
|
1604
|
+
static async use(layer, use, options) {
|
|
1605
|
+
const runtime = await Runtime.make(layer, options);
|
|
1606
|
+
let value;
|
|
1607
|
+
let executionFailed = false;
|
|
1608
|
+
let executionFailure;
|
|
1609
|
+
let programOutcome;
|
|
1610
|
+
try {
|
|
1611
|
+
value = await use(runtime);
|
|
1612
|
+
programOutcome = classifyRuntimeOutcome(value);
|
|
1613
|
+
} catch (cause) {
|
|
1614
|
+
executionFailed = true;
|
|
1615
|
+
executionFailure = cause;
|
|
1616
|
+
programOutcome = {
|
|
1617
|
+
status: "failure",
|
|
1618
|
+
cause
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
const outcome = programOutcome ?? {
|
|
1622
|
+
status: "failure",
|
|
1623
|
+
cause: executionFailure
|
|
1624
|
+
};
|
|
1625
|
+
try {
|
|
1626
|
+
await runtime.disposeWithOutcome(outcome);
|
|
1627
|
+
} catch (shutdownFailure) {
|
|
1628
|
+
if (!executionFailed && outcome.status === "success") throw shutdownFailure;
|
|
1629
|
+
}
|
|
1630
|
+
if (executionFailed) throw executionFailure;
|
|
1631
|
+
return value;
|
|
1632
|
+
}
|
|
1633
|
+
/** Request the non-owning executor view from the current Runtime context. */
|
|
1634
|
+
static executor() {
|
|
1635
|
+
return makeRuntimeExecutorRequest();
|
|
1636
|
+
}
|
|
1637
|
+
/** Resolve every Layer provider and dispose the Runtime if warmup fails. */
|
|
1638
|
+
async warmup() {
|
|
1639
|
+
try {
|
|
1640
|
+
await this.handle.warmup();
|
|
1641
|
+
} catch (cause) {
|
|
1642
|
+
try {
|
|
1643
|
+
await this.handle.dispose({
|
|
1644
|
+
status: "failure",
|
|
1645
|
+
cause
|
|
1646
|
+
});
|
|
1647
|
+
} catch {}
|
|
1648
|
+
throw cause;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
/** Return a detached diagnostic snapshot without changing Runtime state. */
|
|
1652
|
+
inspect() {
|
|
1653
|
+
return this.handle.inspect();
|
|
1654
|
+
}
|
|
1655
|
+
/** Run one execution in this Runtime's child Scope. */
|
|
1656
|
+
run(program, options) {
|
|
1657
|
+
return this.handle.run(program, options);
|
|
1658
|
+
}
|
|
1659
|
+
/** Run one execution with a Layer owned by that execution's child Scope. */
|
|
1660
|
+
runWith(layer, program, options) {
|
|
1661
|
+
return this.handle.runWith(layer, program, options);
|
|
1662
|
+
}
|
|
1663
|
+
runUnchecked(program, options) {
|
|
1664
|
+
return this.handle.run(program, options);
|
|
1665
|
+
}
|
|
1666
|
+
dispose(optionsOrOutcome) {
|
|
1667
|
+
return this.handle.dispose(optionsOrOutcome);
|
|
1668
|
+
}
|
|
1669
|
+
/** Release Runtime-owned resources through JavaScript's async disposal protocol. */
|
|
1670
|
+
async [Symbol.asyncDispose]() {
|
|
1671
|
+
await this.dispose();
|
|
1672
|
+
}
|
|
1673
|
+
disposeWithOutcome(outcome) {
|
|
1674
|
+
return this.handle.dispose(outcome);
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
//#endregion
|
|
1678
|
+
export { RuntimeObserver as a, MapLayerBackend as c, classifyRuntimeOutcome as i, Scope as l, eraseRuntimeExecutor as n, inheritProgramName as o, getRuntimeTaskSupervisor as r, setProgramName as s, Runtime as t, ScopeRuntime as u };
|
|
1679
|
+
|
|
1680
|
+
//# sourceMappingURL=runtime-qpcVwaz_.mjs.map
|