better-effect 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,15 @@
1
- import { t as ItiLayerBackend } from "../iti-xXAb9-Qs.mjs";
2
- export { ItiLayerBackend };
1
+ import { r as LayerProvider, s as AnyServiceToken, t as LayerBackend } from "../backend-CNAls62W.mjs";
2
+ //#region src/adapters/iti.d.ts
3
+ declare class ItiLayerBackend implements LayerBackend {
4
+ private container;
5
+ private readonly keys;
6
+ private readonly registered;
7
+ private nextId;
8
+ private keyFor;
9
+ register(provider: LayerProvider): void;
10
+ resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>;
11
+ disposeAll(): Promise<void>;
12
+ }
13
+ //#endregion
14
+ export { ItiLayerBackend };
15
+ //# sourceMappingURL=iti.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iti.d.mts","names":[],"sources":["../../src/adapters/iti.ts"],"mappings":";;cAMa,2BAA2B;UAC9B;mBAES;mBAEA;UAET;UAEA;EAgBR,SAAS,UAAU;EAsBnB,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;EAUnF,cAAc"}
@@ -1,8 +1,10 @@
1
+ import { n as DuplicateServiceError, o as ServiceNotFoundError } from "../errors-CnvKqBpb.mjs";
1
2
  import { createContainer } from "iti";
2
3
  //#region src/adapters/iti.ts
