better-effect 0.6.1 → 0.8.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 +72 -2
- package/dist/adapters/iti.d.mts +3 -2
- package/dist/adapters/iti.d.mts.map +1 -1
- package/dist/context-B4yO5LaH.mjs +80 -0
- package/dist/context-B4yO5LaH.mjs.map +1 -0
- package/dist/errors-BXKc7juX.d.mts +8 -0
- package/dist/errors-BXKc7juX.d.mts.map +1 -0
- package/dist/{map-layer-backend-7hQkf_QP.d.mts → index-BFgG9zZC.d.mts} +129 -42
- package/dist/index-BFgG9zZC.d.mts.map +1 -0
- package/dist/{index-R3FdVmdr.d.mts → index-fUyY0eNJ.d.mts} +69 -57
- package/dist/index-fUyY0eNJ.d.mts.map +1 -0
- package/dist/index.d.mts +27 -65
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +379 -55
- package/dist/index.mjs.map +1 -1
- package/dist/map-layer-backend-CGibcwkc.d.mts +42 -0
- package/dist/map-layer-backend-CGibcwkc.d.mts.map +1 -0
- package/dist/runtime/explicit.d.mts +18 -0
- package/dist/runtime/explicit.d.mts.map +1 -0
- package/dist/runtime/explicit.mjs +46 -0
- package/dist/runtime/explicit.mjs.map +1 -0
- package/dist/runtime/node.d.mts +13 -0
- package/dist/runtime/node.d.mts.map +1 -0
- package/dist/runtime/node.mjs +21 -0
- package/dist/runtime/node.mjs.map +1 -0
- package/dist/testing.d.mts +1 -1
- package/package.json +3 -1
- package/dist/index-R3FdVmdr.d.mts.map +0 -1
- package/dist/map-layer-backend-7hQkf_QP.d.mts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { a as LayerRegistrationError, c as ServiceAcquisitionError, i as LayerGeneratorYieldError, l as ServiceNotFoundError, n as DuplicateServiceError, o as ServiceTagCollisionError, r as LayerDisposeError, s as CircularDependencyError, u as ServiceRuntimeNotConfiguredError } from "./internal-identity-Cm4-KIUj.mjs";
|
|
2
|
+
import { a as runRuntimeContext, c as RuntimeContextNotConfiguredError, i as makeRuntimeContext, n as currentRuntimeContext, o as setDefaultRuntimeContextStorage, r as getRuntimeContext, t as activeRuntimeContextStorage } from "./context-B4yO5LaH.mjs";
|
|
3
|
+
import { nodeRuntimeContextStorage } from "./runtime/node.mjs";
|
|
2
4
|
import { t as MapLayerBackend } from "./map-layer-backend-BodcEeNA.mjs";
|
|
3
5
|
import { t as isPromiseLike } from "./runtime-CDcCF5cb.mjs";
|
|
4
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
6
|
import { Result, TaggedError } from "better-result";
|
|
7
|
+
//#region src/runtime/default.ts
|
|
8
|
+
/** The Node/Bun storage used by the main Runtime entrypoint. */
|
|
9
|
+
const defaultRuntimeContextStorage = nodeRuntimeContextStorage;
|
|
10
|
+
setDefaultRuntimeContextStorage(defaultRuntimeContextStorage);
|
|
11
|
+
//#endregion
|
|
6
12
|
//#region src/service/runtime.ts
|
|
7
|
-
const storage$1 = new AsyncLocalStorage();
|
|
8
13
|
/** Provides the resolver context used by Service tokens during execution. */
|
|
9
14
|
var ServiceRuntime = class ServiceRuntime {
|
|
10
15
|
/**
|
|
@@ -19,14 +24,21 @@ var ServiceRuntime = class ServiceRuntime {
|
|
|
19
24
|
* })
|
|
20
25
|
* ```
|
|
21
26
|
*/
|
|
22
|
-
static run(resolver, program) {
|
|
23
|
-
|
|
27
|
+
static run(resolver, program, storage = defaultRuntimeContextStorage) {
|
|
28
|
+
const current = getRuntimeContext(storage);
|
|
29
|
+
const context = makeRuntimeContext(resolver, current?.scope, current?.resolver === resolver ? current.resolutionPath : [], current?.signal);
|
|
30
|
+
return runRuntimeContext(storage, context, program);
|
|
24
31
|
}
|
|
25
32
|
/** Return the resolver active in the current execution context. */
|
|
26
33
|
static current() {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
let context;
|
|
35
|
+
try {
|
|
36
|
+
context = currentRuntimeContext();
|
|
37
|
+
} catch {
|
|
38
|
+
throw new ServiceRuntimeNotConfiguredError();
|
|
39
|
+
}
|
|
40
|
+
if (!context.resolver) throw new ServiceRuntimeNotConfiguredError();
|
|
41
|
+
return context.resolver;
|
|
30
42
|
}
|
|
31
43
|
/** Resolve a Service token using the active resolver. */
|
|
32
44
|
static async resolve(token) {
|
|
@@ -193,6 +205,10 @@ var Layer = class Layer {
|
|
|
193
205
|
}
|
|
194
206
|
return new Layer([...providers.values()]);
|
|
195
207
|
}
|
|
208
|
+
/** Mark a Layer composition root as complete without changing its runtime value. */
|
|
209
|
+
static complete(layer) {
|
|
210
|
+
return layer;
|
|
211
|
+
}
|
|
196
212
|
/** Replace providers in a base Layer, using tag identity and compatible contracts. */
|
|
197
213
|
static override(base, ...overrides) {
|
|
198
214
|
const providers = /* @__PURE__ */ new Map();
|
|
@@ -253,18 +269,30 @@ const disposeResource = (resource) => {
|
|
|
253
269
|
};
|
|
254
270
|
//#endregion
|
|
255
271
|
//#region src/scope/runtime.ts
|
|
256
|
-
const
|
|
272
|
+
const scopeStorages = /* @__PURE__ */ new WeakMap();
|
|
257
273
|
/** Bridges the current Scope through async execution context. */
|
|
258
274
|
var ScopeRuntime = class {
|
|
259
275
|
/** Supply a Scope while invoking a callback. */
|
|
260
|
-
static run(scope, program) {
|
|
261
|
-
|
|
276
|
+
static run(scope, program, storage = scopeStorages.get(scope) ?? activeRuntimeContextStorage()) {
|
|
277
|
+
scopeStorages.set(scope, storage);
|
|
278
|
+
const current = getRuntimeContext(storage);
|
|
279
|
+
const context = makeRuntimeContext(current?.resolver, scope, current?.resolutionPath ?? [], current?.signal);
|
|
280
|
+
return runRuntimeContext(storage, context, program);
|
|
262
281
|
}
|
|
263
282
|
/** Return the Scope active in the current execution context. */
|
|
264
283
|
static current() {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
284
|
+
let context;
|
|
285
|
+
try {
|
|
286
|
+
context = currentRuntimeContext();
|
|
287
|
+
} catch {
|
|
288
|
+
throw new ScopeRuntimeNotConfiguredError();
|
|
289
|
+
}
|
|
290
|
+
if (!context.scope) throw new ScopeRuntimeNotConfiguredError();
|
|
291
|
+
return context.scope;
|
|
292
|
+
}
|
|
293
|
+
/** Associate a Runtime-owned Scope with its context storage. */
|
|
294
|
+
static bind(scope, storage) {
|
|
295
|
+
scopeStorages.set(scope, storage);
|
|
268
296
|
}
|
|
269
297
|
};
|
|
270
298
|
//#endregion
|
|
@@ -280,7 +308,8 @@ const runScoped = async (scope, program, options) => {
|
|
|
280
308
|
let programFailed = false;
|
|
281
309
|
let programFailure;
|
|
282
310
|
try {
|
|
283
|
-
|
|
311
|
+
const run = () => ScopeRuntime.run(scope, program, options.contextStorage);
|
|
312
|
+
value = await (options.context && options.contextStorage ? runRuntimeContext(options.contextStorage, options.context, run) : run());
|
|
284
313
|
} catch (cause) {
|
|
285
314
|
programFailed = true;
|
|
286
315
|
programFailure = cause;
|
|
@@ -673,27 +702,110 @@ const classifyRuntimeOutcome = (value) => {
|
|
|
673
702
|
return { status: "success" };
|
|
674
703
|
};
|
|
675
704
|
//#endregion
|
|
705
|
+
//#region src/runtime/signal.ts
|
|
706
|
+
const neverAbortedSignal = new AbortController().signal;
|
|
707
|
+
/** Link caller, Runtime and shutdown signals without owning the caller's controller. */
|
|
708
|
+
const linkAbortSignals = (...signals) => {
|
|
709
|
+
const active = signals.filter((signal) => signal !== void 0);
|
|
710
|
+
if (active.length === 0) return {
|
|
711
|
+
signal: neverAbortedSignal,
|
|
712
|
+
dispose: () => {}
|
|
713
|
+
};
|
|
714
|
+
if (active.length === 1) return {
|
|
715
|
+
signal: active[0],
|
|
716
|
+
dispose: () => {}
|
|
717
|
+
};
|
|
718
|
+
const controller = new AbortController();
|
|
719
|
+
const listeners = [];
|
|
720
|
+
let disposed = false;
|
|
721
|
+
const dispose = () => {
|
|
722
|
+
if (disposed) return;
|
|
723
|
+
disposed = true;
|
|
724
|
+
for (const [source, listener] of listeners) source.removeEventListener("abort", listener);
|
|
725
|
+
listeners.length = 0;
|
|
726
|
+
};
|
|
727
|
+
const abortFrom = (source) => {
|
|
728
|
+
if (controller.signal.aborted) return;
|
|
729
|
+
controller.abort(source.reason);
|
|
730
|
+
dispose();
|
|
731
|
+
};
|
|
732
|
+
for (const source of active) {
|
|
733
|
+
if (source.aborted) {
|
|
734
|
+
abortFrom(source);
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
const listener = () => abortFrom(source);
|
|
738
|
+
listeners.push([source, listener]);
|
|
739
|
+
source.addEventListener("abort", listener, { once: true });
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
signal: controller.signal,
|
|
743
|
+
dispose
|
|
744
|
+
};
|
|
745
|
+
};
|
|
746
|
+
/** Return the current cooperative-cancellation signal. */
|
|
747
|
+
const currentAbortSignal = () => currentRuntimeContext().signal ?? neverAbortedSignal;
|
|
748
|
+
/** Yieldable access to the signal of the current Runtime execution. */
|
|
749
|
+
const CurrentAbortSignal = { *[Symbol.iterator]() {
|
|
750
|
+
return currentAbortSignal();
|
|
751
|
+
} };
|
|
752
|
+
//#endregion
|
|
753
|
+
//#region src/runtime/observer.ts
|
|
754
|
+
const notifyRuntimeObservers = (observers, select, event) => {
|
|
755
|
+
for (const observer of observers) {
|
|
756
|
+
const callback = select(observer);
|
|
757
|
+
if (!callback) continue;
|
|
758
|
+
try {
|
|
759
|
+
Promise.resolve(callback(event)).catch(() => {});
|
|
760
|
+
} catch {}
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
//#endregion
|
|
676
764
|
//#region src/layer/resolution.ts
|
|
677
|
-
const resolutionStorage = new AsyncLocalStorage();
|
|
678
765
|
const findCycleStart = (path, token) => path.findIndex((current) => current.serviceTag === token.serviceTag);
|
|
679
766
|
const shouldPreserve = (cause) => cause instanceof CircularDependencyError || cause instanceof ServiceAcquisitionError || cause instanceof ServiceNotFoundError || cause instanceof ServiceTagCollisionError;
|
|
680
767
|
/** Wrap a backend with Runtime-local resolution paths and acquisition errors. */
|
|
681
|
-
const createResolutionResolver = (resolver) => {
|
|
768
|
+
const createResolutionResolver = (resolver, storage = defaultRuntimeContextStorage, observers = []) => {
|
|
682
769
|
const wrapped = { async resolve(token) {
|
|
683
|
-
const context =
|
|
684
|
-
const path = context?.
|
|
770
|
+
const context = getRuntimeContext(storage);
|
|
771
|
+
const path = context?.resolutionPath ?? [];
|
|
685
772
|
const cycleStart = findCycleStart(path, token);
|
|
686
|
-
if (cycleStart >= 0) throw new CircularDependencyError([...path.slice(cycleStart), token]);
|
|
687
773
|
const resolutionPath = [...path, token];
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
774
|
+
const notifyResolve = (outcome) => {
|
|
775
|
+
notifyRuntimeObservers(observers, (observer) => observer.onServiceResolve, {
|
|
776
|
+
service: token,
|
|
777
|
+
resolutionPath,
|
|
778
|
+
outcome
|
|
779
|
+
});
|
|
780
|
+
};
|
|
781
|
+
if (cycleStart >= 0) {
|
|
782
|
+
const error = new CircularDependencyError([...path.slice(cycleStart), token]);
|
|
783
|
+
notifyResolve({
|
|
784
|
+
status: "failure",
|
|
785
|
+
cause: error
|
|
786
|
+
});
|
|
787
|
+
throw error;
|
|
788
|
+
}
|
|
789
|
+
const nextContext = makeRuntimeContext(wrapped, context?.scope, resolutionPath, context?.signal);
|
|
790
|
+
return await runRuntimeContext(storage, nextContext, async () => {
|
|
692
791
|
try {
|
|
693
|
-
|
|
792
|
+
const instance = await resolver.resolve(token);
|
|
793
|
+
notifyResolve({ status: "success" });
|
|
794
|
+
return instance;
|
|
694
795
|
} catch (cause) {
|
|
695
|
-
if (shouldPreserve(cause))
|
|
696
|
-
|
|
796
|
+
if (shouldPreserve(cause)) {
|
|
797
|
+
notifyResolve({
|
|
798
|
+
status: "failure",
|
|
799
|
+
cause
|
|
800
|
+
});
|
|
801
|
+
throw cause;
|
|
802
|
+
}
|
|
803
|
+
const error = new ServiceAcquisitionError(token, resolutionPath, cause);
|
|
804
|
+
notifyResolve({
|
|
805
|
+
status: "failure",
|
|
806
|
+
cause: error
|
|
807
|
+
});
|
|
808
|
+
throw error;
|
|
697
809
|
}
|
|
698
810
|
});
|
|
699
811
|
} };
|
|
@@ -712,50 +824,163 @@ const normalizeDisposeCauses = (cause) => {
|
|
|
712
824
|
if (cause instanceof AggregateError) return [...cause.errors];
|
|
713
825
|
return [cause];
|
|
714
826
|
};
|
|
827
|
+
const isScopeOutcome = (input) => input !== void 0 && "status" in input;
|
|
828
|
+
const validateDisposeOptions = (options) => {
|
|
829
|
+
const { gracePeriod } = options;
|
|
830
|
+
if (gracePeriod !== void 0 && (!Number.isFinite(gracePeriod) || gracePeriod < 0)) throw new RangeError("Runtime dispose gracePeriod must be a finite non-negative number");
|
|
831
|
+
};
|
|
715
832
|
const notifyShutdownFailure = async (observer, diagnostic) => {
|
|
716
833
|
if (!observer) return;
|
|
717
834
|
try {
|
|
718
835
|
await observer(diagnostic);
|
|
719
836
|
} catch {}
|
|
720
837
|
};
|
|
721
|
-
const bindProviderToScope = (provider,
|
|
838
|
+
const bindProviderToScope = (provider, scope, contextStorage, resolver, observers) => ({
|
|
722
839
|
service: provider.service,
|
|
723
|
-
acquire: () =>
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
840
|
+
acquire: () => {
|
|
841
|
+
const current = getRuntimeContext(contextStorage);
|
|
842
|
+
const context = makeRuntimeContext(resolver, scope, current?.resolutionPath ?? [], current?.signal);
|
|
843
|
+
return runRuntimeContext(contextStorage, context, () => ScopeRuntime.run(scope, async () => {
|
|
844
|
+
const resolutionPath = current?.resolutionPath ?? [provider.service];
|
|
845
|
+
try {
|
|
846
|
+
const instance = provider.release ? await scope.acquire(() => provider.acquire(), async (resource, outcome) => {
|
|
847
|
+
try {
|
|
848
|
+
await provider.release(resource, outcome);
|
|
849
|
+
notifyRuntimeObservers(observers, (observer) => observer.onResourceRelease, {
|
|
850
|
+
service: provider.service,
|
|
851
|
+
outcome
|
|
852
|
+
});
|
|
853
|
+
} catch (cause) {
|
|
854
|
+
notifyRuntimeObservers(observers, (observer) => observer.onResourceRelease, {
|
|
855
|
+
service: provider.service,
|
|
856
|
+
outcome,
|
|
857
|
+
error: cause
|
|
858
|
+
});
|
|
859
|
+
throw cause;
|
|
860
|
+
}
|
|
861
|
+
}) : await provider.acquire();
|
|
862
|
+
notifyRuntimeObservers(observers, (observer) => observer.onServiceAcquire, {
|
|
863
|
+
service: provider.service,
|
|
864
|
+
resolutionPath,
|
|
865
|
+
outcome: SCOPE_SUCCESS
|
|
866
|
+
});
|
|
867
|
+
return instance;
|
|
868
|
+
} catch (cause) {
|
|
869
|
+
notifyRuntimeObservers(observers, (observer) => observer.onServiceAcquire, {
|
|
870
|
+
service: provider.service,
|
|
871
|
+
resolutionPath,
|
|
872
|
+
outcome: {
|
|
873
|
+
status: "failure",
|
|
874
|
+
cause
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
throw cause;
|
|
878
|
+
}
|
|
879
|
+
}, contextStorage));
|
|
880
|
+
}
|
|
727
881
|
});
|
|
882
|
+
/** Resolve request-local providers first, then fall back to the Runtime root. */
|
|
883
|
+
var ExecutionLayerBackend = class {
|
|
884
|
+
local;
|
|
885
|
+
root;
|
|
886
|
+
localTags = /* @__PURE__ */ new Set();
|
|
887
|
+
constructor(local, root) {
|
|
888
|
+
this.local = local;
|
|
889
|
+
this.root = root;
|
|
890
|
+
}
|
|
891
|
+
register(registration) {
|
|
892
|
+
this.localTags.add(registration.service.serviceTag);
|
|
893
|
+
this.local.register(registration);
|
|
894
|
+
}
|
|
895
|
+
async resolve(token) {
|
|
896
|
+
if (this.localTags.has(token.serviceTag)) return await this.local.resolve(token);
|
|
897
|
+
return await this.root.resolve(token);
|
|
898
|
+
}
|
|
899
|
+
async disposeAll() {
|
|
900
|
+
await this.local.disposeAll();
|
|
901
|
+
this.localTags.clear();
|
|
902
|
+
}
|
|
903
|
+
};
|
|
728
904
|
var RuntimeHandleImpl = class {
|
|
729
905
|
backend;
|
|
730
906
|
resolver;
|
|
731
907
|
rootScope;
|
|
732
908
|
onCleanupFailure;
|
|
909
|
+
contextStorage;
|
|
910
|
+
signal;
|
|
911
|
+
observers;
|
|
912
|
+
services;
|
|
733
913
|
disposePromise;
|
|
914
|
+
warmupPromise;
|
|
734
915
|
executions = /* @__PURE__ */ new Set();
|
|
916
|
+
shutdownController = new AbortController();
|
|
735
917
|
state = "active";
|
|
736
|
-
constructor(backend, resolver, rootScope, onCleanupFailure) {
|
|
918
|
+
constructor(backend, resolver, rootScope, onCleanupFailure, contextStorage, signal, observers, services) {
|
|
737
919
|
this.backend = backend;
|
|
738
920
|
this.resolver = resolver;
|
|
739
921
|
this.rootScope = rootScope;
|
|
740
922
|
this.onCleanupFailure = onCleanupFailure;
|
|
923
|
+
this.contextStorage = contextStorage;
|
|
924
|
+
this.signal = signal;
|
|
925
|
+
this.observers = observers;
|
|
926
|
+
this.services = services;
|
|
741
927
|
}
|
|
742
|
-
run(program) {
|
|
928
|
+
run(program, options) {
|
|
743
929
|
this.assertActive();
|
|
744
930
|
const executionScope = this.rootScope.fork();
|
|
931
|
+
const signalLink = linkAbortSignals(this.signal, options?.signal, this.shutdownController.signal);
|
|
932
|
+
return this.startExecution(signalLink, () => this.runExecution(executionScope, program, this.resolver, signalLink.signal));
|
|
933
|
+
}
|
|
934
|
+
runWith(layer, program, options) {
|
|
935
|
+
this.assertActive();
|
|
936
|
+
const executionScope = this.rootScope.fork();
|
|
937
|
+
const localBackend = new MapLayerBackend();
|
|
938
|
+
const backend = new ExecutionLayerBackend(localBackend, this.backend);
|
|
939
|
+
const resolver = createResolutionResolver(backend, this.contextStorage, this.observers);
|
|
940
|
+
const signalLink = linkAbortSignals(this.signal, options?.signal, this.shutdownController.signal);
|
|
941
|
+
return this.startExecution(signalLink, async () => {
|
|
942
|
+
try {
|
|
943
|
+
return await this.runExecution(executionScope, async () => {
|
|
944
|
+
for (const provider of layer.providers) backend.register(bindProviderToScope(provider, executionScope, this.contextStorage, resolver, this.observers));
|
|
945
|
+
return await program();
|
|
946
|
+
}, resolver, signalLink.signal);
|
|
947
|
+
} finally {
|
|
948
|
+
await localBackend.disposeAll();
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
warmup() {
|
|
953
|
+
this.assertActive();
|
|
954
|
+
if (this.warmupPromise) return this.warmupPromise;
|
|
955
|
+
const warmup = runRuntimeContext(this.contextStorage, makeRuntimeContext(this.resolver, this.rootScope, [], this.signal), async () => {
|
|
956
|
+
for (const service of this.services) await this.resolver.resolve(service);
|
|
957
|
+
});
|
|
958
|
+
this.warmupPromise = warmup;
|
|
959
|
+
warmup.then(() => {
|
|
960
|
+
if (this.warmupPromise === warmup) this.warmupPromise = void 0;
|
|
961
|
+
}, () => {
|
|
962
|
+
if (this.warmupPromise === warmup) this.warmupPromise = void 0;
|
|
963
|
+
});
|
|
964
|
+
return warmup;
|
|
965
|
+
}
|
|
966
|
+
startExecution(signalLink, run) {
|
|
745
967
|
let resolveExecution;
|
|
746
968
|
let rejectExecution;
|
|
747
969
|
const execution = new Promise((resolve, reject) => {
|
|
748
970
|
resolveExecution = resolve;
|
|
749
971
|
rejectExecution = reject;
|
|
750
972
|
});
|
|
751
|
-
|
|
973
|
+
const activeExecution = { promise: execution };
|
|
974
|
+
this.executions.add(activeExecution);
|
|
752
975
|
execution.then(() => {
|
|
753
|
-
this.executions.delete(
|
|
976
|
+
this.executions.delete(activeExecution);
|
|
977
|
+
signalLink.dispose();
|
|
754
978
|
}, () => {
|
|
755
|
-
this.executions.delete(
|
|
979
|
+
this.executions.delete(activeExecution);
|
|
980
|
+
signalLink.dispose();
|
|
756
981
|
});
|
|
757
982
|
try {
|
|
758
|
-
|
|
983
|
+
run().then((value) => {
|
|
759
984
|
resolveExecution(value);
|
|
760
985
|
}, (cause) => {
|
|
761
986
|
rejectExecution(cause);
|
|
@@ -765,25 +990,54 @@ var RuntimeHandleImpl = class {
|
|
|
765
990
|
}
|
|
766
991
|
return execution;
|
|
767
992
|
}
|
|
768
|
-
runExecution(executionScope, program) {
|
|
993
|
+
runExecution(executionScope, program, resolver = this.resolver, signal = this.shutdownController.signal) {
|
|
769
994
|
const options = this.onCleanupFailure ? {
|
|
770
995
|
classify: classifyRuntimeOutcome,
|
|
771
996
|
onCleanupFailure: this.onCleanupFailure
|
|
772
997
|
} : { classify: classifyRuntimeOutcome };
|
|
773
|
-
|
|
998
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onExecutionStart, { scope: executionScope });
|
|
999
|
+
return runScoped(executionScope, program, {
|
|
1000
|
+
...options,
|
|
1001
|
+
contextStorage: this.contextStorage,
|
|
1002
|
+
context: makeRuntimeContext(resolver, executionScope, [], signal)
|
|
1003
|
+
}).then((value) => {
|
|
1004
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onExecutionEnd, {
|
|
1005
|
+
scope: executionScope,
|
|
1006
|
+
outcome: classifyRuntimeOutcome(value)
|
|
1007
|
+
});
|
|
1008
|
+
return value;
|
|
1009
|
+
}, (cause) => {
|
|
1010
|
+
notifyRuntimeObservers(this.observers, (observer) => observer.onExecutionEnd, {
|
|
1011
|
+
scope: executionScope,
|
|
1012
|
+
outcome: {
|
|
1013
|
+
status: "failure",
|
|
1014
|
+
cause
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
throw cause;
|
|
1018
|
+
});
|
|
774
1019
|
}
|
|
775
|
-
dispose(
|
|
1020
|
+
dispose(input) {
|
|
776
1021
|
if (this.disposePromise) return this.disposePromise;
|
|
1022
|
+
const outcome = isScopeOutcome(input) || input === void 0 ? input ?? SCOPE_SUCCESS : SCOPE_SUCCESS;
|
|
1023
|
+
const options = isScopeOutcome(input) || input === void 0 ? {} : input;
|
|
1024
|
+
validateDisposeOptions(options);
|
|
777
1025
|
this.state = "disposing";
|
|
778
1026
|
const executions = [...this.executions];
|
|
779
|
-
this.disposePromise = this.performDispose(executions, outcome);
|
|
1027
|
+
this.disposePromise = this.performDispose(executions, outcome, options);
|
|
780
1028
|
return this.disposePromise;
|
|
781
1029
|
}
|
|
782
|
-
async performDispose(executions, outcome) {
|
|
1030
|
+
async performDispose(executions, outcome, options) {
|
|
783
1031
|
const failures = [];
|
|
784
|
-
await Promise.allSettled(
|
|
1032
|
+
await Promise.allSettled(this.warmupPromise ? [this.warmupPromise] : []);
|
|
1033
|
+
await this.waitForExecutions(executions, options);
|
|
785
1034
|
try {
|
|
786
|
-
|
|
1035
|
+
const signalLink = linkAbortSignals(this.signal, this.shutdownController.signal);
|
|
1036
|
+
try {
|
|
1037
|
+
await runRuntimeContext(this.contextStorage, makeRuntimeContext(this.resolver, this.rootScope, [], signalLink.signal), () => this.rootScope.close(outcome));
|
|
1038
|
+
} finally {
|
|
1039
|
+
signalLink.dispose();
|
|
1040
|
+
}
|
|
787
1041
|
} catch (cause) {
|
|
788
1042
|
failures.push(cause);
|
|
789
1043
|
}
|
|
@@ -802,6 +1056,21 @@ var RuntimeHandleImpl = class {
|
|
|
802
1056
|
throw error;
|
|
803
1057
|
}
|
|
804
1058
|
}
|
|
1059
|
+
async waitForExecutions(executions, options) {
|
|
1060
|
+
const settled = Promise.allSettled(executions.map((execution) => execution.promise));
|
|
1061
|
+
if (options.abortAfterGracePeriod !== true || executions.length === 0) {
|
|
1062
|
+
await settled;
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
const gracePeriod = options.gracePeriod ?? 0;
|
|
1066
|
+
let timer;
|
|
1067
|
+
const timedOut = await Promise.race([settled.then(() => false), new Promise((resolve) => {
|
|
1068
|
+
timer = setTimeout(() => resolve(true), gracePeriod);
|
|
1069
|
+
})]);
|
|
1070
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1071
|
+
if (timedOut && !this.shutdownController.signal.aborted) this.shutdownController.abort(/* @__PURE__ */ new Error("Runtime shutdown grace period exceeded"));
|
|
1072
|
+
await settled;
|
|
1073
|
+
}
|
|
805
1074
|
assertActive() {
|
|
806
1075
|
if (this.state !== "active") throw new RuntimeHandleDisposedError();
|
|
807
1076
|
}
|
|
@@ -809,12 +1078,15 @@ var RuntimeHandleImpl = class {
|
|
|
809
1078
|
/** Build a Runtime handle for a complete Layer and register its providers. */
|
|
810
1079
|
const createRuntimeHandle = async (layer, backend, options = {}) => {
|
|
811
1080
|
const rootScope = Scope.make();
|
|
812
|
-
const
|
|
1081
|
+
const contextStorage = options.contextStorage ?? defaultRuntimeContextStorage;
|
|
1082
|
+
const observers = options.observers ?? [];
|
|
1083
|
+
const resolver = createResolutionResolver(backend, contextStorage, observers);
|
|
1084
|
+
ScopeRuntime.bind(rootScope, contextStorage);
|
|
813
1085
|
let current;
|
|
814
1086
|
try {
|
|
815
1087
|
for (const provider of layer.providers) {
|
|
816
1088
|
current = provider;
|
|
817
|
-
await backend.register(bindProviderToScope(provider, rootScope));
|
|
1089
|
+
await backend.register(bindProviderToScope(provider, rootScope, contextStorage, resolver, observers));
|
|
818
1090
|
}
|
|
819
1091
|
} catch (registrationCause) {
|
|
820
1092
|
const outcome = {
|
|
@@ -823,7 +1095,7 @@ const createRuntimeHandle = async (layer, backend, options = {}) => {
|
|
|
823
1095
|
};
|
|
824
1096
|
const cleanupCauses = [];
|
|
825
1097
|
try {
|
|
826
|
-
await
|
|
1098
|
+
await runRuntimeContext(contextStorage, makeRuntimeContext(resolver, rootScope, [], options.signal), () => rootScope.close(outcome));
|
|
827
1099
|
} catch (cause) {
|
|
828
1100
|
cleanupCauses.push(cause);
|
|
829
1101
|
}
|
|
@@ -842,7 +1114,7 @@ const createRuntimeHandle = async (layer, backend, options = {}) => {
|
|
|
842
1114
|
const cleanupCause = cleanupCauses.length === 1 ? cleanupCauses[0] : cleanupCauses.length > 1 ? new LayerDisposeError(cleanupCauses.flatMap(normalizeDisposeCauses)) : void 0;
|
|
843
1115
|
throw new LayerRegistrationError(current?.service, registrationCause, cleanupCause);
|
|
844
1116
|
}
|
|
845
|
-
return new RuntimeHandleImpl(backend, resolver, rootScope, options.onCleanupFailure);
|
|
1117
|
+
return new RuntimeHandleImpl(backend, resolver, rootScope, options.onCleanupFailure, contextStorage, options.signal, observers, layer.providers.map((provider) => provider.service));
|
|
846
1118
|
};
|
|
847
1119
|
//#endregion
|
|
848
1120
|
//#region src/runtime/runtime.ts
|
|
@@ -881,7 +1153,9 @@ var Runtime = class Runtime {
|
|
|
881
1153
|
static async make(layer, backendOrOptions, legacyOptions) {
|
|
882
1154
|
const { backend, options } = resolveRuntimeConfig(backendOrOptions, legacyOptions);
|
|
883
1155
|
const handle = await createRuntimeHandle(layer, backend, options);
|
|
884
|
-
|
|
1156
|
+
const runtime = new Runtime(handle);
|
|
1157
|
+
if (options.warmup) await runtime.warmup();
|
|
1158
|
+
return runtime;
|
|
885
1159
|
}
|
|
886
1160
|
static async run(layer, backendOrProgramOrOptions, programOrOptions, legacyOptions) {
|
|
887
1161
|
let program;
|
|
@@ -932,22 +1206,72 @@ var Runtime = class Runtime {
|
|
|
932
1206
|
if (executionFailed) throw executionFailure;
|
|
933
1207
|
return value;
|
|
934
1208
|
}
|
|
1209
|
+
static async use(layer, use, options) {
|
|
1210
|
+
const runtime = await Runtime.make(layer, options);
|
|
1211
|
+
let value;
|
|
1212
|
+
let executionFailed = false;
|
|
1213
|
+
let executionFailure;
|
|
1214
|
+
let programOutcome;
|
|
1215
|
+
try {
|
|
1216
|
+
value = await use(runtime);
|
|
1217
|
+
programOutcome = classifyRuntimeOutcome(value);
|
|
1218
|
+
} catch (cause) {
|
|
1219
|
+
executionFailed = true;
|
|
1220
|
+
executionFailure = cause;
|
|
1221
|
+
programOutcome = {
|
|
1222
|
+
status: "failure",
|
|
1223
|
+
cause
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
const outcome = programOutcome ?? {
|
|
1227
|
+
status: "failure",
|
|
1228
|
+
cause: executionFailure
|
|
1229
|
+
};
|
|
1230
|
+
try {
|
|
1231
|
+
await runtime.disposeWithOutcome(outcome);
|
|
1232
|
+
} catch (shutdownFailure) {
|
|
1233
|
+
if (!executionFailed && outcome.status === "success") throw shutdownFailure;
|
|
1234
|
+
}
|
|
1235
|
+
if (executionFailed) throw executionFailure;
|
|
1236
|
+
return value;
|
|
1237
|
+
}
|
|
1238
|
+
/** Resolve every Layer provider and dispose the Runtime if warmup fails. */
|
|
1239
|
+
async warmup() {
|
|
1240
|
+
try {
|
|
1241
|
+
await this.handle.warmup();
|
|
1242
|
+
} catch (cause) {
|
|
1243
|
+
try {
|
|
1244
|
+
await this.handle.dispose({
|
|
1245
|
+
status: "failure",
|
|
1246
|
+
cause
|
|
1247
|
+
});
|
|
1248
|
+
} catch {}
|
|
1249
|
+
throw cause;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
935
1252
|
/** Run one execution in this Runtime's child Scope. */
|
|
936
|
-
run(program) {
|
|
937
|
-
return this.handle.run(program);
|
|
1253
|
+
run(program, options) {
|
|
1254
|
+
return this.handle.run(program, options);
|
|
1255
|
+
}
|
|
1256
|
+
/** Run one execution with a Layer owned by that execution's child Scope. */
|
|
1257
|
+
runWith(layer, program, options) {
|
|
1258
|
+
return this.handle.runWith(layer, program, options);
|
|
938
1259
|
}
|
|
939
1260
|
runUnchecked(program) {
|
|
940
1261
|
return this.handle.run(program);
|
|
941
1262
|
}
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1263
|
+
dispose(optionsOrOutcome) {
|
|
1264
|
+
return this.handle.dispose(optionsOrOutcome);
|
|
1265
|
+
}
|
|
1266
|
+
/** Release Runtime-owned resources through JavaScript's async disposal protocol. */
|
|
1267
|
+
async [Symbol.asyncDispose]() {
|
|
1268
|
+
await this.dispose();
|
|
945
1269
|
}
|
|
946
1270
|
disposeWithOutcome(outcome) {
|
|
947
1271
|
return this.handle.dispose(outcome);
|
|
948
1272
|
}
|
|
949
1273
|
};
|
|
950
1274
|
//#endregion
|
|
951
|
-
export { CircularDependencyError, DuplicateServiceError, Effect, Layer, LayerDisposeError, LayerGeneratorYieldError, LayerRegistrationError, MapLayerBackend, Resource, ResourceNotDisposableError, ResourceReleaseFailure, Runtime, Scope, ScopeCloseError, ScopeClosedError, ScopeRuntimeNotConfiguredError, Service, ServiceAcquisitionError, ServiceNotFoundError, ServiceRuntime, ServiceRuntimeNotConfiguredError, ServiceTagCollisionError, pipe };
|
|
1275
|
+
export { CircularDependencyError, CurrentAbortSignal, DuplicateServiceError, Effect, Layer, LayerDisposeError, LayerGeneratorYieldError, LayerRegistrationError, MapLayerBackend, Resource, ResourceNotDisposableError, ResourceReleaseFailure, Runtime, RuntimeContextNotConfiguredError, Scope, ScopeCloseError, ScopeClosedError, ScopeRuntimeNotConfiguredError, Service, ServiceAcquisitionError, ServiceNotFoundError, ServiceRuntime, ServiceRuntimeNotConfiguredError, ServiceTagCollisionError, pipe };
|
|
952
1276
|
|
|
953
1277
|
//# sourceMappingURL=index.mjs.map
|