better-effect 0.9.3 → 0.10.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -16
  3. package/dist/adapters/iti.d.mts +10 -4
  4. package/dist/adapters/iti.d.mts.map +1 -1
  5. package/dist/adapters/iti.mjs +24 -4
  6. package/dist/adapters/iti.mjs.map +1 -1
  7. package/dist/context-BUEf1qjL.mjs +152 -0
  8. package/dist/context-BUEf1qjL.mjs.map +1 -0
  9. package/dist/{effect-CZdZCZLW.mjs → effect-DAMqvegy.mjs} +35 -13
  10. package/dist/effect-DAMqvegy.mjs.map +1 -0
  11. package/dist/errors-BKcsQ0ip.d.mts +12 -0
  12. package/dist/errors-BKcsQ0ip.d.mts.map +1 -0
  13. package/dist/hono.d.mts +52 -18
  14. package/dist/hono.d.mts.map +1 -1
  15. package/dist/hono.mjs +36 -41
  16. package/dist/hono.mjs.map +1 -1
  17. package/dist/{index-BsPr7qHf.d.mts → index-BafDna0B.d.mts} +4 -4
  18. package/dist/{index-BsPr7qHf.d.mts.map → index-BafDna0B.d.mts.map} +1 -1
  19. package/dist/{index-EJlskfAW.d.mts → index-C7KX5rAP.d.mts} +8 -4
  20. package/dist/index-C7KX5rAP.d.mts.map +1 -0
  21. package/dist/{index-1NLNdkJy.d.mts → index-CULgSzUw.d.mts} +4 -4
  22. package/dist/{index-1NLNdkJy.d.mts.map → index-CULgSzUw.d.mts.map} +1 -1
  23. package/dist/{index-CITM15SE.d.mts → index-DklNCz7w.d.mts} +4 -4
  24. package/dist/{index-CITM15SE.d.mts.map → index-DklNCz7w.d.mts.map} +1 -1
  25. package/dist/index.d.mts +5 -5
  26. package/dist/index.mjs +31 -34
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/{map-layer-backend-DMJauecV.d.mts → map-layer-backend-rNwfH0Bz.d.mts} +2 -2
  29. package/dist/{map-layer-backend-DMJauecV.d.mts.map → map-layer-backend-rNwfH0Bz.d.mts.map} +1 -1
  30. package/dist/runtime/explicit.d.mts +11 -7
  31. package/dist/runtime/explicit.d.mts.map +1 -1
  32. package/dist/runtime/explicit.mjs +96 -23
  33. package/dist/runtime/explicit.mjs.map +1 -1
  34. package/dist/runtime/node.d.mts +2 -2
  35. package/dist/runtime/node.d.mts.map +1 -1
  36. package/dist/runtime/node.mjs +10 -6
  37. package/dist/runtime/node.mjs.map +1 -1
  38. package/dist/{signal-Cl9tqGyX.mjs → signal-C1bagvrO.mjs} +6 -5
  39. package/dist/signal-C1bagvrO.mjs.map +1 -0
  40. package/dist/{standard-services-QseopL9g.mjs → standard-services-DW-i4UuA.mjs} +2 -2
  41. package/dist/{standard-services-QseopL9g.mjs.map → standard-services-DW-i4UuA.mjs.map} +1 -1
  42. package/dist/standard-services.d.mts +2 -2
  43. package/dist/standard-services.mjs +2 -2
  44. package/dist/testing.d.mts +1 -1
  45. package/package.json +6 -3
  46. package/dist/context-B4yO5LaH.mjs +0 -80
  47. package/dist/context-B4yO5LaH.mjs.map +0 -1
  48. package/dist/effect-CZdZCZLW.mjs.map +0 -1
  49. package/dist/errors-BXKc7juX.d.mts +0 -8
  50. package/dist/errors-BXKc7juX.d.mts.map +0 -1
  51. package/dist/index-EJlskfAW.d.mts.map +0 -1
  52. package/dist/signal-Cl9tqGyX.mjs.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bruno Alves
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -10,6 +10,10 @@ Use Services directly inside `Effect.fn` Programs (or eager `Effect.gen` workflo
10
10
  bun add better-effect better-result
11
11
  ```
12
12
 
13
+ The published Runtime entrypoint is officially supported on Node.js and Bun.
14
+ The tested runtime matrix is Node.js 24 and Bun 1.3.14, and the default runtime
15
+ context uses Node/Bun async context propagation.
16
+
13
17
  ## TypeScript knows what your application needs
14
18
 
15
19
  ```ts
@@ -109,14 +113,20 @@ const inspectDatabase = Effect.fn(async function* () {
109
113
  await runtime.run(inspectDatabase)
110
114
  ```
111
115
 
116
+ Runtime boundaries inspect only nominal `better-result` values: `Result.err`
117
+ becomes a failed execution, while a plain object with `status: 'error'` is
118
+ still a successful value. Intermediate Results do not close the execution
119
+ Scope.
120
+
112
121
  `Effect.gen` remains eager for code that already runs inside a resolver and
113
122
  Scope. `Effect.fn` captures the generator as a lazy `Program` for Runtime
114
123
  boundaries; the callback form remains supported for compatibility.
115
124
 
116
125
  `Program.all` keeps a collection lazy until the returned Program is run. Pass
117
126
  `{ concurrency: n }` for a positive bounded FIFO worker pool; values retain
118
- input order, and already-started Programs remain owned by the enclosing Runtime
119
- execution when one returns an error.
127
+ input order. If a Program returns an error or throws, scheduling stops, already-
128
+ started Programs are allowed to settle, and the deterministic primary failure remains selected;
129
+ there is no cancellation or Fiber scheduler.
120
130
 
121
131
  `Runtime.make(AppLive)` and `Runtime.run(AppLive, program)` use the built-in
122
132
  `MapLayerBackend`. Pass `{ backend: new ItiLayerBackend() }` when an external
@@ -168,7 +178,8 @@ const runtime = await Runtime.make(AppLive, {
168
178
 
169
179
  Cancellation is cooperative and uses `AbortSignal`; no scheduler or fibers are
170
180
  created. Pass a signal to one execution and read it from the program when an
171
- I/O operation supports cancellation:
181
+ I/O operation supports cancellation. Runtime disposal waits for active work;
182
+ it does not forcibly terminate arbitrary Promises:
172
183
 
173
184
  ```ts
174
185
  const result = await runtime.run(program, { signal: request.signal })
@@ -186,7 +197,9 @@ const result = await runtime.runWith(RequestLive, handleRequest)
186
197
  ```
187
198
 
188
199
  The request Layer may use root Services, while its scoped providers are closed
189
- with the execution Scope and never change the shared Runtime environment.
200
+ with the execution Scope and never change the shared Runtime environment. Its
201
+ external requirements must be provided by the Runtime, and the Hono failure
202
+ handler/request-Layer types are checked at the adapter boundary.
190
203
 
191
204
  ### Hono request boundaries
192
205
 
@@ -201,7 +214,7 @@ import { Result } from 'better-result'
201
214
  import { HonoEffect } from 'better-effect/hono'
202
215
 
203
216
  const http = HonoEffect.make(runtime, {
204
- onFailure: (error, c) => c.json({ error: String(error) }, 400)
217
+ onFailure: (_error, c) => c.json({ error: 'Request failed' }, 400)
205
218
  })
206
219
  const app = new Hono()
207
220
 
@@ -216,9 +229,9 @@ app.get(
216
229
  )
217
230
  ```
218
231
 
219
- Hono validators can be passed as the first argument to `gen` or `handler`.
220
- Their validated `c.req.valid(...)` input is then inferred without a manual
221
- `Input` helper:
232
+ Hono validators can precede the generator or handler callback. Their validated
233
+ `c.req.valid(...)` inputs are combined and inferred without a manual `Input`
234
+ helper:
222
235
 
223
236
  ```ts
224
237
  import { sValidator } from '@hono/standard-validator'
@@ -239,17 +252,40 @@ app.post(
239
252
  )
240
253
  ```
241
254
 
255
+ For multiple validators, pass them in order before the callback:
256
+
257
+ ```ts
258
+ app.post(
259
+ '/work-orders/:id',
260
+ http.gen(validateParam, validateHeader, validateCreateWorkOrder, async function* (c) {
261
+ const id = c.req.valid('param').id
262
+ const key = c.req.valid('header')['X-Idempotency-Key']
263
+ const input = c.req.valid('json')
264
+ return Result.ok({ id, key, input })
265
+ })
266
+ )
267
+ ```
268
+
242
269
  The validator middleware runs before the Program and short-circuits with its
243
- own `Response` when validation fails. `http.handler` accepts the same
244
- `(validator, programFactory, options?)` form.
270
+ own `Response` when validation fails. `http.handler` accepts the same ordered
271
+ validator arguments followed by the program factory and options.
245
272
 
246
273
  Install `hono` only when this subpath is used. The main entrypoint does not
247
- load the framework.
248
-
249
- Service and Scope access share one `RuntimeContext`. Node/Bun uses
250
- `AsyncLocalStorage` by default; hosts without transparent async context can
251
- pass `contextStorage: new ExplicitRuntimeContextStorage()` from
252
- `better-effect/runtime/explicit`.
274
+ load the framework. The default Hono failure policy redacts every non-`Response`
275
+ failure value to `{ error: 'Internal Server Error' }` with status 500. An
276
+ explicitly returned `Response` failure is intentionally passed through
277
+ unchanged. A custom `onFailure` policy should serialize only safe, intentional
278
+ domain details.
279
+
280
+ Service and Scope access share one `RuntimeContext`. The published Runtime
281
+ entrypoint is officially supported on Node.js and Bun, where `AsyncLocalStorage`
282
+ is the default context storage. The `better-effect/runtime/explicit` subpath
283
+ provides `ExplicitRuntimeContextStorage` as a manually managed, sequential
284
+ strategy only when the package entrypoint and host can load it. One instance
285
+ supports one non-overlapping async flow and rejects concurrent overlap. Using
286
+ it does not make the package generally usable in browsers, Deno, Cloudflare
287
+ Workers, or other non-Node hosts; separate explicit instances are not a general
288
+ concurrent-isolation strategy.
253
289
 
254
290
  If a program asks that Runtime for a Service its environment does not provide, TypeScript rejects the call.
255
291
 
@@ -1,6 +1,6 @@
1
- import { N as AnyServiceToken } from "../index-1NLNdkJy.mjs";
2
- import "../index-EJlskfAW.mjs";
3
- import { a as LayerRegistration, n as LayerBackend } from "../map-layer-backend-DMJauecV.mjs";
1
+ import { N as AnyServiceToken } from "../index-CULgSzUw.mjs";
2
+ import "../index-C7KX5rAP.mjs";
3
+ import { a as LayerRegistration, n as LayerBackend } from "../map-layer-backend-rNwfH0Bz.mjs";
4
4
  //#region src/adapters/iti.d.ts
5
5
  /**
6
6
  * ITI-backed Layer backend.
@@ -12,12 +12,18 @@ declare class ItiLayerBackend implements LayerBackend {
12
12
  private container;
13
13
  private readonly keys;
14
14
  private readonly registered;
15
+ /**
16
+ * Track async gets so disposal cannot reset ITI while a provider is acquiring.
17
+ * ITI caches rejected acquisitions; replacing the container is the explicit
18
+ * retry boundary for that sticky failure behavior.
19
+ */
20
+ private readonly pending;
15
21
  private keyFor;
16
22
  /** Register a Layer provider under its deterministic Service-tag key. */
17
23
  register(registration: LayerRegistration): void;
18
24
  /** Resolve a registered Service through the ITI container. */
19
25
  resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>;
20
- /** Dispose all ITI-managed provider instances. */
26
+ /** Reset container-owned ITI state; Scope owns Layer provider releases. */
21
27
  disposeAll(): Promise<void>;
22
28
  }
23
29
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"iti.d.mts","names":[],"sources":["../../src/adapters/iti.ts"],"mappings":";;;;;;;;;;cAsBa,2BAA2B;UAC9B;mBAES;mBAEA;UAET;;EAgBR,SAAS,cAAc;;EAuBvB,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;;EAyBnF,cAAc"}
1
+ {"version":3,"file":"iti.d.mts","names":[],"sources":["../../src/adapters/iti.ts"],"mappings":";;;;;;;;;;cAsBa,2BAA2B;UAC9B;mBAES;mBAEA;;;;;;mBAOA;UAET;;EAgBR,SAAS,cAAc;;EAuBvB,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;;EAiCnF,cAAc"}
@@ -1,6 +1,6 @@
1
1
  import { a as ServiceTagCollisionError, c as ServiceNotFoundError, t as DuplicateServiceError } from "../errors-Dnjhzbt0.mjs";
2
- import { t as assertServiceCompatibility } from "../internal-identity-DmUpBeeL.mjs";
3
2
  import { t as isPromiseLike } from "../runtime-CDcCF5cb.mjs";
3
+ import { t as assertServiceCompatibility } from "../internal-identity-DmUpBeeL.mjs";
4
4
  import { createContainer } from "iti";
5
5
  //#region src/adapters/iti.ts
6
6
  /**
@@ -13,6 +13,12 @@ var ItiLayerBackend = class {
13
13
  container = createContainer();
14
14
  keys = /* @__PURE__ */ new Map();
15
15
  registered = /* @__PURE__ */ new Map();
16
+ /**
17
+ * Track async gets so disposal cannot reset ITI while a provider is acquiring.
18
+ * ITI caches rejected acquisitions; replacing the container is the explicit
19
+ * retry boundary for that sticky failure behavior.
20
+ */
21
+ pending = /* @__PURE__ */ new Set();
16
22
  keyFor(token) {
17
23
  const tag = token.serviceTag;
18
24
  const existing = this.keys.get(tag);
@@ -42,12 +48,26 @@ var ItiLayerBackend = class {
42
48
  assertServiceCompatibility(token, registered, instance);
43
49
  return instance;
44
50
  };
45
- if (isPromiseLike(resolved)) return Promise.resolve(resolved).then(validate);
51
+ if (isPromiseLike(resolved)) {
52
+ const pending = Promise.resolve(resolved).then(validate);
53
+ this.pending.add(pending);
54
+ pending.then(() => this.pending.delete(pending), () => this.pending.delete(pending));
55
+ return pending;
56
+ }
46
57
  return validate(resolved);
47
58
  }
48
- /** Dispose all ITI-managed provider instances. */
59
+ /** Reset container-owned ITI state; Scope owns Layer provider releases. */
49
60
  async disposeAll() {
50
- await this.container.disposeAll();
61
+ const container = this.container;
62
+ try {
63
+ await Promise.allSettled(this.pending);
64
+ await container.disposeAll();
65
+ } finally {
66
+ this.container = createContainer();
67
+ this.registered.clear();
68
+ this.keys.clear();
69
+ this.pending.clear();
70
+ }
51
71
  }
52
72
  };
