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/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
- return storage$1.run(resolver, program);
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
- const resolver = storage$1.getStore();
28
- if (!resolver) throw new ServiceRuntimeNotConfiguredError();
29
- return resolver;
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 storage = new AsyncLocalStorage();
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
- return storage.run(scope, program);
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
- const scope = storage.getStore();
266
- if (!scope) throw new ScopeRuntimeNotConfiguredError();
267
- return scope;
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
- value = await ScopeRuntime.run(scope, program);
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 = resolutionStorage.getStore();
684
- const path = context?.resolver === wrapped ? context.path : [];
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
- return await resolutionStorage.run({
689
- resolver: wrapped,
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, () => ServiceRuntime.run(this.resolver, program), options);
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 ServiceRuntime.run(this.resolver, () => this.rootScope.close(outcome));
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 resolver = createResolutionResolver(backend);
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 ServiceRuntime.run(resolver, () => rootScope.close(outcome));
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