3
4
  var ItiLayerBackend = class {
4
5
  container = createContainer();
5
6
  keys = /* @__PURE__ */ new WeakMap();
7
+ registered = /* @__PURE__ */ new WeakSet();
6
8
  nextId = 0;
7
9
  keyFor(token) {
8
10
  const existing = this.keys.get(token);
@@ -12,11 +14,15 @@ var ItiLayerBackend = class {
12
14
  return key;
13
15
  }
14
16
  register(provider) {
15
- const key = this.keyFor(provider.service);
17
+ const token = provider.service;
18
+ if (this.registered.has(token)) throw new DuplicateServiceError(token);
19
+ const key = this.keyFor(token);
16
20
  this.container = this.container.add({ [key]: provider.acquire });
17
21
  if (provider.release) this.container = this.container.addDisposer({ [key]: provider.release });
22
+ this.registered.add(token);
18
23
  }
19
24
  resolve(token) {
25
+ if (!this.registered.has(token)) throw new ServiceNotFoundError(token);
20
26
  const key = this.keyFor(token);
21
27
  return this.container.get(key);
22
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"iti.mjs","names":[],"sources":["../../src/adapters/iti.ts"],"sourcesContent":["import { createContainer } from 'iti'\n\nimport type { LayerBackend } from '../layer'\n\nimport type { AnyServiceToken, ServiceToken } from '../service'\nimport type { LayerProvider } from '../layer/types'\n\nexport class ItiLayerBackend implements LayerBackend {\n private container: any = createContainer()\n\n private readonly keys = new WeakMap<ServiceToken<any>, string>()\n\n private nextId = 0\n\n private keyFor(token: AnyServiceToken): string {\n const existing = this.keys.get(token)\n\n if (existing) {\n return existing\n }\n\n const name = (token as Function).name || 'Service'\n\n const key = `better-effect:${name}:${this.nextId++}`\n\n this.keys.set(token, key)\n\n return key\n }\n\n register(provider: LayerProvider): void {\n const key = this.keyFor(provider.service)\n\n this.container = this.container.add({\n [key]: provider.acquire\n })\n\n if (provider.release) {\n this.container = this.container.addDisposer({\n [key]: provider.release\n })\n }\n }\n\n resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>> {\n const key = this.keyFor(token)\n\n return this.container.get(key) as InstanceType<T> | PromiseLike<InstanceType<T>>\n }\n\n async disposeAll(): Promise<void> {\n await this.container.disposeAll()\n }\n}\n"],"mappings":";;AAOA,IAAa,kBAAb,MAAqD;CACnD,YAAyB,gBAAgB;CAEzC,uBAAwB,IAAI,QAAmC;CAE/D,SAAiB;CAEjB,OAAe,OAAgC;EAC7C,MAAM,WAAW,KAAK,KAAK,IAAI,KAAK;EAEpC,IAAI,UACF,OAAO;EAKT,MAAM,MAAM,iBAFE,MAAmB,QAAQ,UAEP,GAAG,KAAK;EAE1C,KAAK,KAAK,IAAI,OAAO,GAAG;EAExB,OAAO;CACT;CAEA,SAAS,UAA+B;EACtC,MAAM,MAAM,KAAK,OAAO,SAAS,OAAO;EAExC,KAAK,YAAY,KAAK,UAAU,IAAI,GACjC,MAAM,SAAS,QAClB,CAAC;EAED,IAAI,SAAS,SACX,KAAK,YAAY,KAAK,UAAU,YAAY,GACzC,MAAM,SAAS,QAClB,CAAC;CAEL;CAEA,QAAmC,OAA0D;EAC3F,MAAM,MAAM,KAAK,OAAO,KAAK;EAE7B,OAAO,KAAK,UAAU,IAAI,GAAG;CAC/B;CAEA,MAAM,aAA4B;EAChC,MAAM,KAAK,UAAU,WAAW;CAClC;AACF"}
1
+ {"version":3,"file":"iti.mjs","names":[],"sources":["../../src/adapters/iti.ts"],"sourcesContent":["import { createContainer } from 'iti'\n\nimport { DuplicateServiceError, type LayerBackend, type LayerProvider } from '../layer'\n\nimport { ServiceNotFoundError, type AnyServiceToken } from '../service'\n\nexport class ItiLayerBackend implements LayerBackend {\n private container: any = createContainer()\n\n private readonly keys = new WeakMap<AnyServiceToken, string>()\n\n private readonly registered = new WeakSet<AnyServiceToken>()\n\n private nextId = 0\n\n private keyFor(token: AnyServiceToken): string {\n const existing = this.keys.get(token)\n\n if (existing) {\n return existing\n }\n\n const name = token.name || 'Service'\n\n const key = `better-effect:${name}:${this.nextId++}`\n\n this.keys.set(token, key)\n\n return key\n }\n\n register(provider: LayerProvider): void {\n const token = provider.service\n\n if (this.registered.has(token)) {\n throw new DuplicateServiceError(token)\n }\n\n const key = this.keyFor(token)\n\n this.container = this.container.add({\n [key]: provider.acquire\n })\n\n if (provider.release) {\n this.container = this.container.addDisposer({\n [key]: provider.release\n })\n }\n\n this.registered.add(token)\n }\n\n resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>> {\n if (!this.registered.has(token)) {\n throw new ServiceNotFoundError(token)\n }\n\n const key = this.keyFor(token)\n\n return this.container.get(key) as InstanceType<T> | PromiseLike<InstanceType<T>>\n }\n\n async disposeAll(): Promise<void> {\n await this.container.disposeAll()\n }\n}\n"],"mappings":";;;AAMA,IAAa,kBAAb,MAAqD;CACnD,YAAyB,gBAAgB;CAEzC,uBAAwB,IAAI,QAAiC;CAE7D,6BAA8B,IAAI,QAAyB;CAE3D,SAAiB;CAEjB,OAAe,OAAgC;EAC7C,MAAM,WAAW,KAAK,KAAK,IAAI,KAAK;EAEpC,IAAI,UACF,OAAO;EAKT,MAAM,MAAM,iBAFC,MAAM,QAAQ,UAEO,GAAG,KAAK;EAE1C,KAAK,KAAK,IAAI,OAAO,GAAG;EAExB,OAAO;CACT;CAEA,SAAS,UAA+B;EACtC,MAAM,QAAQ,SAAS;EAEvB,IAAI,KAAK,WAAW,IAAI,KAAK,GAC3B,MAAM,IAAI,sBAAsB,KAAK;EAGvC,MAAM,MAAM,KAAK,OAAO,KAAK;EAE7B,KAAK,YAAY,KAAK,UAAU,IAAI,GACjC,MAAM,SAAS,QAClB,CAAC;EAED,IAAI,SAAS,SACX,KAAK,YAAY,KAAK,UAAU,YAAY,GACzC,MAAM,SAAS,QAClB,CAAC;EAGH,KAAK,WAAW,IAAI,KAAK;CAC3B;CAEA,QAAmC,OAA0D;EAC3F,IAAI,CAAC,KAAK,WAAW,IAAI,KAAK,GAC5B,MAAM,IAAI,qBAAqB,KAAK;EAGtC,MAAM,MAAM,KAAK,OAAO,KAAK;EAE7B,OAAO,KAAK,UAAU,IAAI,GAAG;CAC/B;CAEA,MAAM,aAA4B;EAChC,MAAM,KAAK,UAAU,WAAW;CAClC;AACF"}
@@ -2,44 +2,38 @@
2
2
  type ServiceToken<A = unknown> = (abstract new (...args: any[]) => A) & {
3
3
  readonly name: string;
4
4
  };
5
- type ServiceClass<A = unknown> = ServiceToken<A>;
6
5
  type AnyServiceToken = ServiceToken<any>;
6
+ type ServiceClass<A = unknown> = (new (...args: any[]) => A) & {
7
+ readonly name: string;
8
+ };
7
9
  type ServiceInstance<T extends AnyServiceToken> = InstanceType<T>;
8
10
  //#endregion
9
- //#region src/layer/types.d.ts
10
- type MaybePromise<T> = T | PromiseLike<T>;
11
- interface LayerProvider {
12
- readonly service: ServiceClass<any>;
13
- readonly acquire: () => MaybePromise<unknown>;
14
- readonly release?: (instance: unknown) => MaybePromise<void>;
15
- }
16
- //#endregion
17
11
  //#region src/service/runtime.d.ts
18
12
  interface ServiceResolver {
19
13
  resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>;
20
14
  }
21
15
  declare class ServiceRuntime {
22
- static configure(resolver: ServiceResolver): void;
16
+ static run<A>(resolver: ServiceResolver, program: () => A): A;
17
+ static current(): ServiceResolver;
23
18
  static resolve<T extends AnyServiceToken>(token: T): Promise<InstanceType<T>>;
24
- static reset(): void;
25
19
  }
26
20
  //#endregion
21
+ //#region src/utils/types.d.ts
22
+ type MaybePromise<T> = T | PromiseLike<T>;
23
+ //#endregion
24
+ //#region src/layer/types.d.ts
25
+ interface LayerProvider {
26
+ readonly service: ServiceClass<any>;
27
+ readonly acquire: () => MaybePromise<unknown>;
28
+ readonly release?: (instance: unknown) => MaybePromise<void>;
29
+ }
30
+ type LayerGenerator<S extends ServiceClass<any>> = () => AsyncGenerator<never, InstanceType<S>, unknown>;
31
+ //#endregion
27
32
  //#region src/layer/backend.d.ts
28
33
  interface LayerBackend extends ServiceResolver {
29
34
  register(provider: LayerProvider): void | PromiseLike<void>;
30
35
  disposeAll(): void | PromiseLike<void>;
31
36
  }
32
37
  //#endregion
33
- //#region src/adapters/iti.d.ts
34
- declare class ItiLayerBackend implements LayerBackend {
35
- private container;
36
- private readonly keys;
37
- private nextId;
38
- private keyFor;
39
- register(provider: LayerProvider): void;
40
- resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>;
41
- disposeAll(): Promise<void>;
42
- }
43
- //#endregion
44
- export { LayerProvider as a, ServiceClass as c, ServiceRuntime as i, ServiceInstance as l, LayerBackend as n, MaybePromise as o, ServiceResolver as r, AnyServiceToken as s, ItiLayerBackend as t };
45
- //# sourceMappingURL=iti-xXAb9-Qs.d.mts.map
38
+ export { ServiceResolver as a, ServiceClass as c, MaybePromise as i, ServiceInstance as l, LayerGenerator as n, ServiceRuntime as o, LayerProvider as r, AnyServiceToken as s, LayerBackend as t, ServiceToken as u };
39
+ //# sourceMappingURL=backend-CNAls62W.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend-CNAls62W.d.mts","names":[],"sources":["../src/service/types.ts","../src/service/runtime.ts","../src/utils/types.ts","../src/layer/types.ts","../src/layer/backend.ts"],"mappings":";KAAY,aAAa,iCAAiC,gBAAgB;WAC/D;;KAGC,kBAAkB;KAElB,aAAa,wBAAwB,gBAAgB;WACtD;;KAGC,gBAAgB,UAAU,mBAAmB,aAAa;;;UCJrD;EACf,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;;cAK9E;SACJ,IAAI,GAAG,UAAU,iBAAiB,eAAe,IAAI;SAIrD,WAAW;SAUL,QAAQ,UAAU,iBAAiB,OAAO,IAAI,QAAQ,aAAa;;;;KC3BtE,aAAa,KAAK,IAAI,YAAY;;;UCG7B;WACN,SAAS;WAET,eAAe;WAEf,WAAW,sBAAsB;;KAGhC,eAAe,UAAU,2BAA2B,sBAE9D,aAAa;;;UCTE,qBAAqB;EACpC,SAAS,UAAU,uBAAuB;EAC1C,qBAAqB"}
@@ -0,0 +1,63 @@
1
+ //#region src/service/errors.ts
2
+ var ServiceRuntimeNotConfiguredError = class extends Error {
3
+ constructor() {
4
+ super("No ServiceResolver is available in the current runtime context");
5
+ this.name = "ServiceRuntimeNotConfiguredError";
6
+ }
7
+ };
8
+ var ServiceNotFoundError = class extends Error {
9
+ service;
10
+ constructor(service) {
11
+ super(`Service "${service.name}" was not provided`);
12
+ this.service = service;
13
+ this.name = "ServiceNotFoundError";
14
+ }
15
+ };
16
+ //#endregion
17
+ //#region src/layer/errors.ts
18
+ var DuplicateServiceError = class extends Error {
19
+ service;
20
+ constructor(service) {
21
+ super(`Duplicate service "${service.name}"`);
22
+ this.service = service;
23
+ this.name = "DuplicateServiceError";
24
+ }
25
+ };
26
+ var LayerRegistrationError = class extends Error {
27
+ service;
28
+ registrationCause;
29
+ cleanupCause;
30
+ constructor(service, registrationCause, cleanupCause) {
31
+ super(service ? `Failed to register service "${service.name}"` : "Failed to build Layer", { cause: registrationCause });
32
+ this.service = service;
33
+ this.registrationCause = registrationCause;
34
+ this.cleanupCause = cleanupCause;
35
+ this.name = "LayerRegistrationError";
36
+ }
37
+ };
38
+ var LayerDisposeError = class extends Error {
39
+ causes;
40
+ constructor(causes) {
41
+ super(`Failed to dispose Layer (${causes.length} error${causes.length === 1 ? "" : "s"})`);
42
+ this.causes = causes;
43
+ this.name = "LayerDisposeError";
44
+ }
45
+ };
46
+ var LayerGeneratorYieldError = class extends Error {
47
+ service;
48
+ constructor(service) {
49
+ super(`Layer.gen("${service.name}") yielded an unsupported value`);
50
+ this.service = service;
51
+ this.name = "LayerGeneratorYieldError";
52
+ }
53
+ };
54
+ var BuiltLayerDisposedError = class extends Error {
55
+ constructor() {
56
+ super("Cannot run a program using a disposed Layer");
57
+ this.name = "BuiltLayerDisposedError";
58
+ }
59
+ };
60
+ //#endregion
61
+ export { LayerRegistrationError as a, LayerGeneratorYieldError as i, DuplicateServiceError as n, ServiceNotFoundError as o, LayerDisposeError as r, ServiceRuntimeNotConfiguredError as s, BuiltLayerDisposedError as t };
62
+
63
+ //# sourceMappingURL=errors-CnvKqBpb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-CnvKqBpb.mjs","names":[],"sources":["../src/service/errors.ts","../src/layer/errors.ts"],"sourcesContent":["import type { AnyServiceToken } from './types'\n\nexport class ServiceRuntimeNotConfiguredError extends Error {\n constructor() {\n super('No ServiceResolver is available in the current runtime context')\n\n this.name = 'ServiceRuntimeNotConfiguredError'\n }\n}\n\nexport class ServiceNotFoundError extends Error {\n constructor(readonly service: AnyServiceToken) {\n super(`Service \"${service.name}\" was not provided`)\n\n this.name = 'ServiceNotFoundError'\n }\n}\n","import type { ServiceClass } from '../service'\n\nexport class DuplicateServiceError extends Error {\n constructor(readonly service: ServiceClass<any>) {\n super(`Duplicate service \"${service.name}\"`)\n\n this.name = 'DuplicateServiceError'\n }\n}\n\nexport class LayerRegistrationError extends Error {\n constructor(\n readonly service: ServiceClass<any> | undefined,\n readonly registrationCause: unknown,\n readonly cleanupCause?: unknown\n ) {\n super(service ? `Failed to register service \"${service.name}\"` : 'Failed to build Layer', {\n cause: registrationCause\n })\n\n this.name = 'LayerRegistrationError'\n }\n}\n\nexport class LayerDisposeError extends Error {\n constructor(readonly causes: readonly unknown[]) {\n super(`Failed to dispose Layer (${causes.length} error${causes.length === 1 ? '' : 's'})`)\n\n this.name = 'LayerDisposeError'\n }\n}\n\nexport class LayerGeneratorYieldError extends Error {\n constructor(readonly service: ServiceClass<any>) {\n super(`Layer.gen(\"${service.name}\") yielded an unsupported value`)\n\n this.name = 'LayerGeneratorYieldError'\n }\n}\n\nexport class BuiltLayerDisposedError extends Error {\n constructor() {\n super('Cannot run a program using a disposed Layer')\n\n this.name = 'BuiltLayerDisposedError'\n }\n}\n"],"mappings":";AAEA,IAAa,mCAAb,cAAsD,MAAM;CAC1D,cAAc;EACZ,MAAM,gEAAgE;EAEtE,KAAK,OAAO;CACd;AACF;AAEA,IAAa,uBAAb,cAA0C,MAAM;CACzB;CAArB,YAAY,SAAmC;EAC7C,MAAM,YAAY,QAAQ,KAAK,mBAAmB;EAD/B,KAAA,UAAA;EAGnB,KAAK,OAAO;CACd;AACF;;;ACdA,IAAa,wBAAb,cAA2C,MAAM;CAC1B;CAArB,YAAY,SAAqC;EAC/C,MAAM,sBAAsB,QAAQ,KAAK,EAAE;EADxB,KAAA,UAAA;EAGnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,yBAAb,cAA4C,MAAM;CAErC;CACA;CACA;CAHX,YACE,SACA,mBACA,cACA;EACA,MAAM,UAAU,+BAA+B,QAAQ,KAAK,KAAK,yBAAyB,EACxF,OAAO,kBACT,CAAC;EANQ,KAAA,UAAA;EACA,KAAA,oBAAA;EACA,KAAA,eAAA;EAMT,KAAK,OAAO;CACd;AACF;AAEA,IAAa,oBAAb,cAAuC,MAAM;CACtB;CAArB,YAAY,QAAqC;EAC/C,MAAM,4BAA4B,OAAO,OAAO,QAAQ,OAAO,WAAW,IAAI,KAAK,IAAI,EAAE;EADtE,KAAA,SAAA;EAGnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,2BAAb,cAA8C,MAAM;CAC7B;CAArB,YAAY,SAAqC;EAC/C,MAAM,cAAc,QAAQ,KAAK,gCAAgC;EAD9C,KAAA,UAAA;EAGnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;EACZ,MAAM,6CAA6C;EAEnD,KAAK,OAAO;CACd;AACF"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,20 @@
1
- import { a as LayerProvider, c as ServiceClass, i as ServiceRuntime, l as ServiceInstance, n as LayerBackend, o as MaybePromise$1, r as ServiceResolver, s as AnyServiceToken, t as ItiLayerBackend } from "./iti-xXAb9-Qs.mjs";
1
+ import { a as ServiceResolver, c as ServiceClass, i as MaybePromise$1, l as ServiceInstance, n as LayerGenerator, o as ServiceRuntime, r as LayerProvider, s as AnyServiceToken, t as LayerBackend, u as ServiceToken } from "./backend-CNAls62W.mjs";
2
2
  import { Result, UnhandledException } from "better-result";
3
+ //#region src/service/service.d.ts
4
+ declare function Service<Self>(): {
5
+ new (): {};
6
+ [Symbol.asyncIterator](): AsyncGenerator<never, Self, unknown>;
7
+ };
8
+ //#endregion
9
+ //#region src/service/errors.d.ts
10
+ declare class ServiceRuntimeNotConfiguredError extends Error {
11
+ constructor();
12
+ }
13
+ declare class ServiceNotFoundError extends Error {
14
+ readonly service: AnyServiceToken;
15
+ constructor(service: AnyServiceToken);
16
+ }
17
+ //#endregion
3
18
  //#region src/layer/layer.d.ts
4
19
  declare class Layer {
5
20
  readonly providers: readonly LayerProvider[];
@@ -7,24 +22,41 @@ declare class Layer {
7
22
  static make<S extends ServiceClass<any>>(service: S, acquire: () => MaybePromise$1<InstanceType<S>>): Layer;
8
23
  static succeed<S extends ServiceClass<any>>(service: S, instance: InstanceType<S>): Layer;
9
24
  static scoped<S extends ServiceClass<any>>(service: S, acquire: () => MaybePromise$1<InstanceType<S>>, release: (instance: InstanceType<S>) => MaybePromise$1<void>): Layer;
25
+ static gen<S extends ServiceClass<any>>(service: S, factory: LayerGenerator<S>): Layer;
10
26
  static merge(...layers: readonly Layer[]): Layer;
11
27
  static override(base: Layer, ...overrides: readonly Layer[]): Layer;
12
28
  }
13
29
  //#endregion
14
30
  //#region src/layer/runtime.d.ts
15
31
  interface BuiltLayer {
32
+ readonly backend: LayerBackend;
33
+ run<A>(program: () => A): A;
16
34
  dispose(): Promise<void>;
17
35
  }
18
36
  declare const buildLayer: (layer: Layer, backend: LayerBackend) => Promise<BuiltLayer>;
19
- declare class RuntimeLayer {
20
- static build: typeof buildLayer;
21
- }
22
37
  //#endregion
23
- //#region src/service/service.d.ts
24
- declare function Service<Self>(): {
25
- new (): {};
26
- [Symbol.asyncIterator](): AsyncGenerator<never, Self, unknown>;
27
- };
38
+ //#region src/layer/errors.d.ts
39
+ declare class DuplicateServiceError extends Error {
40
+ readonly service: ServiceClass<any>;
41
+ constructor(service: ServiceClass<any>);
42
+ }
43
+ declare class LayerRegistrationError extends Error {
44
+ readonly service: ServiceClass<any> | undefined;
45
+ readonly registrationCause: unknown;
46
+ readonly cleanupCause?: unknown;
47
+ constructor(service: ServiceClass<any> | undefined, registrationCause: unknown, cleanupCause?: unknown);
48
+ }
49
+ declare class LayerDisposeError extends Error {
50
+ readonly causes: readonly unknown[];
51
+ constructor(causes: readonly unknown[]);
52
+ }
53
+ declare class LayerGeneratorYieldError extends Error {
54
+ readonly service: ServiceClass<any>;
55
+ constructor(service: ServiceClass<any>);
56
+ }
57
+ declare class BuiltLayerDisposedError extends Error {
58
+ constructor();
59
+ }
28
60
  //#endregion
29
61
  //#region src/resource/errors.d.ts
30
62
  declare const ResourceReleaseFailure_base: import("better-result").TaggedErrorClass<"ResourceReleaseFailure">;
@@ -42,38 +74,37 @@ type DisposableResource = {
42
74
  [Symbol.dispose]?: () => void;
43
75
  [Symbol.asyncDispose]?: () => MaybePromise<void>;
44
76
  };
77
+ type ReleaseFailureObserver = (failure: ResourceReleaseFailure) => MaybePromise<void>;
45
78
  type AcquireUseReleaseOptions<R, A, AcquireError, UseError> = {
46
- /**
47
- * Nome utilizado para identificar o recurso
48
- * em eventuais erros de release.
49
- */
50
79
  readonly name: string;
51
- /**
52
- * Adquire o recurso.
53
- */
54
80
  readonly acquire: () => AsyncResult<R, AcquireError>;
55
- /**
56
- * Executa a operação usando o recurso.
57
- */
58
81
  readonly use: (resource: R) => AsyncResult<A, UseError>;
82
+ readonly release?: (resource: R) => MaybePromise<ReleaseOutcome>;
59
83
  /**
60
- * Libera o recurso.
61
- *
62
- * Se não informado, Resource tentará usar:
84
+ * Observa falhas de cleanup sem
85
+ * alterar a precedência de erros.
63
86
  *
64
- * Symbol.asyncDispose
65
- * Symbol.dispose
66
- *
67
- * nessa ordem.
87
+ * Útil principalmente quando use
88
+ * e release falham juntos.
68
89
  */
69
- readonly release?: (resource: R) => MaybePromise<ReleaseOutcome>;
90
+ readonly onReleaseFailure?: ReleaseFailureObserver;
70
91
  };
71
92
  //#endregion
72
93
  //#region src/resource/resource.d.ts
73
- declare const acquireUseRelease: <R, A, AcquireError, UseError>({ name, acquire, use, release }: AcquireUseReleaseOptions<R, A, AcquireError, UseError>) => Promise<Result<A, AcquireError | UseError | UnhandledException | ResourceReleaseFailure>>;
94
+ declare const acquireUseRelease: <R, A, AcquireError, UseError>({ name, acquire, use, release, onReleaseFailure }: AcquireUseReleaseOptions<R, A, AcquireError, UseError>) => Promise<Result<A, AcquireError | UseError | UnhandledException | ResourceReleaseFailure>>;
74
95
  declare const Resource: {
75
96
  readonly acquireUseRelease: typeof acquireUseRelease;
76
97
  };
77
98
  //#endregion
78
- export { type AcquireUseReleaseOptions, type AnyServiceToken, type AsyncResult, type BuiltLayer, type DisposableResource, ItiLayerBackend, Layer, type LayerBackend, type MaybePromise, type ReleaseOutcome, Resource, ResourceReleaseFailure, RuntimeLayer, Service, type ServiceInstance, type ServiceResolver, ServiceRuntime, buildLayer };
99
+ //#region src/runtime/runtime.d.ts
100
+ declare class Runtime {
101
+ private readonly built;
102
+ private constructor();
103
+ static make(layer: Layer, backend: LayerBackend): Promise<Runtime>;
104
+ static run<A>(layer: Layer, backend: LayerBackend, program: () => A | PromiseLike<A>): Promise<Awaited<A>>;
105
+ run<A>(program: () => A): A;
106
+ dispose(): Promise<void>;
107
+ }
108
+ //#endregion
109
+ export { type AcquireUseReleaseOptions, type AnyServiceToken, type AsyncResult, type BuiltLayer, BuiltLayerDisposedError, type DisposableResource, DuplicateServiceError, Layer, type LayerBackend, LayerDisposeError, LayerGeneratorYieldError, LayerRegistrationError, type ReleaseFailureObserver, type ReleaseOutcome, Resource, ResourceReleaseFailure, Runtime, Service, type ServiceClass, type ServiceInstance, ServiceNotFoundError, type ServiceResolver, ServiceRuntime, ServiceRuntimeNotConfiguredError, type ServiceToken, buildLayer };
79
110
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/layer/layer.ts","../src/layer/runtime.ts","../src/service/service.ts","../src/resource/errors.ts","../src/resource/types.ts","../src/resource/resource.ts"],"mappings":";;;cAIa;WACkB,oBAAoB;UAA1C;SAEA,KAAK,UAAU,mBACpB,SAAS,GACT,eAAe,eAAa,aAAa,MACxC;SASI,QAAQ,UAAU,mBAAmB,SAAS,GAAG,UAAU,aAAa,KAAK;SAI7E,OAAO,UAAU,mBACtB,SAAS,GACT,eAAe,eAAa,aAAa,KACzC,UAAU,UAAU,aAAa,OAAO,uBACvC;SAWI,SAAS,iBAAiB,UAAU;SAkBpC,SAAS,MAAM,UAAU,oBAAoB,UAAU;;;;UCnD/C;EACf,WAAW;;cAGA,aAAU,OAAiB,OAAK,SAAW,iBAAe,QAAQ;cAkBlE;SACJ,cAAK;;;;iBCxBE,QAAQ;;GAGoB,OAAA,kBAAA,sBAAsB;;;;;cCLrD,+BAA+B;WACjC;WACA;WACA;;;;KCHC,aAAa,KAAK,IAAI,YAAY;KAElC,YAAY,GAAG,KAAK,aAAa,OAAW,GAAG;KAE/C,wBAAwB;KAExB;GACT,OAAO;GAEP,OAAO,sBAAsB;;KAGpB,yBAAyB,GAAG,GAAG,cAAc;;;;;WAK9C;;;;WAKA,eAAe,YAAY,GAAG;;;;WAK9B,MAAM,UAAU,MAAM,YAAY,GAAG;;;;;;;;;;;WAYrC,WAAW,UAAU,MAAM,aAAa;;;;cCjC7C,oBAAqB,GAAG,GAAG,cAAc,YAAQ,MAAA,SAAA,KAAA,WAKpD,yBAAyB,GAAG,GAAG,cAAc,cAAY,QAC1D,OAAW,GAAG,eAAe,WAAW,qBAAqB;cAmBlD;qCAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/service/service.ts","../src/service/errors.ts","../src/layer/layer.ts","../src/layer/runtime.ts","../src/layer/errors.ts","../src/resource/errors.ts","../src/resource/types.ts","../src/resource/resource.ts","../src/runtime/runtime.ts"],"mappings":";;;iBAIgB,QAAQ;;GAGoB,OAAA,kBAAA,sBAAsB;;;;cCLrD,yCAAyC;EACpD;;cAOW,6BAA6B;WACnB,SAAS;EAA9B,YAAqB,SAAS;;;;cCFnB;WACF,oBAAoB;UAEtB;SAIA,KAAK,UAAU,mBACpB,SAAS,GAET,eAAe,eAAa,aAAa,MACxC;SASI,QAAQ,UAAU,mBAAmB,SAAS,GAAG,UAAU,aAAa,KAAK;SAI7E,OAAO,UAAU,mBACtB,SAAS,GACT,eAAe,eAAa,aAAa,KACzC,UAAU,UAAU,aAAa,OAAO,uBACvC;SAWI,IAAI,UAAU,mBAAmB,SAAS,GAAG,SAAS,eAAe,KAAK;SAI1E,SAAS,iBAAiB,UAAU;SAkBpC,SAAS,MAAM,UAAU,oBAAoB,UAAU;;;;UC5D/C;WACN,SAAS;EAElB,IAAI,GAAG,eAAe,IAAI;EAE1B,WAAW;;cA+CA,aAAU,OAAiB,OAAK,SAAW,iBAAe,QAAQ;;;cC5DlE,8BAA8B;WACpB,SAAS;EAA9B,YAAqB,SAAS;;cAOnB,+BAA+B;WAE/B,SAAS;WACT;WACA;EAHX,YACW,SAAS,+BACT,4BACA;;cAUA,0BAA0B;WAChB;EAArB,YAAqB;;cAOV,iCAAiC;WACvB,SAAS;EAA9B,YAAqB,SAAS;;cAOnB,gCAAgC;EAC3C;;;;;cCvCW,+BAA+B;WACjC;WACA;WACA;;;;KCDC,aAAa,KAAK,IAAI,YAAY;KAElC,YAAY,GAAG,KAAK,aAAa,OAAW,GAAG;KAE/C,wBAAwB;KAExB;GACT,OAAO;GAEP,OAAO,sBAAsB;;KAGpB,0BAA0B,SAAS,2BAA2B;KAE9D,yBAAyB,GAAG,GAAG,cAAc;WAC9C;WAEA,eAAe,YAAY,GAAG;WAE9B,MAAM,UAAU,MAAM,YAAY,GAAG;WAErC,WAAW,UAAU,MAAM,aAAa;;;;;;;;WASxC,mBAAmB;;;;cC1BxB,oBAAqB,GAAG,GAAG,cAAc,YAAQ,MAAA,SAAA,KAAA,SAAA,oBAMpD,yBAAyB,GAAG,GAAG,cAAc,cAAY,QAC1D,OAAW,GAAG,eAAe,WAAW,qBAAqB;cAWlD;qCAAA;;;;cCxBA;mBAC0B;UAA9B;SAEM,KAAK,OAAO,OAAO,SAAS,eAAe,QAAQ;SAMnD,IAAI,GACf,OAAO,OACP,SAAS,cACT,eAAe,IAAI,YAAY,KAC9B,QAAQ,QAAQ;EAUnB,IAAI,GAAG,eAAe,IAAI;EAI1B,WAAW"}
package/dist/index.mjs CHANGED
@@ -1,17 +1,19 @@
1
- import { ItiLayerBackend } from "./adapters/iti.mjs";
1
+ import { a as LayerRegistrationError, i as LayerGeneratorYieldError, n as DuplicateServiceError, o as ServiceNotFoundError, r as LayerDisposeError, s as ServiceRuntimeNotConfiguredError, t as BuiltLayerDisposedError } from "./errors-CnvKqBpb.mjs";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
2
3
  import { Result, TaggedError } from "better-result";
3
4
  //#region src/service/runtime.ts
4
- let currentResolver;
5
- var ServiceRuntime = class {
6
- static configure(resolver) {
7
- currentResolver = resolver;
5
+ const storage = new AsyncLocalStorage();
6
+ var ServiceRuntime = class ServiceRuntime {
7
+ static run(resolver, program) {
8
+ return storage.run(resolver, program);
8
9
  }
9
- static async resolve(token) {
10
- if (!currentResolver) throw new Error("ServiceRuntime has not been configured");
11
- return await currentResolver.resolve(token);
10
+ static current() {
11
+ const resolver = storage.getStore();
12
+ if (!resolver) throw new ServiceRuntimeNotConfiguredError();
13
+ return resolver;
12
14
  }
13
- static reset() {
14
- currentResolver = void 0;
15
+ static async resolve(token) {
16
+ return await ServiceRuntime.current().resolve(token);
15
17
  }
16
18
  };
17
19
  //#endregion
@@ -24,11 +26,23 @@ function Service() {
24
26
  };
25
27
  }
26
28
  //#endregion
29
+ //#region src/layer/internal.ts
30
+ const runLayerGenerator = async (service, factory) => {
31
+ const iterator = factory();
32
+ const state = await iterator.next();
33
+ if (!state.done) try {
34
+ await iterator.return(void 0);
35
+ } finally {
36
+ throw new LayerGeneratorYieldError(service);
37
+ }
38
+ return state.value;
39
+ };
40
+ //#endregion
27
41
  //#region src/layer/layer.ts
28
42
  var Layer = class Layer {
29
43
  providers;
30
44
  constructor(providers) {
31
- this.providers = providers;
45
+ this.providers = Object.freeze([...providers]);
32
46
  }
33
47
  static make(service, acquire) {
34
48
  return new Layer([{
@@ -46,11 +60,14 @@ var Layer = class Layer {
46
60
  release: (instance) => release(instance)
47
61
  }]);
48
62
  }
63
+ static gen(service, factory) {
64
+ return Layer.make(service, () => runLayerGenerator(service, factory));
65
+ }
49
66
  static merge(...layers) {
50
67
  const providers = /* @__PURE__ */ new Map();
51
68
  for (const layer of layers) for (const provider of layer.providers) {
52
69
  const service = provider.service;
53
- if (providers.has(service)) throw new Error(`Duplicate service "${service.name}" in Layer.merge()`);
70
+ if (providers.has(service)) throw new DuplicateServiceError(service);
54
71
  providers.set(service, provider);
55
72
  }
56
73
  return new Layer([...providers.values()]);
@@ -64,19 +81,53 @@ var Layer = class Layer {
64
81
  };
65
82
  //#endregion
66
83
  //#region src/layer/runtime.ts
67
- const buildLayer = async (layer, backend) => {
68
- for (const provider of layer.providers) await backend.register(provider);
69
- ServiceRuntime.configure(backend);
70
- return { async dispose() {
84
+ const normalizeDisposeCauses = (cause) => {
85
+ if (cause instanceof AggregateError) return [...cause.errors];
86
+ return [cause];
87
+ };
88
+ var BuiltLayerImpl = class {
89
+ backend;
90
+ disposePromise;
91
+ disposed = false;
92
+ constructor(backend) {
93
+ this.backend = backend;
94
+ }
95
+ run(program) {
96
+ if (this.disposed || this.disposePromise) throw new BuiltLayerDisposedError();
97
+ return ServiceRuntime.run(this.backend, program);
98
+ }
99
+ dispose() {
100
+ if (this.disposePromise) return this.disposePromise;
101
+ this.disposePromise = this.performDispose();
102
+ return this.disposePromise;
103
+ }
104
+ async performDispose() {
71
105
  try {
72
- await backend.disposeAll();
106
+ await this.backend.disposeAll();
107
+ } catch (cause) {
108
+ throw new LayerDisposeError(normalizeDisposeCauses(cause));
73
109
  } finally {
74
- ServiceRuntime.reset();
110
+ this.disposed = true;
75
111
  }
76
- } };
112
+ }
77
113
  };
78
- var RuntimeLayer = class {
79
- static build = buildLayer;
114
+ const buildLayer = async (layer, backend) => {
115
+ let current;
116
+ try {
117
+ for (const provider of layer.providers) {
118
+ current = provider;
119
+ await backend.register(provider);
120
+ }
121
+ } catch (registrationCause) {
122
+ let cleanupCause;
123
+ try {
124
+ await backend.disposeAll();
125
+ } catch (cause) {
126
+ cleanupCause = cause;
127
+ }
128
+ throw new LayerRegistrationError(current?.service, registrationCause, cleanupCause);
129
+ }
130
+ return new BuiltLayerImpl(backend);
80
131
  };
81
132
  //#endregion
82
133
  //#region src/resource/errors.ts
@@ -92,10 +143,6 @@ const toReleaseFailure = (resource, cause) => new ResourceReleaseFailure({
92
143
  cause,
93
144
  message: `Failed to release resource: ${resource}`
94
145
  });
95
- /**
96
- * Executa uma operação que retorna Result e converte
97
- * exceptions/rejections inesperadas para UnhandledException.
98
- */
99
146
  const runResult = async (operation) => {
100
147
  return (await Result.tryPromise(() => Promise.resolve(operation()))).andThen((result) => result);
101
148
  };
@@ -109,31 +156,59 @@ const runRelease = async (name, resource, release) => {
109
156
  catch: (cause) => toReleaseFailure(name, cause)
110
157
  })).andThen((outcome) => normalizeReleaseOutcome(name, outcome));
111
158
  };
112
- /**
113
- * Precedência:
114
- *
115
- * 1. erro ocorrido durante use
116
- * 2. erro ocorrido durante release
117
- * 3. valor produzido por use
118
- */
119
- const combineUseAndRelease = (used, released) => used.andThen((value) => released.map(() => value));
159
+ const notifyReleaseFailure = async (observer, failure) => {
160
+ if (!observer) return;
161
+ try {
162
+ await observer(failure);
163
+ } catch {}
164
+ };
165
+ const combineUseAndRelease = async (used, released, onReleaseFailure) => {
166
+ if (Result.isError(used)) {
167
+ if (Result.isError(released)) await notifyReleaseFailure(onReleaseFailure, released.error);
168
+ return Result.err(used.error);
169
+ }
170
+ if (Result.isError(released)) {
171
+ await notifyReleaseFailure(onReleaseFailure, released.error);
172
+ return Result.err(released.error);
173
+ }
174
+ return Result.ok(used.value);
175
+ };
120
176
  //#endregion
121
177
  //#region src/resource/resource.ts
122
- const acquireUseRelease = ({ name, acquire, use, release = disposeResource }) => Result.gen(async function* () {
178
+ const acquireUseRelease = ({ name, acquire, use, release = disposeResource, onReleaseFailure }) => Result.gen(async function* () {
123
179
  const resource = yield* Result.await(runResult(acquire));
124
- /**
125
- * Não usamos yield* aqui.
126
- *
127
- * Se use retornar Err, o generator seria
128
- * encerrado imediatamente e o release
129
- * nunca seria executado.
130
- */
131
180
  const used = await runResult(() => use(resource));
132
181
  const released = await runRelease(name, resource, release);
133
- return combineUseAndRelease(used, released);
182
+ return await combineUseAndRelease(used, released, onReleaseFailure);
134
183
  });
135
184
  const Resource = { acquireUseRelease };
136
185
  //#endregion
137
- export { ItiLayerBackend, Layer, Resource, ResourceReleaseFailure, RuntimeLayer, Service, ServiceRuntime, buildLayer };
186
+ //#region src/runtime/runtime.ts
187
+ var Runtime = class Runtime {
188
+ built;
189
+ constructor(built) {
190
+ this.built = built;
191
+ }
192
+ static async make(layer, backend) {
193
+ const built = await buildLayer(layer, backend);
194
+ return new Runtime(built);
195
+ }
196
+ static async run(layer, backend, program) {
197
+ const runtime = await Runtime.make(layer, backend);
198
+ try {
199
+ return await runtime.run(program);
200
+ } finally {
201
+ await runtime.dispose();
202
+ }
203
+ }
204
+ run(program) {
205
+ return this.built.run(program);
206
+ }
207
+ dispose() {
208
+ return this.built.dispose();
209
+ }
210
+ };
211
+ //#endregion
212
+ export { BuiltLayerDisposedError, DuplicateServiceError, Layer, LayerDisposeError, LayerGeneratorYieldError, LayerRegistrationError, Resource, ResourceReleaseFailure, Runtime, Service, ServiceNotFoundError, ServiceRuntime, ServiceRuntimeNotConfiguredError, buildLayer };
138
213
 
139
214
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/service/runtime.ts","../src/service/service.ts","../src/layer/layer.ts","../src/layer/runtime.ts","../src/resource/errors.ts","../src/resource/internal.ts","../src/resource/resource.ts"],"sourcesContent":["import type { AnyServiceToken } from './types'\n\nexport interface ServiceResolver {\n resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>\n}\n\nlet currentResolver: ServiceResolver | undefined\n\nexport class ServiceRuntime {\n static configure(resolver: ServiceResolver): void {\n currentResolver = resolver\n }\n\n static async resolve<T extends AnyServiceToken>(token: T): Promise<InstanceType<T>> {\n if (!currentResolver) {\n throw new Error('ServiceRuntime has not been configured')\n }\n\n return await currentResolver.resolve(token)\n }\n\n static reset(): void {\n currentResolver = undefined\n }\n}\n","import { ServiceRuntime } from './runtime'\n\nimport type { ServiceToken } from './types'\n\nexport function Service<Self>() {\n return class {\n // oxlint-disable-next-line require-yield\n static async *[Symbol.asyncIterator](): AsyncGenerator<never, Self, unknown> {\n return await ServiceRuntime.resolve(this as unknown as ServiceToken<Self>)\n }\n }\n}\n","import type { ServiceClass } from '../service/types'\n\nimport type { LayerProvider, MaybePromise } from './types'\n\nexport class Layer {\n private constructor(readonly providers: readonly LayerProvider[]) {}\n\n static make<S extends ServiceClass<any>>(\n service: S,\n acquire: () => MaybePromise<InstanceType<S>>\n ): Layer {\n return new Layer([\n {\n service,\n acquire\n }\n ])\n }\n\n static succeed<S extends ServiceClass<any>>(service: S, instance: InstanceType<S>): Layer {\n return Layer.make(service, () => instance)\n }\n\n static scoped<S extends ServiceClass<any>>(\n service: S,\n acquire: () => MaybePromise<InstanceType<S>>,\n release: (instance: InstanceType<S>) => MaybePromise<void>\n ): Layer {\n return new Layer([\n {\n service,\n acquire,\n\n release: (instance) => release(instance as InstanceType<S>)\n }\n ])\n }\n\n static merge(...layers: readonly Layer[]): Layer {\n const providers = new Map<ServiceClass<any>, LayerProvider>()\n\n for (const layer of layers) {\n for (const provider of layer.providers) {\n const service = provider.service\n\n if (providers.has(service)) {\n throw new Error(`Duplicate service \"${service.name}\" in Layer.merge()`)\n }\n\n providers.set(service, provider)\n }\n }\n\n return new Layer([...providers.values()])\n }\n\n static override(base: Layer, ...overrides: readonly Layer[]): Layer {\n const providers = new Map<ServiceClass<any>, LayerProvider>()\n\n for (const provider of base.providers) {\n providers.set(provider.service, provider)\n }\n\n for (const layer of overrides) {\n for (const provider of layer.providers) {\n providers.set(provider.service, provider)\n }\n }\n\n return new Layer([...providers.values()])\n }\n}\n","import { ServiceRuntime } from '../service/runtime'\n\nimport type { LayerBackend } from './backend'\nimport type { Layer } from './layer'\n\nexport interface BuiltLayer {\n dispose(): Promise<void>\n}\n\nexport const buildLayer = async (layer: Layer, backend: LayerBackend): Promise<BuiltLayer> => {\n for (const provider of layer.providers) {\n await backend.register(provider)\n }\n\n ServiceRuntime.configure(backend)\n\n return {\n async dispose() {\n try {\n await backend.disposeAll()\n } finally {\n ServiceRuntime.reset()\n }\n }\n }\n}\n\nexport class RuntimeLayer {\n static build = buildLayer\n}\n","import { TaggedError } from 'better-result'\n\nexport class ResourceReleaseFailure extends TaggedError('ResourceReleaseFailure')<{\n readonly resource: string\n readonly cause: unknown\n readonly message: string\n}> {}\n","import { Result, type Result as ResultType, type UnhandledException } from 'better-result'\n\nimport { ResourceReleaseFailure } from './errors'\n\nimport type { AsyncResult, DisposableResource, MaybePromise, ReleaseOutcome } from './types'\n\nexport const disposeResource = (resource: unknown): MaybePromise<void> => {\n const candidate = Object(resource) as DisposableResource\n\n const dispose = candidate[Symbol.asyncDispose] ?? candidate[Symbol.dispose]\n\n return dispose?.call(candidate)\n}\n\nconst toReleaseFailure = (resource: string, cause: unknown): ResourceReleaseFailure =>\n new ResourceReleaseFailure({\n resource,\n cause,\n message: `Failed to release resource: ${resource}`\n })\n\n/**\n * Executa uma operação que retorna Result e converte\n * exceptions/rejections inesperadas para UnhandledException.\n */\nexport const runResult = async <T, E>(\n operation: () => AsyncResult<T, E>\n): Promise<ResultType<T, E | UnhandledException>> => {\n const execution = await Result.tryPromise(() => Promise.resolve(operation()))\n\n return execution.andThen((result) => result)\n}\n\nconst normalizeReleaseOutcome = (\n name: string,\n outcome: ReleaseOutcome\n): ResultType<void, ResourceReleaseFailure> => {\n if (outcome === undefined) {\n return Result.ok()\n }\n\n return outcome.mapError((cause) => toReleaseFailure(name, cause))\n}\n\nexport const runRelease = async <R>(\n name: string,\n resource: R,\n release: (resource: R) => MaybePromise<ReleaseOutcome>\n): Promise<ResultType<void, ResourceReleaseFailure>> => {\n const execution = await Result.tryPromise({\n try: () => Promise.resolve(release(resource)),\n\n catch: (cause) => toReleaseFailure(name, cause)\n })\n\n return execution.andThen((outcome) => normalizeReleaseOutcome(name, outcome))\n}\n\n/**\n * Precedência:\n *\n * 1. erro ocorrido durante use\n * 2. erro ocorrido durante release\n * 3. valor produzido por use\n */\nexport const combineUseAndRelease = <A, E>(\n used: ResultType<A, E>,\n released: ResultType<void, ResourceReleaseFailure>\n): ResultType<A, E | ResourceReleaseFailure> => used.andThen((value) => released.map(() => value))\n","import { Result, type Result as ResultType, type UnhandledException } from 'better-result'\n\nimport { ResourceReleaseFailure } from './errors'\n\nimport { combineUseAndRelease, disposeResource, runRelease, runResult } from './internal'\n\nimport type { AcquireUseReleaseOptions } from './types'\n\nconst acquireUseRelease = <R, A, AcquireError, UseError>({\n name,\n acquire,\n use,\n release = disposeResource\n}: AcquireUseReleaseOptions<R, A, AcquireError, UseError>): Promise<\n ResultType<A, AcquireError | UseError | UnhandledException | ResourceReleaseFailure>\n> =>\n Result.gen(async function* () {\n const resource = yield* Result.await(runResult(acquire))\n\n /**\n * Não usamos yield* aqui.\n *\n * Se use retornar Err, o generator seria\n * encerrado imediatamente e o release\n * nunca seria executado.\n */\n const used = await runResult(() => use(resource))\n\n const released = await runRelease(name, resource, release)\n\n return combineUseAndRelease(used, released)\n })\n\nexport const Resource = {\n acquireUseRelease\n} as const\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/service/runtime.ts","../src/service/service.ts","../src/layer/internal.ts","../src/layer/layer.ts","../src/layer/runtime.ts","../src/resource/errors.ts","../src/resource/internal.ts","../src/resource/resource.ts","../src/runtime/runtime.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\n\nimport { ServiceRuntimeNotConfiguredError } from './errors'\n\nimport type { AnyServiceToken } from './types'\n\nexport interface ServiceResolver {\n resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>\n}\n\nconst storage = new AsyncLocalStorage<ServiceResolver>()\n\nexport class ServiceRuntime {\n static run<A>(resolver: ServiceResolver, program: () => A): A {\n return storage.run(resolver, program)\n }\n\n static current(): ServiceResolver {\n const resolver = storage.getStore()\n\n if (!resolver) {\n throw new ServiceRuntimeNotConfiguredError()\n }\n\n return resolver\n }\n\n static async resolve<T extends AnyServiceToken>(token: T): Promise<InstanceType<T>> {\n const resolver = ServiceRuntime.current()\n\n return await resolver.resolve(token)\n }\n}\n","import { ServiceRuntime } from './runtime'\n\nimport type { ServiceToken } from './types'\n\nexport function Service<Self>() {\n return class {\n // oxlint-disable-next-line require-yield\n static async *[Symbol.asyncIterator](): AsyncGenerator<never, Self, unknown> {\n return await ServiceRuntime.resolve(this as unknown as ServiceToken<Self>)\n }\n }\n}\n","import { LayerGeneratorYieldError } from './errors'\n\nimport type { ServiceClass } from '../service'\n\nimport type { LayerGenerator } from './types'\n\nexport const runLayerGenerator = async <S extends ServiceClass<any>>(\n service: S,\n factory: LayerGenerator<S>\n): Promise<InstanceType<S>> => {\n const iterator = factory()\n\n const state = await iterator.next()\n\n if (!state.done) {\n try {\n await iterator.return(undefined as InstanceType<S>)\n } finally {\n // oxlint-disable-next-line no-unsafe-finally\n throw new LayerGeneratorYieldError(service)\n }\n }\n\n return state.value\n}\n","import type { ServiceClass } from '../service'\nimport type { MaybePromise } from '../utils/types'\n\nimport { DuplicateServiceError } from './errors'\n\nimport { runLayerGenerator } from './internal'\n\nimport type { LayerGenerator, LayerProvider } from './types'\n\nexport class Layer {\n readonly providers: readonly LayerProvider[]\n\n private constructor(providers: readonly LayerProvider[]) {\n this.providers = Object.freeze([...providers])\n }\n\n static make<S extends ServiceClass<any>>(\n service: S,\n\n acquire: () => MaybePromise<InstanceType<S>>\n ): Layer {\n return new Layer([\n {\n service,\n acquire\n }\n ])\n }\n\n static succeed<S extends ServiceClass<any>>(service: S, instance: InstanceType<S>): Layer {\n return Layer.make(service, () => instance)\n }\n\n static scoped<S extends ServiceClass<any>>(\n service: S,\n acquire: () => MaybePromise<InstanceType<S>>,\n release: (instance: InstanceType<S>) => MaybePromise<void>\n ): Layer {\n return new Layer([\n {\n service,\n acquire,\n\n release: (instance) => release(instance as InstanceType<S>)\n }\n ])\n }\n\n static gen<S extends ServiceClass<any>>(service: S, factory: LayerGenerator<S>): Layer {\n return Layer.make(service, () => runLayerGenerator(service, factory))\n }\n\n static merge(...layers: readonly Layer[]): Layer {\n const providers = new Map<ServiceClass<any>, LayerProvider>()\n\n for (const layer of layers) {\n for (const provider of layer.providers) {\n const service = provider.service\n\n if (providers.has(service)) {\n throw new DuplicateServiceError(service)\n }\n\n providers.set(service, provider)\n }\n }\n\n return new Layer([...providers.values()])\n }\n\n static override(base: Layer, ...overrides: readonly Layer[]): Layer {\n const providers = new Map<ServiceClass<any>, LayerProvider>()\n\n for (const provider of base.providers) {\n providers.set(provider.service, provider)\n }\n\n for (const layer of overrides) {\n for (const provider of layer.providers) {\n providers.set(provider.service, provider)\n }\n }\n\n return new Layer([...providers.values()])\n }\n}\n","import { ServiceRuntime } from '../service'\n\nimport { BuiltLayerDisposedError, LayerDisposeError, LayerRegistrationError } from './errors'\n\nimport type { LayerBackend } from './backend'\n\nimport type { Layer } from './layer'\n\nimport type { LayerProvider } from './types'\n\nexport interface BuiltLayer {\n readonly backend: LayerBackend\n\n run<A>(program: () => A): A\n\n dispose(): Promise<void>\n}\n\nconst normalizeDisposeCauses = (cause: unknown): readonly unknown[] => {\n if (cause instanceof AggregateError) {\n return [...cause.errors]\n }\n\n return [cause]\n}\n\nclass BuiltLayerImpl implements BuiltLayer {\n private disposePromise: Promise<void> | undefined\n\n private disposed = false\n\n constructor(readonly backend: LayerBackend) {}\n\n run<A>(program: () => A): A {\n if (this.disposed || this.disposePromise) {\n throw new BuiltLayerDisposedError()\n }\n\n return ServiceRuntime.run(this.backend, program)\n }\n\n dispose(): Promise<void> {\n if (this.disposePromise) {\n return this.disposePromise\n }\n\n this.disposePromise = this.performDispose()\n\n return this.disposePromise\n }\n\n private async performDispose(): Promise<void> {\n try {\n await this.backend.disposeAll()\n } catch (cause) {\n throw new LayerDisposeError(normalizeDisposeCauses(cause))\n } finally {\n this.disposed = true\n }\n }\n}\n\nexport const buildLayer = async (layer: Layer, backend: LayerBackend): Promise<BuiltLayer> => {\n let current: LayerProvider | undefined\n\n try {\n for (const provider of layer.providers) {\n current = provider\n\n await backend.register(provider)\n }\n } catch (registrationCause) {\n let cleanupCause: unknown\n\n try {\n await backend.disposeAll()\n } catch (cause) {\n cleanupCause = cause\n }\n\n throw new LayerRegistrationError(current?.service, registrationCause, cleanupCause)\n }\n\n return new BuiltLayerImpl(backend)\n}\n","import { TaggedError } from 'better-result'\n\nexport class ResourceReleaseFailure extends TaggedError('ResourceReleaseFailure')<{\n readonly resource: string\n readonly cause: unknown\n readonly message: string\n}> {}\n","import { Result, type Result as ResultType, type UnhandledException } from 'better-result'\n\nimport { ResourceReleaseFailure } from './errors'\n\nimport type {\n AsyncResult,\n DisposableResource,\n MaybePromise,\n ReleaseFailureObserver,\n ReleaseOutcome\n} from './types'\n\nexport const disposeResource = (resource: unknown): MaybePromise<void> => {\n const candidate = Object(resource) as DisposableResource\n\n const dispose = candidate[Symbol.asyncDispose] ?? candidate[Symbol.dispose]\n\n return dispose?.call(candidate)\n}\n\nconst toReleaseFailure = (resource: string, cause: unknown): ResourceReleaseFailure =>\n new ResourceReleaseFailure({\n resource,\n cause,\n message: `Failed to release resource: ${resource}`\n })\n\nexport const runResult = async <T, E>(\n operation: () => AsyncResult<T, E>\n): Promise<ResultType<T, E | UnhandledException>> => {\n const execution = await Result.tryPromise(() => Promise.resolve(operation()))\n\n return execution.andThen((result) => result)\n}\n\nconst normalizeReleaseOutcome = (\n name: string,\n outcome: ReleaseOutcome\n): ResultType<void, ResourceReleaseFailure> => {\n if (outcome === undefined) {\n return Result.ok()\n }\n\n return outcome.mapError((cause) => toReleaseFailure(name, cause))\n}\n\nexport const runRelease = async <R>(\n name: string,\n resource: R,\n release: (resource: R) => MaybePromise<ReleaseOutcome>\n): Promise<ResultType<void, ResourceReleaseFailure>> => {\n const execution = await Result.tryPromise({\n try: () => Promise.resolve(release(resource)),\n catch: (cause) => toReleaseFailure(name, cause)\n })\n\n return execution.andThen((outcome) => normalizeReleaseOutcome(name, outcome))\n}\n\nconst notifyReleaseFailure = async (\n observer: ReleaseFailureObserver | undefined,\n\n failure: ResourceReleaseFailure\n): Promise<void> => {\n if (!observer) {\n return\n }\n\n try {\n await observer(failure)\n } catch {\n /*\n * Diagnostics must never replace\n * the actual operation error.\n */\n }\n}\n\nexport const combineUseAndRelease = async <A, E>(\n used: ResultType<A, E>,\n\n released: ResultType<void, ResourceReleaseFailure>,\n\n onReleaseFailure?: ReleaseFailureObserver\n): Promise<ResultType<A, E | ResourceReleaseFailure>> => {\n if (Result.isError(used)) {\n if (Result.isError(released)) {\n await notifyReleaseFailure(onReleaseFailure, released.error)\n }\n\n return Result.err<A, E | ResourceReleaseFailure>(used.error)\n }\n\n if (Result.isError(released)) {\n await notifyReleaseFailure(onReleaseFailure, released.error)\n\n return Result.err<A, E | ResourceReleaseFailure>(released.error)\n }\n\n return Result.ok<A, E | ResourceReleaseFailure>(used.value)\n}\n","import { Result, type Result as ResultType, type UnhandledException } from 'better-result'\n\nimport { ResourceReleaseFailure } from './errors'\n\nimport { combineUseAndRelease, disposeResource, runRelease, runResult } from './internal'\n\nimport type { AcquireUseReleaseOptions } from './types'\n\nconst acquireUseRelease = <R, A, AcquireError, UseError>({\n name,\n acquire,\n use,\n release = disposeResource,\n onReleaseFailure\n}: AcquireUseReleaseOptions<R, A, AcquireError, UseError>): Promise<\n ResultType<A, AcquireError | UseError | UnhandledException | ResourceReleaseFailure>\n> =>\n Result.gen(async function* () {\n const resource = yield* Result.await(runResult(acquire))\n const used = await runResult(() => use(resource))\n const released = await runRelease(name, resource, release)\n const result = await combineUseAndRelease(used, released, onReleaseFailure)\n\n return result\n })\n\nexport const Resource = {\n acquireUseRelease\n} as const\n","import { buildLayer, type BuiltLayer, type Layer, type LayerBackend } from '../layer'\n\nexport class Runtime {\n private constructor(private readonly built: BuiltLayer) {}\n\n static async make(layer: Layer, backend: LayerBackend): Promise<Runtime> {\n const built = await buildLayer(layer, backend)\n\n return new Runtime(built)\n }\n\n static async run<A>(\n layer: Layer,\n backend: LayerBackend,\n program: () => A | PromiseLike<A>\n ): Promise<Awaited<A>> {\n const runtime = await Runtime.make(layer, backend)\n\n try {\n return await runtime.run(program)\n } finally {\n await runtime.dispose()\n }\n }\n\n run<A>(program: () => A): A {\n return this.built.run(program)\n }\n\n dispose(): Promise<void> {\n return this.built.dispose()\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { r as LayerProvider, s as AnyServiceToken, t as LayerBackend } from "./backend-CNAls62W.mjs";
2
+ //#region src/testing/memory-layer-backend.d.ts
3
+ declare class MemoryLayerBackend implements LayerBackend {
4
+ private readonly providers;
5
+ private readonly instances;
6
+ private readonly pending;
7
+ private readonly resolutionOrder;
8
+ register(provider: LayerProvider): void;
9
+ resolve<T extends AnyServiceToken>(token: T): Promise<InstanceType<T>>;
10
+ disposeAll(): Promise<void>;
11
+ }
12
+ //#endregion
13
+ export { MemoryLayerBackend };
14
+ //# sourceMappingURL=testing.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.mts","names":[],"sources":["../src/testing/memory-layer-backend.ts"],"mappings":";;cAIa,8BAA8B;mBACxB;mBAEA;mBAEA;mBAEA;EAEjB,SAAS,UAAU;EAQb,QAAQ,UAAU,iBAAiB,OAAO,IAAI,QAAQ,aAAa;EAkCnE,cAAc"}
@@ -0,0 +1,52 @@
1
+ import { n as DuplicateServiceError, o as ServiceNotFoundError } from "./errors-CnvKqBpb.mjs";
2
+ //#region src/testing/memory-layer-backend.ts
3
+ var MemoryLayerBackend = class {
4
+ providers = /* @__PURE__ */ new Map();
5
+ instances = /* @__PURE__ */ new Map();
6
+ pending = /* @__PURE__ */ new Map();
7
+ resolutionOrder = [];
8
+ register(provider) {
9
+ if (this.providers.has(provider.service)) throw new DuplicateServiceError(provider.service);
10
+ this.providers.set(provider.service, provider);
11
+ }
12
+ async resolve(token) {
13
+ if (this.instances.has(token)) return this.instances.get(token);
14
+ const pending = this.pending.get(token);
15
+ if (pending) return await pending;
16
+ const provider = this.providers.get(token);
17
+ if (!provider) throw new ServiceNotFoundError(token);
18
+ const acquisition = Promise.resolve(provider.acquire()).then((instance) => {
19
+ this.instances.set(token, instance);
20
+ this.resolutionOrder.push(token);
21
+ return instance;
22
+ }).finally(() => {
23
+ this.pending.delete(token);
24
+ });
25
+ this.pending.set(token, acquisition);
26
+ return await acquisition;
27
+ }
28
+ async disposeAll() {
29
+ if (this.pending.size > 0) await Promise.allSettled(this.pending.values());
30
+ const errors = [];
31
+ for (let index = this.resolutionOrder.length - 1; index >= 0; index--) {
32
+ const token = this.resolutionOrder[index];
33
+ if (!token) continue;
34
+ const instance = this.instances.get(token);
35
+ const provider = this.providers.get(token);
36
+ if (instance === void 0 || !provider?.release) continue;
37
+ try {
38
+ await provider.release(instance);
39
+ } catch (cause) {
40
+ errors.push(cause);
41
+ }
42
+ }
43
+ this.instances.clear();
44
+ this.pending.clear();
45
+ this.resolutionOrder.splice(0);
46
+ if (errors.length > 0) throw new AggregateError(errors, "Failed to dispose one or more services");
47
+ }
48
+ };
49
+ //#endregion
50
+ export { MemoryLayerBackend };
51
+
52
+ //# sourceMappingURL=testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../src/testing/memory-layer-backend.ts"],"sourcesContent":["import { DuplicateServiceError, type LayerBackend, type LayerProvider } from '../layer'\n\nimport { ServiceNotFoundError, type AnyServiceToken } from '../service'\n\nexport class MemoryLayerBackend implements LayerBackend {\n private readonly providers = new Map<AnyServiceToken, LayerProvider>()\n\n private readonly instances = new Map<AnyServiceToken, unknown>()\n\n private readonly pending = new Map<AnyServiceToken, Promise<unknown>>()\n\n private readonly resolutionOrder: AnyServiceToken[] = []\n\n register(provider: LayerProvider): void {\n if (this.providers.has(provider.service)) {\n throw new DuplicateServiceError(provider.service)\n }\n\n this.providers.set(provider.service, provider)\n }\n\n async resolve<T extends AnyServiceToken>(token: T): Promise<InstanceType<T>> {\n if (this.instances.has(token)) {\n return this.instances.get(token) as InstanceType<T>\n }\n\n const pending = this.pending.get(token)\n\n if (pending) {\n return (await pending) as InstanceType<T>\n }\n\n const provider = this.providers.get(token)\n\n if (!provider) {\n throw new ServiceNotFoundError(token)\n }\n\n const acquisition = Promise.resolve(provider.acquire())\n .then((instance) => {\n this.instances.set(token, instance)\n\n this.resolutionOrder.push(token)\n\n return instance\n })\n .finally(() => {\n this.pending.delete(token)\n })\n\n this.pending.set(token, acquisition)\n\n return (await acquisition) as InstanceType<T>\n }\n\n async disposeAll(): Promise<void> {\n if (this.pending.size > 0) {\n await Promise.allSettled(this.pending.values())\n }\n\n const errors: unknown[] = []\n\n for (let index = this.resolutionOrder.length - 1; index >= 0; index--) {\n const token = this.resolutionOrder[index]\n\n if (!token) {\n continue\n }\n\n const instance = this.instances.get(token)\n\n const provider = this.providers.get(token)\n\n if (instance === undefined || !provider?.release) {\n continue\n }\n\n try {\n await provider.release(instance)\n } catch (cause) {\n errors.push(cause)\n }\n }\n\n this.instances.clear()\n this.pending.clear()\n\n this.resolutionOrder.splice(0)\n\n if (errors.length > 0) {\n throw new AggregateError(errors, 'Failed to dispose one or more services')\n }\n }\n}\n"],"mappings":";;AAIA,IAAa,qBAAb,MAAwD;CACtD,4BAA6B,IAAI,IAAoC;CAErE,4BAA6B,IAAI,IAA8B;CAE/D,0BAA2B,IAAI,IAAuC;CAEtE,kBAAsD,CAAC;CAEvD,SAAS,UAA+B;EACtC,IAAI,KAAK,UAAU,IAAI,SAAS,OAAO,GACrC,MAAM,IAAI,sBAAsB,SAAS,OAAO;EAGlD,KAAK,UAAU,IAAI,SAAS,SAAS,QAAQ;CAC/C;CAEA,MAAM,QAAmC,OAAoC;EAC3E,IAAI,KAAK,UAAU,IAAI,KAAK,GAC1B,OAAO,KAAK,UAAU,IAAI,KAAK;EAGjC,MAAM,UAAU,KAAK,QAAQ,IAAI,KAAK;EAEtC,IAAI,SACF,OAAQ,MAAM;EAGhB,MAAM,WAAW,KAAK,UAAU,IAAI,KAAK;EAEzC,IAAI,CAAC,UACH,MAAM,IAAI,qBAAqB,KAAK;EAGtC,MAAM,cAAc,QAAQ,QAAQ,SAAS,QAAQ,CAAC,CAAC,CACpD,MAAM,aAAa;GAClB,KAAK,UAAU,IAAI,OAAO,QAAQ;GAElC,KAAK,gBAAgB,KAAK,KAAK;GAE/B,OAAO;EACT,CAAC,CAAC,CACD,cAAc;GACb,KAAK,QAAQ,OAAO,KAAK;EAC3B,CAAC;EAEH,KAAK,QAAQ,IAAI,OAAO,WAAW;EAEnC,OAAQ,MAAM;CAChB;CAEA,MAAM,aAA4B;EAChC,IAAI,KAAK,QAAQ,OAAO,GACtB,MAAM,QAAQ,WAAW,KAAK,QAAQ,OAAO,CAAC;EAGhD,MAAM,SAAoB,CAAC;EAE3B,KAAK,IAAI,QAAQ,KAAK,gBAAgB,SAAS,GAAG,SAAS,GAAG,SAAS;GACrE,MAAM,QAAQ,KAAK,gBAAgB;GAEnC,IAAI,CAAC,OACH;GAGF,MAAM,WAAW,KAAK,UAAU,IAAI,KAAK;GAEzC,MAAM,WAAW,KAAK,UAAU,IAAI,KAAK;GAEzC,IAAI,aAAa,KAAA,KAAa,CAAC,UAAU,SACvC;GAGF,IAAI;IACF,MAAM,SAAS,QAAQ,QAAQ;GACjC,SAAS,OAAO;IACd,OAAO,KAAK,KAAK;GACnB;EACF;EAEA,KAAK,UAAU,MAAM;EACrB,KAAK,QAAQ,MAAM;EAEnB,KAAK,gBAAgB,OAAO,CAAC;EAE7B,IAAI,OAAO,SAAS,GAClB,MAAM,IAAI,eAAe,QAAQ,wCAAwC;CAE7E;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-effect",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Lightweight Effect-inspired primitives for better-result",
5
5
  "keywords": [
6
6
  "better-result",
@@ -28,6 +28,7 @@
28
28
  "exports": {
29
29
  ".": "./dist/index.mjs",
30
30
  "./adapters/iti": "./dist/adapters/iti.mjs",
31
+ "./testing": "./dist/testing.mjs",
31
32
  "./package.json": "./package.json"
32
33
  },
33
34
  "publishConfig": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"iti-xXAb9-Qs.d.mts","names":[],"sources":["../src/service/types.ts","../src/layer/types.ts","../src/service/runtime.ts","../src/layer/backend.ts","../src/adapters/iti.ts"],"mappings":";KAAY,aAAa,iCAAiC,gBAAgB;WAC/D;;KAGC,aAAa,eAAe,aAAa;KAEzC,kBAAkB;KAElB,gBAAgB,UAAU,mBAAmB,aAAa;;;KCN1D,aAAa,KAAK,IAAI,YAAY;UAE7B;WACN,SAAS;WAET,eAAe;WAEf,WAAW,sBAAsB;;;;UCP3B;EACf,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;;cAK9E;SACJ,UAAU,UAAU;SAId,QAAQ,UAAU,iBAAiB,OAAO,IAAI,QAAQ,aAAa;SAQzE;;;;UCjBQ,qBAAqB;EACpC,SAAS,UAAU,uBAAuB;EAE1C,qBAAqB;;;;cCAV,2BAA2B;UAC9B;mBAES;UAET;UAEA;EAgBR,SAAS,UAAU;EAcnB,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;EAMnF,cAAc"}