better-effect 0.6.1 → 0.7.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 +23 -1
- 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-BddlcJK6.d.mts} +14 -57
- package/dist/index-BddlcJK6.d.mts.map +1 -0
- package/dist/index.d.mts +9 -63
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +101 -32
- 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;
|
|
@@ -674,21 +703,18 @@ const classifyRuntimeOutcome = (value) => {
|
|
|
674
703
|
};
|
|
675
704
|
//#endregion
|
|
676
705
|
//#region src/layer/resolution.ts
|
|
677
|
-
const resolutionStorage = new AsyncLocalStorage();
|
|
678
706
|
const findCycleStart = (path, token) => path.findIndex((current) => current.serviceTag === token.serviceTag);
|
|
679
707
|
const shouldPreserve = (cause) => cause instanceof CircularDependencyError || cause instanceof ServiceAcquisitionError || cause instanceof ServiceNotFoundError || cause instanceof ServiceTagCollisionError;
|
|
680
708
|
/** Wrap a backend with Runtime-local resolution paths and acquisition errors. */
|
|
681
|
-
const createResolutionResolver = (resolver) => {
|
|
709
|
+
const createResolutionResolver = (resolver, storage = defaultRuntimeContextStorage) => {
|
|
682
710
|
const wrapped = { async resolve(token) {
|
|
683
|
-
const context =
|
|
684
|
-
const path = context?.resolver === wrapped ? context.
|
|
711
|
+
const context = getRuntimeContext(storage);
|
|
712
|
+
const path = context?.resolver === wrapped ? context.resolutionPath : [];
|
|
685
713
|
const cycleStart = findCycleStart(path, token);
|
|
686
714
|
if (cycleStart >= 0) throw new CircularDependencyError([...path.slice(cycleStart), token]);
|
|
687
715
|
const resolutionPath = [...path, token];
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
path: resolutionPath
|
|
691
|
-
}, async () => {
|
|
716
|
+
const nextContext = makeRuntimeContext(wrapped, context?.scope, resolutionPath, context?.signal);
|
|
717
|
+
return await runRuntimeContext(storage, nextContext, async () => {
|
|
692
718
|
try {
|
|
693
719
|
return await resolver.resolve(token);
|
|
694
720
|
} catch (cause) {
|
|
@@ -718,26 +744,30 @@ const notifyShutdownFailure = async (observer, diagnostic) => {
|
|
|
718
744
|
await observer(diagnostic);
|
|
719
745
|
} catch {}
|
|
720
746
|
};
|
|
721
|
-
const bindProviderToScope = (provider, rootScope) => ({
|
|
747
|
+
const bindProviderToScope = (provider, rootScope, contextStorage) => ({
|
|
722
748
|
service: provider.service,
|
|
723
749
|
acquire: () => ScopeRuntime.run(rootScope, async () => {
|
|
724
750
|
if (!provider.release) return await provider.acquire();
|
|
725
751
|
return await rootScope.acquire(() => provider.acquire(), (resource, outcome) => provider.release(resource, outcome));
|
|
726
|
-
})
|
|
752
|
+
}, contextStorage)
|
|
727
753
|
});
|
|
728
754
|
var RuntimeHandleImpl = class {
|
|
729
755
|
backend;
|
|
730
756
|
resolver;
|
|
731
757
|
rootScope;
|
|
732
758
|
onCleanupFailure;
|
|
759
|
+
contextStorage;
|
|
760
|
+
signal;
|
|
733
761
|
disposePromise;
|
|
734
762
|
executions = /* @__PURE__ */ new Set();
|
|
735
763
|
state = "active";
|
|
736
|
-
constructor(backend, resolver, rootScope, onCleanupFailure) {
|
|
764
|
+
constructor(backend, resolver, rootScope, onCleanupFailure, contextStorage, signal) {
|
|
737
765
|
this.backend = backend;
|
|
738
766
|
this.resolver = resolver;
|
|
739
767
|
this.rootScope = rootScope;
|
|
740
768
|
this.onCleanupFailure = onCleanupFailure;
|
|
769
|
+
this.contextStorage = contextStorage;
|
|
770
|
+
this.signal = signal;
|
|
741
771
|
}
|
|
742
772
|
run(program) {
|
|
743
773
|
this.assertActive();
|
|
@@ -770,7 +800,11 @@ var RuntimeHandleImpl = class {
|
|
|
770
800
|
classify: classifyRuntimeOutcome,
|
|
771
801
|
onCleanupFailure: this.onCleanupFailure
|
|
772
802
|
} : { classify: classifyRuntimeOutcome };
|
|
773
|
-
return runScoped(executionScope,
|
|
803
|
+
return runScoped(executionScope, program, {
|
|
804
|
+
...options,
|
|
805
|
+
contextStorage: this.contextStorage,
|
|
806
|
+
context: makeRuntimeContext(this.resolver, executionScope, [], this.signal)
|
|
807
|
+
});
|
|
774
808
|
}
|
|
775
809
|
dispose(outcome = SCOPE_SUCCESS) {
|
|
776
810
|
if (this.disposePromise) return this.disposePromise;
|
|
@@ -783,7 +817,7 @@ var RuntimeHandleImpl = class {
|
|
|
783
817
|
const failures = [];
|
|
784
818
|
await Promise.allSettled(executions);
|
|
785
819
|
try {
|
|
786
|
-
await
|
|
820
|
+
await runRuntimeContext(this.contextStorage, makeRuntimeContext(this.resolver, this.rootScope, [], this.signal), () => this.rootScope.close(outcome));
|
|
787
821
|
} catch (cause) {
|
|
788
822
|
failures.push(cause);
|
|
789
823
|
}
|
|
@@ -809,12 +843,14 @@ var RuntimeHandleImpl = class {
|
|
|
809
843
|
/** Build a Runtime handle for a complete Layer and register its providers. */
|
|
810
844
|
const createRuntimeHandle = async (layer, backend, options = {}) => {
|
|
811
845
|
const rootScope = Scope.make();
|
|
812
|
-
const
|
|
846
|
+
const contextStorage = options.contextStorage ?? defaultRuntimeContextStorage;
|
|
847
|
+
const resolver = createResolutionResolver(backend, contextStorage);
|
|
848
|
+
ScopeRuntime.bind(rootScope, contextStorage);
|
|
813
849
|
let current;
|
|
814
850
|
try {
|
|
815
851
|
for (const provider of layer.providers) {
|
|
816
852
|
current = provider;
|
|
817
|
-
await backend.register(bindProviderToScope(provider, rootScope));
|
|
853
|
+
await backend.register(bindProviderToScope(provider, rootScope, contextStorage));
|
|
818
854
|
}
|
|
819
855
|
} catch (registrationCause) {
|
|
820
856
|
const outcome = {
|
|
@@ -823,7 +859,7 @@ const createRuntimeHandle = async (layer, backend, options = {}) => {
|
|
|
823
859
|
};
|
|
824
860
|
const cleanupCauses = [];
|
|
825
861
|
try {
|
|
826
|
-
await
|
|
862
|
+
await runRuntimeContext(contextStorage, makeRuntimeContext(resolver, rootScope, [], options.signal), () => rootScope.close(outcome));
|
|
827
863
|
} catch (cause) {
|
|
828
864
|
cleanupCauses.push(cause);
|
|
829
865
|
}
|
|
@@ -842,7 +878,7 @@ const createRuntimeHandle = async (layer, backend, options = {}) => {
|
|
|
842
878
|
const cleanupCause = cleanupCauses.length === 1 ? cleanupCauses[0] : cleanupCauses.length > 1 ? new LayerDisposeError(cleanupCauses.flatMap(normalizeDisposeCauses)) : void 0;
|
|
843
879
|
throw new LayerRegistrationError(current?.service, registrationCause, cleanupCause);
|
|
844
880
|
}
|
|
845
|
-
return new RuntimeHandleImpl(backend, resolver, rootScope, options.onCleanupFailure);
|
|
881
|
+
return new RuntimeHandleImpl(backend, resolver, rootScope, options.onCleanupFailure, contextStorage, options.signal);
|
|
846
882
|
};
|
|
847
883
|
//#endregion
|
|
848
884
|
//#region src/runtime/runtime.ts
|
|
@@ -932,6 +968,35 @@ var Runtime = class Runtime {
|
|
|
932
968
|
if (executionFailed) throw executionFailure;
|
|
933
969
|
return value;
|
|
934
970
|
}
|
|
971
|
+
static async use(layer, use, options) {
|
|
972
|
+
const runtime = await Runtime.make(layer, options);
|
|
973
|
+
let value;
|
|
974
|
+
let executionFailed = false;
|
|
975
|
+
let executionFailure;
|
|
976
|
+
let programOutcome;
|
|
977
|
+
try {
|
|
978
|
+
value = await use(runtime);
|
|
979
|
+
programOutcome = classifyRuntimeOutcome(value);
|
|
980
|
+
} catch (cause) {
|
|
981
|
+
executionFailed = true;
|
|
982
|
+
executionFailure = cause;
|
|
983
|
+
programOutcome = {
|
|
984
|
+
status: "failure",
|
|
985
|
+
cause
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
const outcome = programOutcome ?? {
|
|
989
|
+
status: "failure",
|
|
990
|
+
cause: executionFailure
|
|
991
|
+
};
|
|
992
|
+
try {
|
|
993
|
+
await runtime.disposeWithOutcome(outcome);
|
|
994
|
+
} catch (shutdownFailure) {
|
|
995
|
+
if (!executionFailed && outcome.status === "success") throw shutdownFailure;
|
|
996
|
+
}
|
|
997
|
+
if (executionFailed) throw executionFailure;
|
|
998
|
+
return value;
|
|
999
|
+
}
|
|
935
1000
|
/** Run one execution in this Runtime's child Scope. */
|
|
936
1001
|
run(program) {
|
|
937
1002
|
return this.handle.run(program);
|
|
@@ -943,11 +1008,15 @@ var Runtime = class Runtime {
|
|
|
943
1008
|
dispose() {
|
|
944
1009
|
return this.handle.dispose();
|
|
945
1010
|
}
|
|
1011
|
+
/** Release Runtime-owned resources through JavaScript's async disposal protocol. */
|
|
1012
|
+
async [Symbol.asyncDispose]() {
|
|
1013
|
+
await this.dispose();
|
|
1014
|
+
}
|
|
946
1015
|
disposeWithOutcome(outcome) {
|
|
947
1016
|
return this.handle.dispose(outcome);
|
|
948
1017
|
}
|
|
949
1018
|
};
|
|
950
1019
|
//#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 };
|
|
1020
|
+
export { CircularDependencyError, DuplicateServiceError, Effect, Layer, LayerDisposeError, LayerGeneratorYieldError, LayerRegistrationError, MapLayerBackend, Resource, ResourceNotDisposableError, ResourceReleaseFailure, Runtime, RuntimeContextNotConfiguredError, Scope, ScopeCloseError, ScopeClosedError, ScopeRuntimeNotConfiguredError, Service, ServiceAcquisitionError, ServiceNotFoundError, ServiceRuntime, ServiceRuntimeNotConfiguredError, ServiceTagCollisionError, pipe };
|
|
952
1021
|
|
|
953
1022
|
//# sourceMappingURL=index.mjs.map
|