53
73
  //#endregion
@@ -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 private keyFor(token: AnyServiceToken): string {\n const tag = token.serviceTag\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 token = registration.service\n const tag = token.serviceTag\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(token)\n\n this.container = this.container.add({\n [key]: registration.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 registered = this.registered.get(token.serviceTag)\n\n if (registered === undefined) {\n throw new ServiceNotFoundError(token)\n }\n\n const key = this.keyFor(token)\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 return Promise.resolve(resolved).then(validate)\n }\n\n return validate(resolved)\n }\n\n /** Dispose all ITI-managed provider instances. */\n async disposeAll(): Promise<void> {\n await this.container.disposeAll()\n }\n}\n"],"mappings":";;;;;;;;;;;AAsBA,IAAa,kBAAb,MAAqD;CACnD,YAAyB,gBAAgB;CAEzC,uBAAwB,IAAI,IAAoB;CAEhD,6BAA8B,IAAI,IAA6B;CAE/D,OAAe,OAAgC;EAC7C,MAAM,MAAM,MAAM;EAClB,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,QAAQ,aAAa;EAC3B,MAAM,MAAM,MAAM;EAClB,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,KAAK;EAE7B,KAAK,YAAY,KAAK,UAAU,IAAI,GACjC,MAAM,aAAa,QACtB,CAAC;EAED,KAAK,WAAW,IAAI,KAAK,KAAK;CAChC;;CAGA,QAAmC,OAA0D;EAC3F,MAAM,aAAa,KAAK,WAAW,IAAI,MAAM,UAAU;EAEvD,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,qBAAqB,KAAK;EAGtC,MAAM,MAAM,KAAK,OAAO,KAAK;EAC7B,MAAM,WAAW,KAAK,UAAU,IAAI,GAAG;EAEvC,MAAM,YAAY,aAAkD;GAClE,2BAA2B,OAAO,YAAY,QAAQ;GAGtD,OAAO;EACT;EAEA,IAAI,cAAc,QAAQ,GACxB,OAAO,QAAQ,QAAQ,QAAQ,CAAC,CAAC,KAAK,QAAQ;EAGhD,OAAO,SAAS,QAAQ;CAC1B;;CAGA,MAAM,aAA4B;EAChC,MAAM,KAAK,UAAU,WAAW;CAClC;AACF"}
1
+ {"version":3,"file":"iti.mjs","names":[],"sources":["../../src/adapters/iti.ts"],"sourcesContent":["import { createContainer } from 'iti'\n\nimport {\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 Set<Promise<unknown>>()\n\n private keyFor(token: AnyServiceToken): string {\n const tag = token.serviceTag\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 token = registration.service\n const tag = token.serviceTag\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(token)\n\n this.container = this.container.add({\n [key]: registration.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 registered = this.registered.get(token.serviceTag)\n\n if (registered === undefined) {\n throw new ServiceNotFoundError(token)\n }\n\n const key = this.keyFor(token)\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.add(pending)\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(): Promise<void> {\n const container = this.container\n\n try {\n await Promise.allSettled(this.pending)\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":";;;;;;;;;;;AAsBA,IAAa,kBAAb,MAAqD;CACnD,YAAyB,gBAAgB;CAEzC,uBAAwB,IAAI,IAAoB;CAEhD,6BAA8B,IAAI,IAA6B;;;;;;CAO/D,0BAA2B,IAAI,IAAsB;CAErD,OAAe,OAAgC;EAC7C,MAAM,MAAM,MAAM;EAClB,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,QAAQ,aAAa;EAC3B,MAAM,MAAM,MAAM;EAClB,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,KAAK;EAE7B,KAAK,YAAY,KAAK,UAAU,IAAI,GACjC,MAAM,aAAa,QACtB,CAAC;EAED,KAAK,WAAW,IAAI,KAAK,KAAK;CAChC;;CAGA,QAAmC,OAA0D;EAC3F,MAAM,aAAa,KAAK,WAAW,IAAI,MAAM,UAAU;EAEvD,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,qBAAqB,KAAK;EAGtC,MAAM,MAAM,KAAK,OAAO,KAAK;EAC7B,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,OAAO;GACxB,QAAa,WACL,KAAK,QAAQ,OAAO,OAAO,SAC3B,KAAK,QAAQ,OAAO,OAAO,CACnC;GAEA,OAAO;EACT;EAEA,OAAO,SAAS,QAAQ;CAC1B;;CAGA,MAAM,aAA4B;EAChC,MAAM,YAAY,KAAK;EAEvB,IAAI;GACF,MAAM,QAAQ,WAAW,KAAK,OAAO;GACrC,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,152 @@
1
+ import { t as isPromiseLike } from "./runtime-CDcCF5cb.mjs";
2
+ //#region src/runtime/errors.ts
3
+ /** Thrown when no RuntimeContext is active in the selected storage. */
4
+ var RuntimeContextNotConfiguredError = class extends Error {
5
+ constructor() {
6
+ super("No RuntimeContext is available in the current execution context");
7
+ this.name = "RuntimeContextNotConfiguredError";
8
+ }
9
+ };
10
+ /** Thrown when ExplicitRuntimeContextStorage cannot safely overlap lineages. */
11
+ var RuntimeContextOverlapError = class extends Error {
12
+ constructor() {
13
+ super("ExplicitRuntimeContextStorage does not support overlapping root or derived context runs; use NodeRuntimeContextStorage for concurrent async branches");
14
+ this.name = "RuntimeContextOverlapError";
15
+ }
16
+ };
17
+ //#endregion
18
+ //#region src/runtime/context.ts
19
+ const unconfiguredRuntimeContextStorage = {
20
+ run: (_context, program) => program(),
21
+ current: () => {
22
+ throw new RuntimeContextNotConfiguredError();
23
+ }
24
+ };
25
+ const contextLineages = /* @__PURE__ */ new WeakMap();
26
+ /** Associate a derived context with the root context of its execution lineage. */
27
+ const inheritRuntimeContextLineage = (context, parent) => {
28
+ const lineage = contextLineages.get(parent) ?? parent;
29
+ contextLineages.set(context, lineage);
30
+ return context;
31
+ };
32
+ /** Return the root context associated with a contextual value, when known. */
33
+ const getRuntimeContextLineage = (context) => contextLineages.get(context);
34
+ /** Associate a newly-entered root context with itself. */
35
+ const setRuntimeContextLineage = (context) => {
36
+ contextLineages.set(context, context);
37
+ return context;
38
+ };
39
+ const hasPathPrefix = (parent, child) => child.length >= parent.length && parent.every((token, index) => child[index] === token);
40
+ /** Recognize contexts created by a nested Service, Scope, or Layer operation. */
41
+ const isDerivedRuntimeContext = (parent, child) => parent.resolver !== void 0 && child.resolver === parent.resolver && parent.resolutionPath.length > 0 && hasPathPrefix(parent.resolutionPath, child.resolutionPath) && (child.resolutionPath.length > parent.resolutionPath.length || child.scope !== parent.scope);
42
+ function makeRuntimeContext(resolver, scope, resolutionPath, signal, parent) {
43
+ const context = { resolutionPath };
44
+ if (resolver !== void 0) Object.assign(context, { resolver });
45
+ if (scope !== void 0) Object.assign(context, { scope });
46
+ if (signal !== void 0) Object.assign(context, { signal });
47
+ if (parent !== void 0) inheritRuntimeContextLineage(context, parent);
48
+ return context;
49
+ }
50
+ let defaultStorage = unconfiguredRuntimeContextStorage;
51
+ let fallbackFrame;
52
+ const fallbackFrames = /* @__PURE__ */ new Set();
53
+ const settleWithRuntimeStorage = (value, restore) => {
54
+ let promiseLike;
55
+ try {
56
+ promiseLike = isPromiseLike(value);
57
+ } catch (cause) {
58
+ restore();
59
+ throw cause;
60
+ }
61
+ if (!promiseLike) {
62
+ restore();
63
+ return value;
64
+ }
65
+ try {
66
+ return Promise.resolve(value).then((resolved) => {
67
+ restore();
68
+ return resolved;
69
+ }, (cause) => {
70
+ restore();
71
+ throw cause;
72
+ });
73
+ } catch (cause) {
74
+ restore();
75
+ throw cause;
76
+ }
77
+ };
78
+ const restoreFallbackFrame = (frame) => {
79
+ fallbackFrames.delete(frame);
80
+ if (fallbackFrame !== frame) return;
81
+ let parent = frame.parent;
82
+ while (parent !== void 0 && !fallbackFrames.has(parent)) parent = parent.parent;
83
+ fallbackFrame = parent;
84
+ };
85
+ const fallbackRuntimeContextFrameCarrier = {
86
+ run(frame, program) {
87
+ fallbackFrames.add(frame);
88
+ fallbackFrame = frame;
89
+ const restore = () => restoreFallbackFrame(frame);
90
+ try {
91
+ return settleWithRuntimeStorage(program(), restore);
92
+ } catch (cause) {
93
+ restore();
94
+ throw cause;
95
+ }
96
+ },
97
+ current: () => fallbackFrame
98
+ };
99
+ let runtimeContextFrameCarrier = fallbackRuntimeContextFrameCarrier;
100
+ /** Install host async-context propagation for Runtime context frames. */
101
+ const setRuntimeContextFrameCarrier = (carrier) => {
102
+ runtimeContextFrameCarrier = carrier;
103
+ };
104
+ /** Return whether a host async-context carrier has been installed. */
105
+ const isRuntimeContextFrameCarrierInstalled = () => runtimeContextFrameCarrier !== fallbackRuntimeContextFrameCarrier;
106
+ /** Return the frame associated with the executing callback. */
107
+ const currentRuntimeContextFrame = () => runtimeContextFrameCarrier.current();
108
+ /** Install the host default used by the main Runtime entrypoint. */
109
+ const setDefaultRuntimeContextStorage = (storage) => {
110
+ defaultStorage = storage;
111
+ };
112
+ /** Return the storage currently associated with the executing callback. */
113
+ const activeRuntimeContextStorage = () => currentRuntimeContextFrame()?.storage ?? defaultStorage;
114
+ /** Return the active context, or undefined when the storage has not been entered. */
115
+ const getRuntimeContext = (storage = activeRuntimeContextStorage()) => {
116
+ const frame = currentRuntimeContextFrame();
117
+ if (frame?.storage === storage) return frame.context;
118
+ try {
119
+ return storage.current();
120
+ } catch (cause) {
121
+ if (cause instanceof RuntimeContextNotConfiguredError) return;
122
+ throw cause;
123
+ }
124
+ };
125
+ /** Return the active context or throw the storage's standard missing-context error. */
126
+ const currentRuntimeContext = () => {
127
+ return currentRuntimeContextFrame()?.context ?? defaultStorage.current();
128
+ };
129
+ /** Keep a storage discoverable to Service and Scope compatibility bridges. */
130
+ const withActiveRuntimeContextStorage = (storage, context, program) => {
131
+ const current = currentRuntimeContextFrame();
132
+ if (current?.storage === storage && current.context === context) return program();
133
+ const frame = current === void 0 ? {
134
+ storage,
135
+ context
136
+ } : {
137
+ storage,
138
+ context,
139
+ parent: current
140
+ };
141
+ return runtimeContextFrameCarrier.run(frame, program);
142
+ };
143
+ /** Run a callback in a context while keeping the selected storage discoverable to bridges. */
144
+ const runRuntimeContext = (storage, context, program) => {
145
+ const current = getRuntimeContext(storage);
146
+ const contextual = current !== void 0 && isDerivedRuntimeContext(current, context) ? inheritRuntimeContextLineage(context, current) : context;
147
+ return storage.run(contextual, () => withActiveRuntimeContextStorage(storage, contextual, program));
148
+ };
149
+ //#endregion
150
+ export { getRuntimeContextLineage as a, isRuntimeContextFrameCarrierInstalled as c, setDefaultRuntimeContextStorage as d, setRuntimeContextFrameCarrier as f, RuntimeContextOverlapError as g, RuntimeContextNotConfiguredError as h, getRuntimeContext as i, makeRuntimeContext as l, withActiveRuntimeContextStorage as m, currentRuntimeContext as n, inheritRuntimeContextLineage as o, setRuntimeContextLineage as p, currentRuntimeContextFrame as r, isDerivedRuntimeContext as s, activeRuntimeContextStorage as t, runRuntimeContext as u };
151
+
152
+ //# sourceMappingURL=context-BUEf1qjL.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-BUEf1qjL.mjs","names":[],"sources":["../src/runtime/errors.ts","../src/runtime/context.ts"],"sourcesContent":["/** Thrown when no RuntimeContext is active in the selected storage. */\nexport class RuntimeContextNotConfiguredError extends Error {\n constructor() {\n super('No RuntimeContext is available in the current execution context')\n\n this.name = 'RuntimeContextNotConfiguredError'\n }\n}\n\n/** Thrown when ExplicitRuntimeContextStorage cannot safely overlap lineages. */\nexport class RuntimeContextOverlapError extends Error {\n constructor() {\n super(\n 'ExplicitRuntimeContextStorage does not support overlapping root or derived context runs; use NodeRuntimeContextStorage for concurrent async branches'\n )\n\n this.name = 'RuntimeContextOverlapError'\n }\n}\n","import { RuntimeContextNotConfiguredError } from './errors'\n\nimport type { Scope } from '../scope/scope'\nimport type { AnyServiceToken, ServiceResolver } from '../service'\nimport { isPromiseLike } from '../utils/runtime'\n\n/** Contextual state shared by Service, Scope and Layer resolution. */\nexport interface RuntimeContext {\n readonly resolver?: ServiceResolver\n readonly scope?: Scope\n readonly signal?: AbortSignal\n readonly resolutionPath: readonly AnyServiceToken[]\n}\n\n/** A context with both channels required by a Runtime execution. */\nexport type CompleteRuntimeContext = RuntimeContext & {\n readonly resolver: ServiceResolver\n readonly scope: Scope\n}\n\n/** Host-specific storage for the current RuntimeContext. */\nexport interface RuntimeContextStorage {\n run<A>(context: RuntimeContext, program: () => A): A\n current(): RuntimeContext\n}\n\nconst unconfiguredRuntimeContextStorage: RuntimeContextStorage = {\n run: (_context, program) => program(),\n current: () => {\n throw new RuntimeContextNotConfiguredError()\n }\n}\n\nconst contextLineages = new WeakMap<object, RuntimeContext>()\n\n/** Associate a derived context with the root context of its execution lineage. */\nexport const inheritRuntimeContextLineage = (\n context: RuntimeContext,\n parent: RuntimeContext\n): RuntimeContext => {\n const lineage = contextLineages.get(parent) ?? parent\n contextLineages.set(context, lineage)\n return context\n}\n\n/** Return the root context associated with a contextual value, when known. */\nexport const getRuntimeContextLineage = (context: RuntimeContext): RuntimeContext | undefined =>\n contextLineages.get(context)\n\n/** Associate a newly-entered root context with itself. */\nexport const setRuntimeContextLineage = (context: RuntimeContext): RuntimeContext => {\n contextLineages.set(context, context)\n return context\n}\n\nconst hasPathPrefix = (\n parent: readonly AnyServiceToken[],\n child: readonly AnyServiceToken[]\n): boolean =>\n child.length >= parent.length && parent.every((token, index) => child[index] === token)\n\n/** Recognize contexts created by a nested Service, Scope, or Layer operation. */\nexport const isDerivedRuntimeContext = (parent: RuntimeContext, child: RuntimeContext): boolean =>\n parent.resolver !== undefined &&\n child.resolver === parent.resolver &&\n parent.resolutionPath.length > 0 &&\n hasPathPrefix(parent.resolutionPath, child.resolutionPath) &&\n (child.resolutionPath.length > parent.resolutionPath.length || child.scope !== parent.scope)\n\n/** Build a context while retaining only channels that are actually available. */\nexport function makeRuntimeContext(\n resolver: ServiceResolver,\n scope: Scope,\n resolutionPath: readonly AnyServiceToken[],\n signal: AbortSignal | undefined,\n parent?: RuntimeContext\n): CompleteRuntimeContext\nexport function makeRuntimeContext(\n resolver: ServiceResolver | undefined,\n scope: Scope | undefined,\n resolutionPath: readonly AnyServiceToken[],\n signal: AbortSignal | undefined,\n parent?: RuntimeContext\n): RuntimeContext\nexport function makeRuntimeContext(\n resolver: ServiceResolver | undefined,\n scope: Scope | undefined,\n resolutionPath: readonly AnyServiceToken[],\n signal: AbortSignal | undefined,\n parent?: RuntimeContext\n): RuntimeContext {\n const context: RuntimeContext = { resolutionPath }\n\n if (resolver !== undefined) {\n Object.assign(context, { resolver })\n }\n\n if (scope !== undefined) {\n Object.assign(context, { scope })\n }\n\n if (signal !== undefined) {\n Object.assign(context, { signal })\n }\n\n if (parent !== undefined) {\n inheritRuntimeContextLineage(context, parent)\n }\n\n return context\n}\n\n/** A context frame selected for one logical Runtime execution branch. */\nexport type ActiveRuntimeContextFrame = {\n readonly storage: RuntimeContextStorage\n readonly context: RuntimeContext\n readonly parent?: ActiveRuntimeContextFrame\n}\n\n/** Host-provided propagation for Runtime context frames. */\nexport interface RuntimeContextFrameCarrier {\n run<A>(frame: ActiveRuntimeContextFrame, program: () => A): A\n current(): ActiveRuntimeContextFrame | undefined\n}\n\nlet defaultStorage = unconfiguredRuntimeContextStorage\nlet fallbackFrame: ActiveRuntimeContextFrame | undefined\nconst fallbackFrames = new Set<ActiveRuntimeContextFrame>()\n\nconst settleWithRuntimeStorage = <A>(value: A, restore: () => void): A => {\n let promiseLike: boolean\n\n try {\n promiseLike = isPromiseLike(value)\n } catch (cause) {\n restore()\n throw cause\n }\n\n if (!promiseLike) {\n restore()\n return value\n }\n\n try {\n // SAFETY: PromiseLike values are normalized only to restore the storage after settlement; the public generic retains the callback's awaited shape.\n return Promise.resolve(value).then(\n (resolved) => {\n restore()\n return resolved\n },\n (cause) => {\n restore()\n throw cause\n }\n ) as A\n } catch (cause) {\n restore()\n throw cause\n }\n}\n\nconst restoreFallbackFrame = (frame: ActiveRuntimeContextFrame): void => {\n fallbackFrames.delete(frame)\n\n if (fallbackFrame !== frame) {\n return\n }\n\n let parent = frame.parent\n\n while (parent !== undefined && !fallbackFrames.has(parent)) {\n parent = parent.parent\n }\n\n fallbackFrame = parent\n}\n\nconst fallbackRuntimeContextFrameCarrier: RuntimeContextFrameCarrier = {\n run<A>(frame: ActiveRuntimeContextFrame, program: () => A): A {\n fallbackFrames.add(frame)\n fallbackFrame = frame\n const restore = (): void => restoreFallbackFrame(frame)\n\n try {\n return settleWithRuntimeStorage(program(), restore)\n } catch (cause) {\n restore()\n throw cause\n }\n },\n\n current: () => fallbackFrame\n}\n\nlet runtimeContextFrameCarrier: RuntimeContextFrameCarrier = fallbackRuntimeContextFrameCarrier\n\n/** Install host async-context propagation for Runtime context frames. */\nexport const setRuntimeContextFrameCarrier = (carrier: RuntimeContextFrameCarrier): void => {\n runtimeContextFrameCarrier = carrier\n}\n\n/** Return whether a host async-context carrier has been installed. */\nexport const isRuntimeContextFrameCarrierInstalled = (): boolean =>\n runtimeContextFrameCarrier !== fallbackRuntimeContextFrameCarrier\n\n/** Return the frame associated with the executing callback. */\nexport const currentRuntimeContextFrame = (): ActiveRuntimeContextFrame | undefined =>\n runtimeContextFrameCarrier.current()\n\n/** Install the host default used by the main Runtime entrypoint. */\nexport const setDefaultRuntimeContextStorage = (storage: RuntimeContextStorage): void => {\n defaultStorage = storage\n}\n\n/** Return the storage currently associated with the executing callback. */\nexport const activeRuntimeContextStorage = (): RuntimeContextStorage =>\n currentRuntimeContextFrame()?.storage ?? defaultStorage\n\n/** Return the active context, or undefined when the storage has not been entered. */\nexport const getRuntimeContext = (\n storage: RuntimeContextStorage = activeRuntimeContextStorage()\n): RuntimeContext | undefined => {\n const frame = currentRuntimeContextFrame()\n\n if (frame?.storage === storage) {\n return frame.context\n }\n\n try {\n return storage.current()\n } catch (cause) {\n if (cause instanceof RuntimeContextNotConfiguredError) {\n return undefined\n }\n\n throw cause\n }\n}\n\n/** Return the active context or throw the storage's standard missing-context error. */\nexport const currentRuntimeContext = (): RuntimeContext => {\n const frame = currentRuntimeContextFrame()\n\n return frame?.context ?? defaultStorage.current()\n}\n\n/** Keep a storage discoverable to Service and Scope compatibility bridges. */\nexport const withActiveRuntimeContextStorage = <A>(\n storage: RuntimeContextStorage,\n context: RuntimeContext,\n program: () => A\n): A => {\n const current = currentRuntimeContextFrame()\n\n if (current?.storage === storage && current.context === context) {\n return program()\n }\n\n const frame: ActiveRuntimeContextFrame =\n current === undefined ? { storage, context } : { storage, context, parent: current }\n\n return runtimeContextFrameCarrier.run(frame, program)\n}\n\n/** Run a callback in a context while keeping the selected storage discoverable to bridges. */\nexport const runRuntimeContext = <A>(\n storage: RuntimeContextStorage,\n context: RuntimeContext,\n program: () => A\n): A => {\n const current = getRuntimeContext(storage)\n const contextual =\n current !== undefined && isDerivedRuntimeContext(current, context)\n ? inheritRuntimeContextLineage(context, current)\n : context\n\n return storage.run(contextual, () =>\n withActiveRuntimeContextStorage(storage, contextual, program)\n )\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { a as runRuntimeContext, i as makeRuntimeContext, n as currentRuntimeContext, r as getRuntimeContext, t as activeRuntimeContextStorage } from "./context-B4yO5LaH.mjs";
1
+ import { h as RuntimeContextNotConfiguredError, i as getRuntimeContext, l as makeRuntimeContext, n as currentRuntimeContext, t as activeRuntimeContextStorage, u as runRuntimeContext } from "./context-BUEf1qjL.mjs";
2
2
  import { t as isPromiseLike } from "./runtime-CDcCF5cb.mjs";
3
- import { Result } from "better-result";
3
+ import { Err, Result } from "better-result";
4
4
  //#region src/scope/errors.ts
5
5
  /** Thrown when Scope context is accessed outside an active Scope execution. */
6
6
  var ScopeRuntimeNotConfiguredError = class extends Error {
@@ -56,7 +56,7 @@ var ScopeRuntime = class {
56
56
  static run(scope, program, storage = scopeStorages.get(scope) ?? activeRuntimeContextStorage()) {
57
57
  scopeStorages.set(scope, storage);
58
58
  const current = getRuntimeContext(storage);
59
- const context = makeRuntimeContext(current?.resolver, scope, current?.resolutionPath ?? [], current?.signal);
59
+ const context = makeRuntimeContext(current?.resolver, scope, current?.resolutionPath ?? [], current?.signal, current);
60
60
  return runRuntimeContext(storage, context, program);
61
61
  }
62
62
  /** Return the Scope active in the current execution context. */
@@ -64,8 +64,9 @@ var ScopeRuntime = class {
64
64
  let context;
65
65
  try {
66
66
  context = currentRuntimeContext();
67
- } catch {
68
- throw new ScopeRuntimeNotConfiguredError();
67
+ } catch (cause) {
68
+ if (cause instanceof RuntimeContextNotConfiguredError) throw new ScopeRuntimeNotConfiguredError();
69
+ throw cause;
69
70
  }
70
71
  if (!context.scope) throw new ScopeRuntimeNotConfiguredError();
71
72
  return context.scope;
@@ -94,10 +95,27 @@ const runScoped = async (scope, program, options) => {
94
95
  programFailed = true;
95
96
  programFailure = cause;
96
97
  }
97
- const outcome = programFailed ? {
98
- status: "failure",
99
- cause: programFailure
100
- } : options.classify(value);
98
+ let outcome;
99
+ let outcomeStatus;
100
+ if (programFailed) {
101
+ outcome = {
102
+ status: "failure",
103
+ cause: programFailure
104
+ };
105
+ outcomeStatus = "failure";
106
+ } else try {
107
+ outcome = options.classify(value);
108
+ outcomeStatus = outcome.status;
109
+ } catch (cause) {
110
+ programFailed = true;
111
+ programFailure = cause;
112
+ outcome = {
113
+ status: "failure",
114
+ cause
115
+ };
116
+ outcomeStatus = "failure";
117
+ }
118
+ options.onOutcome?.(outcome);
101
119
  let cleanupFailed = false;
102
120
  let cleanupFailure;
103
121
  try {
@@ -115,7 +133,7 @@ const runScoped = async (scope, program, options) => {
115
133
  cleanupFailure = error;
116
134
  }
117
135
  if (programFailed) throw programFailure;
118
- if (outcome.status === "failure") return value;
136
+ if (outcomeStatus === "failure") return value;
119
137
  if (cleanupFailed) throw cleanupFailure;
120
138
  return value;
121
139
  };
@@ -419,15 +437,19 @@ function programAll(programs, options = {}) {
419
437
  const failures = Array.from({ length: programs.length }, () => false);
420
438
  const causes = Array.from({ length: programs.length });
421
439
  let nextIndex = 0;
440
+ let stopScheduling = false;
422
441
  const worker = async () => {
423
- while (true) {
442
+ while (!stopScheduling) {
424
443
  const index = nextIndex++;
425
444
  if (index >= programs.length) return;
426
445
  try {
427
- results[index] = await programs[index]();
446
+ const result = await programs[index]();
447
+ results[index] = result;
448
+ if (result instanceof Err) stopScheduling = true;
428
449
  } catch (cause) {
429
450
  failures[index] = true;
430
451
  causes[index] = cause;
452
+ stopScheduling = true;
431
453
  }
432
454
  }
433
455
  };
@@ -519,4 +541,4 @@ const Effect = {
519
541
  //#endregion
520
542
  export { ScopeRuntime as a, ScopeCloseError as c, runScoped as i, ScopeClosedError as l, Program as n, disposeResource as o, Scope as r, ResourceNotDisposableError as s, Effect as t, ScopeRuntimeNotConfiguredError as u };
521
543
 
522
- //# sourceMappingURL=effect-CZdZCZLW.mjs.map
544
+ //# sourceMappingURL=effect-DAMqvegy.mjs.map