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.
- package/dist/adapters/iti.d.mts +15 -2
- package/dist/adapters/iti.d.mts.map +1 -0
- package/dist/adapters/iti.mjs +7 -1
- package/dist/adapters/iti.mjs.map +1 -1
- package/dist/{iti-xXAb9-Qs.d.mts → backend-CNAls62W.d.mts} +18 -24
- package/dist/backend-CNAls62W.d.mts.map +1 -0
- package/dist/errors-CnvKqBpb.mjs +63 -0
- package/dist/errors-CnvKqBpb.mjs.map +1 -0
- package/dist/index.d.mts +60 -29
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +118 -43
- package/dist/index.mjs.map +1 -1
- package/dist/testing.d.mts +14 -0
- package/dist/testing.d.mts.map +1 -0
- package/dist/testing.mjs +52 -0
- package/dist/testing.mjs.map +1 -0
- package/package.json +2 -1
- package/dist/iti-xXAb9-Qs.d.mts.map +0 -1
package/dist/adapters/iti.d.mts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
|
|
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"}
|
package/dist/adapters/iti.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
34
|
-
|
|
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
|
|
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/
|
|
24
|
-
declare
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* nessa ordem.
|
|
87
|
+
* Útil principalmente quando use
|
|
88
|
+
* e release falham juntos.
|
|
68
89
|
*/
|
|
69
|
-
readonly
|
|
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
|
-
|
|
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
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/layer/layer.ts","../src/layer/runtime.ts","../src/
|
|
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 {
|
|
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
|
-
|
|
5
|
-
var ServiceRuntime = class {
|
|
6
|
-
static
|
|
7
|
-
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
static current() {
|
|
11
|
+
const resolver = storage.getStore();
|
|
12
|
+
if (!resolver) throw new ServiceRuntimeNotConfiguredError();
|
|
13
|
+
return resolver;
|
|
12
14
|
}
|
|
13
|
-
static
|
|
14
|
-
|
|
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
|
|
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
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
110
|
+
this.disposed = true;
|
|
75
111
|
}
|
|
76
|
-
}
|
|
112
|
+
}
|
|
77
113
|
};
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
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
|
package/dist/index.mjs.map
CHANGED
|
@@ -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"}
|
package/dist/testing.mjs
ADDED
|
@@ -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.
|
|
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"}
|