better-effect 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +464 -11
- package/dist/adapters/iti.d.mts +4 -4
- package/dist/adapters/iti.d.mts.map +1 -1
- package/dist/adapters/iti.mjs +20 -14
- package/dist/adapters/iti.mjs.map +1 -1
- package/dist/effect-CK4YZaX0.mjs +559 -0
- package/dist/effect-CK4YZaX0.mjs.map +1 -0
- package/dist/hono.d.mts +18 -13
- package/dist/hono.d.mts.map +1 -1
- package/dist/hono.mjs +9 -5
- package/dist/hono.mjs.map +1 -1
- package/dist/{index-DklNCz7w.d.mts → index-B1nH4luq.d.mts} +74 -7
- package/dist/index-B1nH4luq.d.mts.map +1 -0
- package/dist/index-BMdU_Z72.d.mts +18 -0
- package/dist/index-BMdU_Z72.d.mts.map +1 -0
- package/dist/{index-BafDna0B.d.mts → index-CX6iAoCB.d.mts} +168 -174
- package/dist/index-CX6iAoCB.d.mts.map +1 -0
- package/dist/index-GYJ4qm9K.d.mts +179 -0
- package/dist/index-GYJ4qm9K.d.mts.map +1 -0
- package/dist/{index-CULgSzUw.d.mts → index-heuaRmXR.d.mts} +5 -5
- package/dist/{index-CULgSzUw.d.mts.map → index-heuaRmXR.d.mts.map} +1 -1
- package/dist/index.d.mts +6 -5
- package/dist/index.mjs +9 -539
- package/dist/index.mjs.map +1 -1
- package/dist/{internal-identity-DmUpBeeL.mjs → internal-identity-CbXoZIfw.mjs} +3 -3
- package/dist/internal-identity-CbXoZIfw.mjs.map +1 -0
- package/dist/{signal-C1bagvrO.mjs → layer-D56fRdLt.mjs} +62 -64
- package/dist/layer-D56fRdLt.mjs.map +1 -0
- package/dist/node-context-DWsCUBRa.d.mts +12 -0
- package/dist/node-context-DWsCUBRa.d.mts.map +1 -0
- package/dist/node-context-lggRF4bR.mjs +25 -0
- package/dist/node-context-lggRF4bR.mjs.map +1 -0
- package/dist/node.d.mts +51 -0
- package/dist/node.d.mts.map +1 -0
- package/dist/node.mjs +233 -0
- package/dist/node.mjs.map +1 -0
- package/dist/{index-C7KX5rAP.d.mts → outcome-CB43bnFW.d.mts} +140 -27
- package/dist/outcome-CB43bnFW.d.mts.map +1 -0
- package/dist/program-metadata-DhQCCirO.mjs +260 -0
- package/dist/program-metadata-DhQCCirO.mjs.map +1 -0
- package/dist/registration-drnZk0SY.mjs +182 -0
- package/dist/registration-drnZk0SY.mjs.map +1 -0
- package/dist/runtime/explicit.d.mts +1 -1
- package/dist/runtime/node.d.mts +3 -12
- package/dist/runtime/node.mjs +2 -24
- package/dist/runtime-CBMrX8Wl.mjs +776 -0
- package/dist/runtime-CBMrX8Wl.mjs.map +1 -0
- package/dist/signal-D2UTcaaG.mjs +105 -0
- package/dist/signal-D2UTcaaG.mjs.map +1 -0
- package/dist/{standard-services-DW-i4UuA.mjs → standard-services-GBZ32tll.mjs} +200 -16
- package/dist/standard-services-GBZ32tll.mjs.map +1 -0
- package/dist/standard-services.d.mts +3 -3
- package/dist/standard-services.mjs +3 -3
- package/dist/testing.d.mts +257 -2
- package/dist/testing.d.mts.map +1 -0
- package/dist/testing.mjs +1159 -2
- package/dist/testing.mjs.map +1 -0
- package/package.json +8 -3
- package/dist/effect-DAMqvegy.mjs +0 -544
- package/dist/effect-DAMqvegy.mjs.map +0 -1
- package/dist/errors-Dnjhzbt0.mjs +0 -99
- package/dist/errors-Dnjhzbt0.mjs.map +0 -1
- package/dist/index-BafDna0B.d.mts.map +0 -1
- package/dist/index-C7KX5rAP.d.mts.map +0 -1
- package/dist/index-DklNCz7w.d.mts.map +0 -1
- package/dist/internal-identity-DmUpBeeL.mjs.map +0 -1
- package/dist/map-layer-backend-gal-mcRv.mjs +0 -53
- package/dist/map-layer-backend-gal-mcRv.mjs.map +0 -1
- package/dist/map-layer-backend-rNwfH0Bz.d.mts +0 -42
- package/dist/map-layer-backend-rNwfH0Bz.d.mts.map +0 -1
- package/dist/runtime/node.d.mts.map +0 -1
- package/dist/runtime/node.mjs.map +0 -1
- package/dist/signal-C1bagvrO.mjs.map +0 -1
- package/dist/standard-services-DW-i4UuA.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iti.mjs","names":[],"sources":["../../src/adapters/iti.ts"],"sourcesContent":["import { createContainer } from 'iti'\n\nimport {\n DuplicateServiceError,\n ServiceTagCollisionError,\n type LayerBackend,\n type LayerRegistration\n} from '../layer'\n\nimport { ServiceNotFoundError, type AnyServiceToken } from '../service'\n\nimport { assertServiceCompatibility } from '../layer/internal-identity'\nimport { isPromiseLike } from '../utils/runtime'\n\ntype LayerAcquiredValue = Awaited<ReturnType<LayerRegistration['acquire']>>\n\n/**\n * ITI-backed Layer backend.\n *\n * Install `iti` as the optional peer dependency and pass an instance to\n * `Runtime.make` when using ITI's container implementation.\n */\nexport class ItiLayerBackend implements LayerBackend {\n private container: any = createContainer()\n\n private readonly keys = new Map<string, string>()\n\n private readonly registered = new Map<string, AnyServiceToken>()\n\n /**\n * Track async gets so disposal cannot reset ITI while a provider is acquiring.\n * ITI caches rejected acquisitions; replacing the container is the explicit\n * retry boundary for that sticky failure behavior.\n */\n private readonly pending = new
|
|
1
|
+
{"version":3,"file":"iti.mjs","names":[],"sources":["../../src/adapters/iti.ts"],"sourcesContent":["import { createContainer } from 'iti'\n\nimport {\n DuplicateServiceError,\n ServiceTagCollisionError,\n type LayerBackend,\n type LayerBackendDisposeOptions,\n type LayerRegistration\n} from '../layer'\n\nimport { ServiceNotFoundError, type AnyServiceToken } from '../service'\nimport { captureServiceTag } from '../service/tag'\n\nimport { assertServiceCompatibility } from '../layer/internal-identity'\nimport { captureLayerRegistrationTag, normalizeLayerRegistration } from '../layer/registration'\nimport { isPromiseLike } from '../utils/runtime'\n\ntype LayerAcquiredValue = Awaited<ReturnType<LayerRegistration['acquire']>>\n\n/**\n * ITI-backed Layer backend.\n *\n * Install `iti` as the optional peer dependency and pass an instance to\n * `Runtime.make` when using ITI's container implementation.\n */\nexport class ItiLayerBackend implements LayerBackend {\n private container: any = createContainer()\n\n private readonly keys = new Map<string, string>()\n\n private readonly registered = new Map<string, AnyServiceToken>()\n\n /**\n * Track async gets so disposal cannot reset ITI while a provider is acquiring.\n * ITI caches rejected acquisitions; replacing the container is the explicit\n * retry boundary for that sticky failure behavior.\n */\n private readonly pending = new Map<Promise<unknown>, AnyServiceToken>()\n\n private keyFor(tag: string): string {\n const existing = this.keys.get(tag)\n\n if (existing) {\n return existing\n }\n\n const key = `better-effect:${tag}`\n\n this.keys.set(tag, key)\n\n return key\n }\n\n /** Register a Layer provider under its deterministic Service-tag key. */\n register(registration: LayerRegistration): void {\n const normalized = normalizeLayerRegistration(registration)\n const token = normalized.service\n const tag = captureLayerRegistrationTag(normalized)\n const existing = this.registered.get(tag)\n\n if (existing === token) {\n throw new DuplicateServiceError(token)\n }\n\n if (existing) {\n throw new ServiceTagCollisionError(existing, token)\n }\n\n const key = this.keyFor(tag)\n\n this.container = this.container.add({\n [key]: normalized.acquire\n })\n\n this.registered.set(tag, token)\n }\n\n /** Resolve a registered Service through the ITI container. */\n resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>> {\n const tag = captureServiceTag(token)\n const registered = this.registered.get(tag)\n\n if (registered === undefined) {\n throw new ServiceNotFoundError(token)\n }\n\n const key = this.keyFor(tag)\n const resolved = this.container.get(key)\n\n const validate = (instance: LayerAcquiredValue): InstanceType<T> => {\n assertServiceCompatibility(token, registered, instance)\n\n // SAFETY: The registered tag and compatibility check establish the constructor-to-instance relationship after ITI erases it.\n return instance as InstanceType<T>\n }\n\n if (isPromiseLike(resolved)) {\n const pending = Promise.resolve(resolved).then(validate)\n\n this.pending.set(pending, registered)\n void pending.then(\n () => this.pending.delete(pending),\n () => this.pending.delete(pending)\n )\n\n return pending\n }\n\n return validate(resolved)\n }\n\n /** Reset container-owned ITI state; Scope owns Layer provider releases. */\n async disposeAll(options?: LayerBackendDisposeOptions): Promise<void> {\n const container = this.container\n const acquisitions = [...this.pending.keys()]\n\n try {\n if (acquisitions.length > 0) {\n const observePending = options?.onPendingAcquisitions\n\n if (observePending) {\n await observePending(acquisitions)\n }\n\n await Promise.allSettled(acquisitions)\n }\n await container.disposeAll()\n } finally {\n this.container = createContainer()\n this.registered.clear()\n this.keys.clear()\n this.pending.clear()\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AAyBA,IAAa,kBAAb,MAAqD;CACnD,YAAyB,gBAAgB;CAEzC,uBAAwB,IAAI,IAAoB;CAEhD,6BAA8B,IAAI,IAA6B;;;;;;CAO/D,0BAA2B,IAAI,IAAuC;CAEtE,OAAe,KAAqB;EAClC,MAAM,WAAW,KAAK,KAAK,IAAI,GAAG;EAElC,IAAI,UACF,OAAO;EAGT,MAAM,MAAM,iBAAiB;EAE7B,KAAK,KAAK,IAAI,KAAK,GAAG;EAEtB,OAAO;CACT;;CAGA,SAAS,cAAuC;EAC9C,MAAM,aAAa,2BAA2B,YAAY;EAC1D,MAAM,QAAQ,WAAW;EACzB,MAAM,MAAM,4BAA4B,UAAU;EAClD,MAAM,WAAW,KAAK,WAAW,IAAI,GAAG;EAExC,IAAI,aAAa,OACf,MAAM,IAAI,sBAAsB,KAAK;EAGvC,IAAI,UACF,MAAM,IAAI,yBAAyB,UAAU,KAAK;EAGpD,MAAM,MAAM,KAAK,OAAO,GAAG;EAE3B,KAAK,YAAY,KAAK,UAAU,IAAI,GACjC,MAAM,WAAW,QACpB,CAAC;EAED,KAAK,WAAW,IAAI,KAAK,KAAK;CAChC;;CAGA,QAAmC,OAA0D;EAC3F,MAAM,MAAM,kBAAkB,KAAK;EACnC,MAAM,aAAa,KAAK,WAAW,IAAI,GAAG;EAE1C,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,qBAAqB,KAAK;EAGtC,MAAM,MAAM,KAAK,OAAO,GAAG;EAC3B,MAAM,WAAW,KAAK,UAAU,IAAI,GAAG;EAEvC,MAAM,YAAY,aAAkD;GAClE,2BAA2B,OAAO,YAAY,QAAQ;GAGtD,OAAO;EACT;EAEA,IAAI,cAAc,QAAQ,GAAG;GAC3B,MAAM,UAAU,QAAQ,QAAQ,QAAQ,CAAC,CAAC,KAAK,QAAQ;GAEvD,KAAK,QAAQ,IAAI,SAAS,UAAU;GACpC,QAAa,WACL,KAAK,QAAQ,OAAO,OAAO,SAC3B,KAAK,QAAQ,OAAO,OAAO,CACnC;GAEA,OAAO;EACT;EAEA,OAAO,SAAS,QAAQ;CAC1B;;CAGA,MAAM,WAAW,SAAqD;EACpE,MAAM,YAAY,KAAK;EACvB,MAAM,eAAe,CAAC,GAAG,KAAK,QAAQ,KAAK,CAAC;EAE5C,IAAI;GACF,IAAI,aAAa,SAAS,GAAG;IAC3B,MAAM,iBAAiB,SAAS;IAEhC,IAAI,gBACF,MAAM,eAAe,YAAY;IAGnC,MAAM,QAAQ,WAAW,YAAY;GACvC;GACA,MAAM,UAAU,WAAW;EAC7B,UAAU;GACR,KAAK,YAAY,gBAAgB;GACjC,KAAK,WAAW,MAAM;GACtB,KAAK,KAAK,MAAM;GAChB,KAAK,QAAQ,MAAM;EACrB;CACF;AACF"}
|
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
import { t as isPromiseLike } from "./runtime-CDcCF5cb.mjs";
|
|
2
|
+
import { i as Scope, n as inheritProgramName, r as setProgramName } from "./program-metadata-DhQCCirO.mjs";
|
|
3
|
+
import { Err, Result, matchError, matchErrorPartial } from "better-result";
|
|
4
|
+
//#region src/effect/combinators.ts
|
|
5
|
+
const asResult = (value) => {
|
|
6
|
+
return value;
|
|
7
|
+
};
|
|
8
|
+
const mapResult = (result, fn) => {
|
|
9
|
+
return Result.map(result, fn);
|
|
10
|
+
};
|
|
11
|
+
const mapErrorResult = (result, fn) => {
|
|
12
|
+
return Result.mapError(result, fn);
|
|
13
|
+
};
|
|
14
|
+
const andThenResult = (result, next) => {
|
|
15
|
+
const resultNext = next;
|
|
16
|
+
return Result.andThen(result, resultNext);
|
|
17
|
+
};
|
|
18
|
+
const andThenAsyncResult = (result, next) => {
|
|
19
|
+
const resultNext = (value) => {
|
|
20
|
+
return Promise.resolve(next(value));
|
|
21
|
+
};
|
|
22
|
+
return Result.andThenAsync(result, resultNext);
|
|
23
|
+
};
|
|
24
|
+
function map$1(first, second) {
|
|
25
|
+
if (first instanceof Function && second === void 0) {
|
|
26
|
+
const callback = first;
|
|
27
|
+
return (effect) => {
|
|
28
|
+
return map$1(effect, callback);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const fn = second;
|
|
32
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => {
|
|
33
|
+
return mapResult(result, fn);
|
|
34
|
+
});
|
|
35
|
+
return mapResult(first, fn);
|
|
36
|
+
}
|
|
37
|
+
function mapError$1(first, second) {
|
|
38
|
+
if (first instanceof Function && second === void 0) {
|
|
39
|
+
const callback = first;
|
|
40
|
+
return (effect) => {
|
|
41
|
+
return mapError$1(effect, callback);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const fn = second;
|
|
45
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => {
|
|
46
|
+
return mapErrorResult(result, fn);
|
|
47
|
+
});
|
|
48
|
+
return mapErrorResult(first, fn);
|
|
49
|
+
}
|
|
50
|
+
function andThen$1(first, second) {
|
|
51
|
+
if (first instanceof Function && second === void 0) {
|
|
52
|
+
const callback = first;
|
|
53
|
+
return (effect) => {
|
|
54
|
+
return andThen$1(effect, callback);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return andThenResult(first, second);
|
|
58
|
+
}
|
|
59
|
+
function andThenAsync(first, second) {
|
|
60
|
+
if (first instanceof Function && second === void 0) {
|
|
61
|
+
const callback = first;
|
|
62
|
+
return (effect) => {
|
|
63
|
+
return andThenAsync(effect, callback);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const next = second;
|
|
67
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => {
|
|
68
|
+
return andThenAsyncResult(result, next);
|
|
69
|
+
});
|
|
70
|
+
return andThenAsyncResult(first, next);
|
|
71
|
+
}
|
|
72
|
+
const tapResult = (result, fn) => Result.tap(result, fn);
|
|
73
|
+
const tapErrorResult = (result, fn) => Result.tapError(result, fn);
|
|
74
|
+
const tapBothResult = (result, handlers) => Result.tapBoth(result, handlers);
|
|
75
|
+
const tapAsyncResult = (result, fn) => Result.tapAsync(result, (value) => Promise.resolve(fn(value)));
|
|
76
|
+
const tapErrorAsyncResult = (result, fn) => Result.tapErrorAsync(result, (error) => Promise.resolve(fn(error)));
|
|
77
|
+
const tapBothAsyncResult = (result, handlers) => Result.tapBothAsync(result, {
|
|
78
|
+
ok: (value) => Promise.resolve(handlers.ok(value)),
|
|
79
|
+
err: (error) => Promise.resolve(handlers.err(error))
|
|
80
|
+
});
|
|
81
|
+
const asTaggedResult = (result) => {
|
|
82
|
+
return result;
|
|
83
|
+
};
|
|
84
|
+
const matchErrorResult = (result, handlers) => {
|
|
85
|
+
return Result.mapError(result, (error) => matchError(error, handlers));
|
|
86
|
+
};
|
|
87
|
+
const matchErrorPartialResult = (result, handlers) => {
|
|
88
|
+
return Result.mapError(result, (error) => matchErrorPartial(error, handlers));
|
|
89
|
+
};
|
|
90
|
+
const recoverResult = (result, fn) => Result.tryRecover(result, fn);
|
|
91
|
+
const recoverAsyncResult = (result, fn) => Result.tryRecoverAsync(result, (error) => Promise.resolve(fn(error)));
|
|
92
|
+
const flattenResult = (result) => Result.flatten(result);
|
|
93
|
+
const matchResult = (result, handlers) => Result.match(result, handlers);
|
|
94
|
+
const allResult = (results) => Result.all(results);
|
|
95
|
+
function tap$1(first, second) {
|
|
96
|
+
if (first instanceof Function && second === void 0) {
|
|
97
|
+
const callback = first;
|
|
98
|
+
return (effect) => tap$1(effect, callback);
|
|
99
|
+
}
|
|
100
|
+
if (second === void 0) throw new TypeError("Effect.tap requires a callback");
|
|
101
|
+
const fn = second;
|
|
102
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => tapResult(asResult(result), fn));
|
|
103
|
+
return tapResult(asResult(first), fn);
|
|
104
|
+
}
|
|
105
|
+
function tapError$1(first, second) {
|
|
106
|
+
if (first instanceof Function && second === void 0) {
|
|
107
|
+
const callback = first;
|
|
108
|
+
return (effect) => tapError$1(effect, callback);
|
|
109
|
+
}
|
|
110
|
+
if (second === void 0) throw new TypeError("Effect.tapError requires a callback");
|
|
111
|
+
const fn = second;
|
|
112
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => tapErrorResult(asResult(result), fn));
|
|
113
|
+
return tapErrorResult(asResult(first), fn);
|
|
114
|
+
}
|
|
115
|
+
function tapBoth(first, second) {
|
|
116
|
+
if (second === void 0) return (effect) => tapBoth(effect, first);
|
|
117
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => tapBothResult(result, second));
|
|
118
|
+
return tapBothResult(asResult(first), second);
|
|
119
|
+
}
|
|
120
|
+
function tapAsync(first, second) {
|
|
121
|
+
if (first instanceof Function && second === void 0) {
|
|
122
|
+
const callback = first;
|
|
123
|
+
return (effect) => tapAsync(effect, callback);
|
|
124
|
+
}
|
|
125
|
+
if (second === void 0) throw new TypeError("Effect.tapAsync requires a callback");
|
|
126
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => tapAsyncResult(asResult(result), second));
|
|
127
|
+
return tapAsyncResult(asResult(first), second);
|
|
128
|
+
}
|
|
129
|
+
function tapErrorAsync(first, second) {
|
|
130
|
+
if (first instanceof Function && second === void 0) {
|
|
131
|
+
const callback = first;
|
|
132
|
+
return (effect) => tapErrorAsync(effect, callback);
|
|
133
|
+
}
|
|
134
|
+
if (second === void 0) throw new TypeError("Effect.tapErrorAsync requires a callback");
|
|
135
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => tapErrorAsyncResult(asResult(result), second));
|
|
136
|
+
return tapErrorAsyncResult(asResult(first), second);
|
|
137
|
+
}
|
|
138
|
+
function tapBothAsync(first, second) {
|
|
139
|
+
if (second === void 0) return (effect) => tapBothAsync(effect, first);
|
|
140
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => tapBothAsyncResult(result, second));
|
|
141
|
+
return tapBothAsyncResult(asResult(first), second);
|
|
142
|
+
}
|
|
143
|
+
function matchError$1(first, second) {
|
|
144
|
+
if (second === void 0) throw new TypeError("Effect.matchError requires handlers");
|
|
145
|
+
const handlers = second;
|
|
146
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => matchErrorResult(asTaggedResult(asResult(result)), handlers));
|
|
147
|
+
return matchErrorResult(asTaggedResult(asResult(first)), handlers);
|
|
148
|
+
}
|
|
149
|
+
function matchErrorPartial$1(first, second) {
|
|
150
|
+
if (second === void 0) throw new TypeError("Effect.matchErrorPartial requires handlers");
|
|
151
|
+
const handlers = second;
|
|
152
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => matchErrorPartialResult(asTaggedResult(asResult(result)), handlers));
|
|
153
|
+
return matchErrorPartialResult(asTaggedResult(asResult(first)), handlers);
|
|
154
|
+
}
|
|
155
|
+
function recover$1(first, second) {
|
|
156
|
+
if (first instanceof Function && second === void 0) {
|
|
157
|
+
const callback = first;
|
|
158
|
+
return (effect) => recover$1(effect, callback);
|
|
159
|
+
}
|
|
160
|
+
if (second === void 0) throw new TypeError("Effect.recover requires a callback");
|
|
161
|
+
const fn = second;
|
|
162
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => recoverResult(asResult(result), fn));
|
|
163
|
+
return recoverResult(asResult(first), fn);
|
|
164
|
+
}
|
|
165
|
+
function recoverAsync(first, second) {
|
|
166
|
+
if (first instanceof Function && second === void 0) {
|
|
167
|
+
const callback = first;
|
|
168
|
+
return (effect) => recoverAsync(effect, callback);
|
|
169
|
+
}
|
|
170
|
+
if (second === void 0) throw new TypeError("Effect.recoverAsync requires a callback");
|
|
171
|
+
const fn = second;
|
|
172
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => recoverAsyncResult(asResult(result), fn));
|
|
173
|
+
return recoverAsyncResult(asResult(first), fn);
|
|
174
|
+
}
|
|
175
|
+
/** Remove one nested Result/Effect layer. */
|
|
176
|
+
function flatten(effect) {
|
|
177
|
+
return flattenResult(asResult(effect));
|
|
178
|
+
}
|
|
179
|
+
function as(first, second) {
|
|
180
|
+
if (arguments.length < 2) return (effect) => as(effect, first);
|
|
181
|
+
return mapResult(asResult(first), () => second);
|
|
182
|
+
}
|
|
183
|
+
/** Replace a successful value with void. */
|
|
184
|
+
function asVoid(effect) {
|
|
185
|
+
return mapResult(asResult(effect), () => void 0);
|
|
186
|
+
}
|
|
187
|
+
function match(first, second) {
|
|
188
|
+
if (isPromiseLike(first)) return Promise.resolve(first).then((result) => match(asResult(result), second));
|
|
189
|
+
return matchResult(asResult(first), second);
|
|
190
|
+
}
|
|
191
|
+
/** Collect already-created Effects in input order. */
|
|
192
|
+
function all(results) {
|
|
193
|
+
return allResult(results);
|
|
194
|
+
}
|
|
195
|
+
/** Combine two already-created Effects in input order. */
|
|
196
|
+
function zip(left, right) {
|
|
197
|
+
return Result.all([left, right]);
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/effect/program-combinators.ts
|
|
201
|
+
const asProgram = (program) => program;
|
|
202
|
+
const isAsyncEffect = (effect) => isPromiseLike(effect);
|
|
203
|
+
const mapRuntime = (effect, fn) => {
|
|
204
|
+
if (isAsyncEffect(effect)) return Promise.resolve(effect).then((result) => Result.map(result, fn));
|
|
205
|
+
return Result.map(effect, fn);
|
|
206
|
+
};
|
|
207
|
+
const mapErrorRuntime = (effect, fn) => {
|
|
208
|
+
if (isAsyncEffect(effect)) return Promise.resolve(effect).then((result) => Result.mapError(result, fn));
|
|
209
|
+
return Result.mapError(effect, fn);
|
|
210
|
+
};
|
|
211
|
+
const tapRuntime = (effect, fn) => {
|
|
212
|
+
if (isAsyncEffect(effect)) return Promise.resolve(effect).then((result) => Result.tap(result, fn));
|
|
213
|
+
return Result.tap(effect, fn);
|
|
214
|
+
};
|
|
215
|
+
const tapErrorRuntime = (effect, fn) => {
|
|
216
|
+
if (isAsyncEffect(effect)) return Promise.resolve(effect).then((result) => Result.tapError(result, fn));
|
|
217
|
+
return Result.tapError(effect, fn);
|
|
218
|
+
};
|
|
219
|
+
const invoke = (output) => output instanceof Function ? output() : output;
|
|
220
|
+
const andThenRuntime = (effect, next) => {
|
|
221
|
+
const continueWith = (value) => Promise.resolve(invoke(next(value)));
|
|
222
|
+
if (isAsyncEffect(effect)) return Promise.resolve(effect).then((result) => Result.andThenAsync(result, continueWith));
|
|
223
|
+
return Result.andThenAsync(effect, continueWith);
|
|
224
|
+
};
|
|
225
|
+
const recoverRuntime = (effect, recover) => {
|
|
226
|
+
const continueWith = (error) => Promise.resolve(invoke(recover(error)));
|
|
227
|
+
if (isAsyncEffect(effect)) return Promise.resolve(effect).then((result) => Result.tryRecoverAsync(result, continueWith));
|
|
228
|
+
return Result.tryRecoverAsync(effect, continueWith);
|
|
229
|
+
};
|
|
230
|
+
const mapProgram = (program, fn) => {
|
|
231
|
+
const mapped = asProgram(() => mapRuntime(program(), fn));
|
|
232
|
+
return inheritProgramName(program, mapped);
|
|
233
|
+
};
|
|
234
|
+
const mapErrorProgram = (program, fn) => {
|
|
235
|
+
const mapped = asProgram(() => mapErrorRuntime(program(), fn));
|
|
236
|
+
return inheritProgramName(program, mapped);
|
|
237
|
+
};
|
|
238
|
+
const tapProgram = (program, fn) => {
|
|
239
|
+
const tapped = asProgram(() => tapRuntime(program(), fn));
|
|
240
|
+
return inheritProgramName(program, tapped);
|
|
241
|
+
};
|
|
242
|
+
const tapErrorProgram = (program, fn) => {
|
|
243
|
+
const tapped = asProgram(() => tapErrorRuntime(program(), fn));
|
|
244
|
+
return inheritProgramName(program, tapped);
|
|
245
|
+
};
|
|
246
|
+
function map(first, second) {
|
|
247
|
+
if (second === void 0) return (program) => {
|
|
248
|
+
return mapProgram(program, first);
|
|
249
|
+
};
|
|
250
|
+
return mapProgram(first, second);
|
|
251
|
+
}
|
|
252
|
+
function mapError(first, second) {
|
|
253
|
+
if (second === void 0) return (program) => {
|
|
254
|
+
return mapErrorProgram(program, first);
|
|
255
|
+
};
|
|
256
|
+
return mapErrorProgram(first, second);
|
|
257
|
+
}
|
|
258
|
+
function andThen(first, second) {
|
|
259
|
+
if (second === void 0) return (program) => {
|
|
260
|
+
const chained = () => andThenRuntime(program(), first);
|
|
261
|
+
return inheritProgramName(program, chained);
|
|
262
|
+
};
|
|
263
|
+
const source = first;
|
|
264
|
+
const chained = () => andThenRuntime(source(), second);
|
|
265
|
+
return inheritProgramName(source, chained);
|
|
266
|
+
}
|
|
267
|
+
function tap(first, second) {
|
|
268
|
+
if (second === void 0) return (program) => {
|
|
269
|
+
return tapProgram(program, first);
|
|
270
|
+
};
|
|
271
|
+
return tapProgram(first, second);
|
|
272
|
+
}
|
|
273
|
+
function tapError(first, second) {
|
|
274
|
+
if (second === void 0) return (program) => {
|
|
275
|
+
return tapErrorProgram(program, first);
|
|
276
|
+
};
|
|
277
|
+
return tapErrorProgram(first, second);
|
|
278
|
+
}
|
|
279
|
+
function recover(first, second) {
|
|
280
|
+
if (second === void 0) return (program) => {
|
|
281
|
+
const recovered = () => recoverRuntime(program(), first);
|
|
282
|
+
return inheritProgramName(program, recovered);
|
|
283
|
+
};
|
|
284
|
+
const source = first;
|
|
285
|
+
const recovered = () => recoverRuntime(source(), second);
|
|
286
|
+
return inheritProgramName(source, recovered);
|
|
287
|
+
}
|
|
288
|
+
//#endregion
|
|
289
|
+
//#region src/effect/program-scheduler.ts
|
|
290
|
+
/** Validate the shared bounded-concurrency option before a Program is run. */
|
|
291
|
+
const validateProgramConcurrency = (concurrency) => {
|
|
292
|
+
if (concurrency !== void 0 && (!Number.isFinite(concurrency) || !Number.isInteger(concurrency) || concurrency <= 0)) throw new RangeError("Program.all concurrency must be a positive integer");
|
|
293
|
+
};
|
|
294
|
+
const runWorker = async (length, task, stopOnResultError, state) => {
|
|
295
|
+
while (!state.stopScheduling) {
|
|
296
|
+
const index = state.nextIndex++;
|
|
297
|
+
if (index >= length) return;
|
|
298
|
+
try {
|
|
299
|
+
const result = await task(index);
|
|
300
|
+
state.results[index] = result;
|
|
301
|
+
if (result instanceof Err) state.stopScheduling ||= stopOnResultError;
|
|
302
|
+
} catch (cause) {
|
|
303
|
+
state.defects[index] = { cause };
|
|
304
|
+
state.stopScheduling = true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
/** Run claimed Programs with a shared FIFO worker pool and no cancellation. */
|
|
309
|
+
const runProgramCollection = async (length, task, options) => {
|
|
310
|
+
const state = {
|
|
311
|
+
nextIndex: 0,
|
|
312
|
+
stopScheduling: false,
|
|
313
|
+
results: Array.from({ length }),
|
|
314
|
+
defects: Array.from({ length })
|
|
315
|
+
};
|
|
316
|
+
const workerCount = Math.min(options.concurrency ?? length, length);
|
|
317
|
+
const workers = Array.from({ length: workerCount }, () => runWorker(length, task, options.stopOnResultError, state));
|
|
318
|
+
await Promise.all(workers);
|
|
319
|
+
return state;
|
|
320
|
+
};
|
|
321
|
+
/** Select the first defect by input index, independent of settlement order. */
|
|
322
|
+
const firstProgramDefect = (defects) => {
|
|
323
|
+
for (const defect of defects) if (defect !== void 0) return defect;
|
|
324
|
+
};
|
|
325
|
+
/** Select the first short-circuiting failure by input index. */
|
|
326
|
+
const firstProgramFailure = (results, defects) => {
|
|
327
|
+
const length = Math.max(results.length, defects.length);
|
|
328
|
+
for (let index = 0; index < length; index++) {
|
|
329
|
+
const defect = defects[index];
|
|
330
|
+
if (defect !== void 0) return {
|
|
331
|
+
kind: "defect",
|
|
332
|
+
cause: defect.cause
|
|
333
|
+
};
|
|
334
|
+
const result = results[index];
|
|
335
|
+
if (result instanceof Err) return {
|
|
336
|
+
kind: "result",
|
|
337
|
+
result
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region src/effect/effect.ts
|
|
343
|
+
const runResultGenerator = Result.gen;
|
|
344
|
+
function gen(body) {
|
|
345
|
+
return runResultGenerator(body);
|
|
346
|
+
}
|
|
347
|
+
function fn(body) {
|
|
348
|
+
const program = () => runResultGenerator(body);
|
|
349
|
+
return program;
|
|
350
|
+
}
|
|
351
|
+
const validateProgramName = (name) => {
|
|
352
|
+
if (typeof name !== "string") throw new TypeError("Program name must be a string");
|
|
353
|
+
};
|
|
354
|
+
function programNamed(name, program) {
|
|
355
|
+
validateProgramName(name);
|
|
356
|
+
if (program === void 0) return (source) => setProgramName(source, name);
|
|
357
|
+
return setProgramName(program, name);
|
|
358
|
+
}
|
|
359
|
+
const runShortCircuitingCollection = async (length, task, concurrency) => {
|
|
360
|
+
const outcome = await runProgramCollection(length, task, {
|
|
361
|
+
concurrency,
|
|
362
|
+
stopOnResultError: true
|
|
363
|
+
});
|
|
364
|
+
const failure = firstProgramFailure(outcome.results, outcome.defects);
|
|
365
|
+
if (failure?.kind === "defect") throw failure.cause;
|
|
366
|
+
if (failure?.kind === "result") return failure.result;
|
|
367
|
+
return Result.all(outcome.results);
|
|
368
|
+
};
|
|
369
|
+
const runAllResultsCollection = async (length, task, concurrency) => {
|
|
370
|
+
const outcome = await runProgramCollection(length, task, {
|
|
371
|
+
concurrency,
|
|
372
|
+
stopOnResultError: false
|
|
373
|
+
});
|
|
374
|
+
const defect = firstProgramDefect(outcome.defects);
|
|
375
|
+
if (defect !== void 0) throw defect.cause;
|
|
376
|
+
return Result.ok(outcome.results);
|
|
377
|
+
};
|
|
378
|
+
/** Build a lazy Program collection with optional bounded concurrency. */
|
|
379
|
+
function programAll(programs, options = {}) {
|
|
380
|
+
const concurrency = options.concurrency;
|
|
381
|
+
const name = options?.name;
|
|
382
|
+
validateProgramConcurrency(concurrency);
|
|
383
|
+
const program = () => runShortCircuitingCollection(programs.length, (index) => programs[index](), concurrency);
|
|
384
|
+
if (name !== void 0) {
|
|
385
|
+
validateProgramName(name);
|
|
386
|
+
setProgramName(program, name);
|
|
387
|
+
}
|
|
388
|
+
return program;
|
|
389
|
+
}
|
|
390
|
+
/** Build a lazy Program collection from an input array and Program factory. */
|
|
391
|
+
function programForEach(items, makeProgram, options = {}) {
|
|
392
|
+
const concurrency = options.concurrency;
|
|
393
|
+
const name = options?.name;
|
|
394
|
+
validateProgramConcurrency(concurrency);
|
|
395
|
+
const program = () => runShortCircuitingCollection(items.length, (index) => makeProgram(items[index], index)(), concurrency);
|
|
396
|
+
if (name !== void 0) {
|
|
397
|
+
validateProgramName(name);
|
|
398
|
+
setProgramName(program, name);
|
|
399
|
+
}
|
|
400
|
+
return program;
|
|
401
|
+
}
|
|
402
|
+
/** Build a lazy Program collection that retains every child Result. */
|
|
403
|
+
function programAllResults(programs, options = {}) {
|
|
404
|
+
const concurrency = options.concurrency;
|
|
405
|
+
const name = options?.name;
|
|
406
|
+
validateProgramConcurrency(concurrency);
|
|
407
|
+
const program = () => runAllResultsCollection(programs.length, (index) => programs[index](), concurrency);
|
|
408
|
+
if (name !== void 0) {
|
|
409
|
+
validateProgramName(name);
|
|
410
|
+
setProgramName(program, name);
|
|
411
|
+
}
|
|
412
|
+
return program;
|
|
413
|
+
}
|
|
414
|
+
/** Value-level namespace for lazy Program combinators. */
|
|
415
|
+
const Program = {
|
|
416
|
+
named: programNamed,
|
|
417
|
+
all: programAll,
|
|
418
|
+
forEach: programForEach,
|
|
419
|
+
allResults: programAllResults,
|
|
420
|
+
map,
|
|
421
|
+
mapError,
|
|
422
|
+
andThen,
|
|
423
|
+
tap,
|
|
424
|
+
tapError,
|
|
425
|
+
recover
|
|
426
|
+
};
|
|
427
|
+
/**
|
|
428
|
+
* Acquire a resource in the current Scope and register its release callback.
|
|
429
|
+
*
|
|
430
|
+
* Acquisition failures are represented in the Effect Result error channel;
|
|
431
|
+
* release failures remain owned by Scope cleanup. The release callback
|
|
432
|
+
* receives the final outcome chosen by the enclosing execution boundary.
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```ts
|
|
436
|
+
* const connection = yield* Effect.acquireRelease(
|
|
437
|
+
* () => pool.connect(),
|
|
438
|
+
* (connection, outcome) => connection.close(outcome)
|
|
439
|
+
* )
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
function acquireRelease(acquire, release) {
|
|
443
|
+
const scope = Scope.current();
|
|
444
|
+
return Result.await(Result.tryPromise(() => scope.acquire(acquire, release)));
|
|
445
|
+
}
|
|
446
|
+
var AcquireReleaseResultFailure = class {
|
|
447
|
+
result;
|
|
448
|
+
constructor(result) {
|
|
449
|
+
this.result = result;
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
/**
|
|
453
|
+
* Acquire a Result-valued resource and register only successful acquisitions.
|
|
454
|
+
*
|
|
455
|
+
* Typed acquisition failures remain unchanged in the Effect error channel;
|
|
456
|
+
* unexpected acquisition and registration failures are normalized by
|
|
457
|
+
* `better-result`. Release failures remain Scope cleanup failures.
|
|
458
|
+
*/
|
|
459
|
+
function acquireReleaseResult(acquire, release) {
|
|
460
|
+
const scope = Scope.current();
|
|
461
|
+
const registered = Result.tryPromise(async () => {
|
|
462
|
+
try {
|
|
463
|
+
const resource = await scope.acquire(async () => {
|
|
464
|
+
const result = await acquire();
|
|
465
|
+
if (Result.isError(result)) throw new AcquireReleaseResultFailure(result);
|
|
466
|
+
return result.value;
|
|
467
|
+
}, release);
|
|
468
|
+
return Result.ok(resource);
|
|
469
|
+
} catch (cause) {
|
|
470
|
+
if (cause instanceof AcquireReleaseResultFailure) return cause.result;
|
|
471
|
+
throw cause;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
return Result.await(registered.then((result) => result.andThen((acquired) => acquired)));
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Lazily acquire and register a disposable resource in the current Scope.
|
|
478
|
+
*
|
|
479
|
+
* Disposal is delegated to `Scope.add`, including protocol preference,
|
|
480
|
+
* validation, race-safe registration, and cleanup error handling.
|
|
481
|
+
*/
|
|
482
|
+
function acquireDisposable(acquire) {
|
|
483
|
+
const scope = Scope.current();
|
|
484
|
+
return Result.await(Result.tryPromise(async () => scope.add(await acquire())));
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Register an already-acquired disposable resource in the current Scope.
|
|
488
|
+
*
|
|
489
|
+
* The resource is not acquired by this helper. Registration failures are
|
|
490
|
+
* represented in the Effect Result error channel; disposal failures remain
|
|
491
|
+
* owned by Scope cleanup.
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* ```ts
|
|
495
|
+
* const file = yield* Effect.add(await openFile('notes.txt'))
|
|
496
|
+
* ```
|
|
497
|
+
*/
|
|
498
|
+
function add(resource) {
|
|
499
|
+
const scope = Scope.current();
|
|
500
|
+
return Result.await(Result.tryPromise(() => scope.add(resource)));
|
|
501
|
+
}
|
|
502
|
+
const Effect = {
|
|
503
|
+
/** Compose a generator-based Effect program. */
|
|
504
|
+
gen,
|
|
505
|
+
/** Build a lazy Program from a generator. */
|
|
506
|
+
fn,
|
|
507
|
+
/** Acquire and register a resource in the current Scope. */
|
|
508
|
+
acquireRelease,
|
|
509
|
+
/** Acquire a Result-valued resource and register successful acquisitions. */
|
|
510
|
+
acquireReleaseResult,
|
|
511
|
+
/** Acquire and register a disposable resource in the current Scope. */
|
|
512
|
+
acquireDisposable,
|
|
513
|
+
/** Register an already-acquired disposable in the current Scope. */
|
|
514
|
+
add,
|
|
515
|
+
/** Map a successful Effect result. */
|
|
516
|
+
map: map$1,
|
|
517
|
+
/** Map an Effect error. */
|
|
518
|
+
mapError: mapError$1,
|
|
519
|
+
/** Chain a synchronous Effect result. */
|
|
520
|
+
andThen: andThen$1,
|
|
521
|
+
/** Chain an asynchronous Effect result. */
|
|
522
|
+
andThenAsync,
|
|
523
|
+
/** Observe successful values without changing the Result. */
|
|
524
|
+
tap: tap$1,
|
|
525
|
+
/** Observe successful values asynchronously without changing the Result. */
|
|
526
|
+
tapAsync,
|
|
527
|
+
/** Observe error values without changing the Result. */
|
|
528
|
+
tapError: tapError$1,
|
|
529
|
+
/** Observe error values asynchronously without changing the Result. */
|
|
530
|
+
tapErrorAsync,
|
|
531
|
+
/** Observe the active Result branch without changing the Result. */
|
|
532
|
+
tapBoth,
|
|
533
|
+
/** Observe the active Result branch asynchronously without changing the Result. */
|
|
534
|
+
tapBothAsync,
|
|
535
|
+
/** Match every tagged error variant and map it to a new error value. */
|
|
536
|
+
matchError: matchError$1,
|
|
537
|
+
/** Match selected tagged errors and retain unhandled variants. */
|
|
538
|
+
matchErrorPartial: matchErrorPartial$1,
|
|
539
|
+
/** Recover an error with another Effect. */
|
|
540
|
+
recover: recover$1,
|
|
541
|
+
/** Recover an error asynchronously with another Effect. */
|
|
542
|
+
recoverAsync,
|
|
543
|
+
/** Remove one nested Effect layer. */
|
|
544
|
+
flatten,
|
|
545
|
+
/** Replace a successful value. */
|
|
546
|
+
as,
|
|
547
|
+
/** Replace a successful value with void. */
|
|
548
|
+
asVoid,
|
|
549
|
+
/** Match either Result branch. */
|
|
550
|
+
match,
|
|
551
|
+
/** Collect Effects in input order. */
|
|
552
|
+
all,
|
|
553
|
+
/** Zip two Effects in input order. */
|
|
554
|
+
zip
|
|
555
|
+
};
|
|
556
|
+
//#endregion
|
|
557
|
+
export { Program as n, Effect as t };
|
|
558
|
+
|
|
559
|
+
//# sourceMappingURL=effect-CK4YZaX0.mjs.map
|