better-effect 0.9.31 → 0.11.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 (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +277 -16
  3. package/dist/adapters/iti.d.mts +10 -5
  4. package/dist/adapters/iti.d.mts.map +1 -1
  5. package/dist/adapters/iti.mjs +30 -5
  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-2ZcGZI8A.mjs +513 -0
  10. package/dist/effect-2ZcGZI8A.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 +39 -21
  14. package/dist/hono.d.mts.map +1 -1
  15. package/dist/hono.mjs +25 -20
  16. package/dist/hono.mjs.map +1 -1
  17. package/dist/{index-BsPr7qHf.d.mts → index-C7Qild0_.d.mts} +158 -28
  18. package/dist/index-C7Qild0_.d.mts.map +1 -0
  19. package/dist/{index-CITM15SE.d.mts → index-DStz0JMN.d.mts} +4 -4
  20. package/dist/{index-CITM15SE.d.mts.map → index-DStz0JMN.d.mts.map} +1 -1
  21. package/dist/{index-1NLNdkJy.d.mts → index-heuaRmXR.d.mts} +8 -8
  22. package/dist/{index-1NLNdkJy.d.mts.map → index-heuaRmXR.d.mts.map} +1 -1
  23. package/dist/{index-EJlskfAW.d.mts → index-rQhZk3Nt.d.mts} +130 -23
  24. package/dist/index-rQhZk3Nt.d.mts.map +1 -0
  25. package/dist/index.d.mts +5 -6
  26. package/dist/index.mjs +6 -540
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/runtime/explicit.d.mts +11 -7
  29. package/dist/runtime/explicit.d.mts.map +1 -1
  30. package/dist/runtime/explicit.mjs +96 -23
  31. package/dist/runtime/explicit.mjs.map +1 -1
  32. package/dist/runtime/node.d.mts +2 -2
  33. package/dist/runtime/node.d.mts.map +1 -1
  34. package/dist/runtime/node.mjs +10 -6
  35. package/dist/runtime/node.mjs.map +1 -1
  36. package/dist/runtime-DnMn0X0X.mjs +613 -0
  37. package/dist/runtime-DnMn0X0X.mjs.map +1 -0
  38. package/dist/scope-GGnmTQck.mjs +245 -0
  39. package/dist/scope-GGnmTQck.mjs.map +1 -0
  40. package/dist/{signal-Cl9tqGyX.mjs → signal-B97cs85Z.mjs} +86 -6
  41. package/dist/signal-B97cs85Z.mjs.map +1 -0
  42. package/dist/{standard-services-QseopL9g.mjs → standard-services-BFBq-4lo.mjs} +2 -2
  43. package/dist/{standard-services-QseopL9g.mjs.map → standard-services-BFBq-4lo.mjs.map} +1 -1
  44. package/dist/standard-services.d.mts +2 -2
  45. package/dist/standard-services.mjs +2 -2
  46. package/dist/testing.d.mts +198 -2
  47. package/dist/testing.d.mts.map +1 -0
  48. package/dist/testing.mjs +976 -2
  49. package/dist/testing.mjs.map +1 -0
  50. package/package.json +6 -3
  51. package/dist/context-B4yO5LaH.mjs +0 -80
  52. package/dist/context-B4yO5LaH.mjs.map +0 -1
  53. package/dist/effect-CZdZCZLW.mjs +0 -522
  54. package/dist/effect-CZdZCZLW.mjs.map +0 -1
  55. package/dist/errors-BXKc7juX.d.mts +0 -8
  56. package/dist/errors-BXKc7juX.d.mts.map +0 -1
  57. package/dist/index-BsPr7qHf.d.mts.map +0 -1
  58. package/dist/index-EJlskfAW.d.mts.map +0 -1
  59. package/dist/map-layer-backend-DMJauecV.d.mts +0 -42
  60. package/dist/map-layer-backend-DMJauecV.d.mts.map +0 -1
  61. package/dist/map-layer-backend-gal-mcRv.mjs +0 -53
  62. package/dist/map-layer-backend-gal-mcRv.mjs.map +0 -1
  63. 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
@@ -95,6 +99,40 @@ const AppLive = Layer.merge(DatabaseLive, UserRepositoryLive)
95
99
  const runtime = await Runtime.make(AppLive)
96
100
  ```
97
101
 
102
+ Use `Layer.empty` when a composition root intentionally has no providers. It is
103
+ stable and has the exact `Layer<never, never>` type:
104
+
105
+ ```ts
106
+ const EmptyLive = Layer.empty
107
+ const runtime = await Runtime.make(EmptyLive)
108
+ await runtime.run(() => 'no Services required')
109
+ ```
110
+
111
+ For a port/adapter boundary, `Layer.alias` exposes one compatible implementation
112
+ under another Service token without constructing, cloning, or proxying it:
113
+
114
+ ```ts
115
+ class SqlUserRepository extends Service<SqlUserRepository>()('SqlUserRepository') {
116
+ findById(id: string): string {
117
+ return `sql:${id}`
118
+ }
119
+ }
120
+
121
+ class UserRepository extends Service<UserRepository>()('UserRepository') {
122
+ declare findById: SqlUserRepository['findById']
123
+ }
124
+
125
+ const UserRepositoryPort = Layer.alias({
126
+ from: SqlUserRepository,
127
+ to: UserRepository
128
+ })
129
+ const ApplicationLive = Layer.merge(Layer.empty, Layer.make(SqlUserRepository), UserRepositoryPort)
130
+ ```
131
+
132
+ The alias lazily resolves `from`, returns the same object under `to`, and checks
133
+ that the source satisfies the target's `Service.Contract`. Its source remains an
134
+ external Layer requirement until the source provider is composed.
135
+
98
136
  And the contract does not disappear after startup.
99
137
 
100
138
  A Runtime also knows which Services exist in its environment:
@@ -109,20 +147,117 @@ const inspectDatabase = Effect.fn(async function* () {
109
147
  await runtime.run(inspectDatabase)
110
148
  ```
111
149
 
150
+ Runtime boundaries inspect only nominal `better-result` values: `Result.err`
151
+ becomes a failed execution, while a plain object with `status: 'error'` is
152
+ still a successful value. Intermediate Results do not close the execution
153
+ Scope.
154
+
112
155
  `Effect.gen` remains eager for code that already runs inside a resolver and
113
156
  Scope. `Effect.fn` captures the generator as a lazy `Program` for Runtime
114
157
  boundaries; the callback form remains supported for compatibility.
115
158
 
116
159
  `Program.all` keeps a collection lazy until the returned Program is run. Pass
117
160
  `{ 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.
161
+ input order. If a Program returns an error or throws, scheduling stops, already-
162
+ started Programs are allowed to settle, and the deterministic primary failure remains selected;
163
+ there is no cancellation or Fiber scheduler.
164
+
165
+ Use `Program.forEach` when each item needs a lazy Program factory. The callback
166
+ receives the item and its input index, and the returned Program produces a
167
+ readonly collection in input order:
168
+
169
+ ```ts
170
+ const synchronized = Program.forEach(userIds, (userId, index) => synchronizeUser(userId, index), {
171
+ concurrency: 8
172
+ })
173
+
174
+ const result = await runtime.run(synchronized)
175
+ ```
176
+
177
+ Use `Program.allResults` when typed validation errors should be retained rather
178
+ than short-circuiting. It returns every exact child `Result` as a successful
179
+ collection element; defects still stop new work and reject the outer Program:
180
+
181
+ ```ts
182
+ const validations = Program.allResults(
183
+ [validateIdentity, validateAddress, validateDocuments] as const,
184
+ { concurrency: 3 }
185
+ )
186
+
187
+ const results = await runtime.run(validations)
188
+ ```
189
+
190
+ Both helpers use the same lazy bounded scheduler as `Program.all`: indexes are
191
+ claimed in order, output order is stable, and already-started work is always
192
+ allowed to settle.
193
+
194
+ Use `Effect.*` to transform an already-created Result, and `Program.*` to compose
195
+ an `Effect.fn` Program without starting it. `Program.map`, `mapError`, `tap`, and
196
+ `tapError` preserve that laziness; `andThen` and `recover` accept an Effect, a
197
+ Promise of an Effect, or another Program only after their matching Result branch
198
+ is selected. `Program.andThen` unions its source and continuation error and
199
+ Service requirement channels. `Program.recover` handles and removes the source
200
+ `Err` channel, exposes the recovery error channel, and unions the source and
201
+ recovery Service requirements. Taps preserve the original Result object on
202
+ success.
120
203
 
121
204
  `Runtime.make(AppLive)` and `Runtime.run(AppLive, program)` use the built-in
122
205
  `MapLayerBackend`. Pass `{ backend: new ItiLayerBackend() }` when an external
123
206
  container is needed; `MemoryLayerBackend` remains its compatibility alias from
124
207
  `better-effect/testing`.
125
208
 
209
+ ### Verify custom adapters
210
+
211
+ `better-effect/testing` provides runner-neutral conformance scenarios for
212
+ third-party `LayerBackend` and `RuntimeContextStorage` implementations. Each
213
+ scenario has a stable `name` and `run` callback, creates a fresh adapter, and
214
+ runs the optional adapter cleanup after every assertion outcome. Backends must
215
+ synchronously pass the actual readonly pending acquisition Promise collection to
216
+ `disposeAll`'s `onPendingAcquisitions` hook, await the callback, then await the
217
+ acquisitions before clearing state. Declare the backend's acquisition-failure
218
+ policy explicitly: `MapLayerBackend` retries,
219
+ while ITI keeps an asynchronous failure cached until disposal.
220
+
221
+ Register the scenarios with Bun:
222
+
223
+ ```ts
224
+ import { describe, test } from 'bun:test'
225
+ import { MapLayerBackend } from 'better-effect'
226
+ import { layerBackendContract } from 'better-effect/testing'
227
+
228
+ describe('My backend', () => {
229
+ for (const scenario of layerBackendContract({
230
+ makeBackend: () => new MapLayerBackend(),
231
+ acquisitionFailure: 'retry'
232
+ })) {
233
+ test(scenario.name, scenario.run)
234
+ }
235
+ })
236
+ ```
237
+
238
+ The same scenarios work with Vitest without adding a runner dependency to the
239
+ published entrypoint:
240
+
241
+ ```ts
242
+ import { describe, it } from 'vitest'
243
+ import { NodeRuntimeContextStorage } from 'better-effect/runtime/node'
244
+ import { runtimeContextStorageContract } from 'better-effect/testing'
245
+
246
+ describe('My context storage', () => {
247
+ for (const scenario of runtimeContextStorageContract({
248
+ makeStorage: () => new NodeRuntimeContextStorage(),
249
+ concurrency: 'concurrent'
250
+ })) {
251
+ it(scenario.name, scenario.run)
252
+ }
253
+ })
254
+ ```
255
+
256
+ Use `concurrency: 'sequential'` for a storage that rejects overlapping roots
257
+ with `RuntimeContextOverlapError`; pass `makeCompanionStorage` to also verify
258
+ that it does not leak frames into a Node or explicit storage. See the adapter
259
+ guide for the complete `LayerBackend` contract.
260
+
126
261
  Runtimes are async disposables, so request-scoped code can use:
127
262
 
128
263
  ```ts
@@ -136,6 +271,42 @@ Or let `Runtime.use` own the lifetime:
136
271
  const result = await Runtime.use(AppLive, (runtime) => runtime.run(program))
137
272
  ```
138
273
 
274
+ For isolated application tests, use the testing facade over that same Layer and
275
+ Runtime. It installs only the controlled Services you pass, records lifecycle
276
+ events, and disposes automatically:
277
+
278
+ ```ts
279
+ import { ClockTest, LoggerTest, TestRuntime } from 'better-effect/testing'
280
+
281
+ const logger = new LoggerTest()
282
+ const result = await TestRuntime.use(
283
+ AppLive,
284
+ {
285
+ overrides: [DatabaseTest],
286
+ clock: new ClockTest(Date.UTC(2026, 0, 1)),
287
+ logger
288
+ },
289
+ async (test) => {
290
+ const value = await test.run(loadDashboard)
291
+ expect(test.observer.executionEnds).toHaveLength(1)
292
+ return value
293
+ }
294
+ )
295
+
296
+ expect(logger.events).toHaveLength(1)
297
+ ```
298
+
299
+ A long-lived test boundary supports integration-style request Layers as well:
300
+
301
+ ```ts
302
+ await using test = await TestRuntime.make(AppLive, { overrides: [DatabaseTest] })
303
+ const result = await test.runWith(RequestLive, handleRequest)
304
+ ```
305
+
306
+ `TestRuntime.use` preserves program-vs-cleanup failure precedence. The default
307
+ recorder is available as `test.observer`; use `test.runtime` only when an
308
+ advanced test explicitly needs the underlying Runtime.
309
+
139
310
  Layer providers remain lazy unless startup validation is requested. Warm them
140
311
  all before accepting work with either form:
141
312
 
@@ -166,9 +337,38 @@ const runtime = await Runtime.make(AppLive, {
166
337
  })
167
338
  ```
168
339
 
340
+ For lifecycle assertions, `better-effect/testing` provides a recorder and a
341
+ best-effort composition utility:
342
+
343
+ ```ts
344
+ import { RecordedRuntimeObserver, RuntimeObserver } from 'better-effect/testing'
345
+
346
+ const recorded = RecordedRuntimeObserver.make()
347
+ const runtime = await Runtime.make(AppTest, {
348
+ observers: [
349
+ RuntimeObserver.compose(recorded, {
350
+ onExecutionEnd: ({ outcome }) => console.debug(outcome.status)
351
+ })
352
+ ]
353
+ })
354
+
355
+ await runtime.run(program)
356
+ const snapshot = recorded.snapshot()
357
+ expect(snapshot.executionEnds).toHaveLength(1)
358
+ expect(snapshot.timeline).toContain(snapshot.executionEnds[0])
359
+
360
+ await runtime.dispose()
361
+ ```
362
+
363
+ `RecordedRuntimeObserver` preserves event identity in immutable category views
364
+ and its ordered `timeline`; call `clear()` to reuse it. Composition invokes
365
+ observers in declaration order and isolates thrown or rejected observer
366
+ failures from the Runtime result.
367
+
169
368
  Cancellation is cooperative and uses `AbortSignal`; no scheduler or fibers are
170
369
  created. Pass a signal to one execution and read it from the program when an
171
- I/O operation supports cancellation:
370
+ I/O operation supports cancellation. Runtime disposal waits for active work;
371
+ it does not forcibly terminate arbitrary Promises:
172
372
 
173
373
  ```ts
174
374
  const result = await runtime.run(program, { signal: request.signal })
@@ -186,7 +386,9 @@ const result = await runtime.runWith(RequestLive, handleRequest)
186
386
  ```
187
387
 
188
388
  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.
389
+ with the execution Scope and never change the shared Runtime environment. Its
390
+ external requirements must be provided by the Runtime, and the Hono failure
391
+ handler/request-Layer types are checked at the adapter boundary.
190
392
 
191
393
  ### Hono request boundaries
192
394
 
@@ -201,7 +403,7 @@ import { Result } from 'better-result'
201
403
  import { HonoEffect } from 'better-effect/hono'
202
404
 
203
405
  const http = HonoEffect.make(runtime, {
204
- onFailure: (error, c) => c.json({ error: String(error) }, 400)
406
+ onFailure: (_error, c) => c.json({ error: 'Request failed' }, 400)
205
407
  })
206
408
  const app = new Hono()
207
409
 
@@ -216,9 +418,9 @@ app.get(
216
418
  )
217
419
  ```
218
420
 
219
- Hono validators can precede the generator or handler callback. Their validated
220
- `c.req.valid(...)` inputs are combined and inferred without a manual `Input`
221
- helper:
421
+ One or more Hono validators can precede the generator or handler callback, in
422
+ the order they should run. Their validated `c.req.valid(...)` inputs are
423
+ combined and inferred without a manual `Input` helper:
222
424
 
223
425
  ```ts
224
426
  import { sValidator } from '@hono/standard-validator'
@@ -258,12 +460,21 @@ own `Response` when validation fails. `http.handler` accepts the same ordered
258
460
  validator arguments followed by the program factory and options.
259
461
 
260
462
  Install `hono` only when this subpath is used. The main entrypoint does not
261
- load the framework.
262
-
263
- Service and Scope access share one `RuntimeContext`. Node/Bun uses
264
- `AsyncLocalStorage` by default; hosts without transparent async context can
265
- pass `contextStorage: new ExplicitRuntimeContextStorage()` from
266
- `better-effect/runtime/explicit`.
463
+ load the framework. The default Hono failure policy redacts every non-`Response`
464
+ failure value to `{ error: 'Internal Server Error' }` with status 500. An
465
+ explicitly returned `Response` failure is intentionally passed through
466
+ unchanged. A custom `onFailure` policy should serialize only safe, intentional
467
+ domain details.
468
+
469
+ Service and Scope access share one `RuntimeContext`. The published Runtime
470
+ entrypoint is officially supported on Node.js and Bun, where `AsyncLocalStorage`
471
+ is the default context storage. The `better-effect/runtime/explicit` subpath
472
+ provides `ExplicitRuntimeContextStorage` as a manually managed, sequential
473
+ strategy only when the package entrypoint and host can load it. One instance
474
+ supports one non-overlapping async flow and rejects concurrent overlap. Using
475
+ it does not make the package generally usable in browsers, Deno, Cloudflare
476
+ Workers, or other non-Node hosts; separate explicit instances are not a general
477
+ concurrent-isolation strategy.
267
478
 
268
479
  If a program asks that Runtime for a Service its environment does not provide, TypeScript rejects the call.
269
480
 
@@ -386,7 +597,9 @@ Some dependencies are values.
386
597
 
387
598
  Others own connections, sessions, files or other resources.
388
599
 
389
- `Layer.scoped`, `Layer.scopedGen`, `Effect.acquireRelease`, `Effect.add` and `Scope` make their lifetime explicit.
600
+ `Layer.scoped`, `Layer.scopedGen`, `Layer.scopedDisposable`, `Effect.acquireRelease`,
601
+ `Effect.acquireReleaseResult`, `Effect.acquireDisposable`, `Effect.add` and `Scope` make their
602
+ lifetime explicit.
390
603
 
391
604
  ```ts
392
605
  const DatabaseLive = Layer.scoped(
@@ -400,6 +613,35 @@ Runtime owns the application lifetime and safely releases scoped resources when
400
613
 
401
614
  Resources acquired during an individual execution belong to that execution instead.
402
615
 
616
+ When an existing API already returns a `Result`, keep its typed failure channel while
617
+ registering only successful acquisitions:
618
+
619
+ ```ts
620
+ const connection =
621
+ yield *
622
+ Effect.acquireReleaseResult(
623
+ () => pool.connect(),
624
+ (connection, outcome) => connection.close(outcome)
625
+ )
626
+ ```
627
+
628
+ An `Err` is returned unchanged and is never released. Thrown or rejected acquisition
629
+ defects use the normal `UnhandledException` channel; release failures remain Scope
630
+ cleanup failures rather than widening the acquisition error type.
631
+
632
+ For values that implement JavaScript disposal, use the disposable helpers instead of
633
+ repeating a release callback. Async disposal is preferred when both protocols exist:
634
+
635
+ ```ts
636
+ const file = yield * Effect.acquireDisposable(() => openFile(path))
637
+
638
+ const DatabaseLive = Layer.scopedDisposable(Database, () => Database.connect())
639
+ ```
640
+
641
+ `Effect.acquireDisposable` belongs to the current execution Scope. `Layer.scopedDisposable`
642
+ keeps the client alive across executions and disposes it with the Runtime root; the DI
643
+ backend never owns that release.
644
+
403
645
  ### Keep your runtime choices
404
646
 
405
647
  `better-effect` is not a replacement implementation of Effect.
@@ -550,7 +792,26 @@ still rejects it when its Layer does not provide every required Service.
550
792
 
551
793
  Observation helpers such as `Effect.tap`, `Effect.tapError`, and `Effect.tapBoth`
552
794
  run only the active branch and return the original Result, so logging or metrics
553
- do not change the pipeline's value or requirement channel.
795
+ do not change the pipeline's value or requirement channel. The async variants
796
+ `Effect.tapAsync`, `Effect.tapErrorAsync`, and `Effect.tapBothAsync` accept
797
+ `PromiseLike<void>` observers, always return a Promise, and preserve the source
798
+ requirements. They delegate branch selection and defect handling to
799
+ `better-result`; only the active observer runs, and a successful observation
800
+ returns the exact original Result. They do not create a Scope or resolve
801
+ Services inside the callback.
802
+
803
+ ```ts
804
+ const audited = pipe(
805
+ loadUser(userId),
806
+ Effect.tapAsync((user) => metrics.recordUserLoaded(user.id)),
807
+ Effect.tapErrorAsync((error) => metrics.recordUserFailure(error))
808
+ )
809
+ ```
810
+
811
+ `Effect.matchError` exhaustively maps a tagged `Err` union, while
812
+ `Effect.matchErrorPartial` maps selected tags and retains unhandled variants in
813
+ the resulting error union. Both delegate to `better-result`'s tagged-error
814
+ matchers and preserve the source success and requirement channels.
554
815
 
555
816
  Use `Effect.recover` or `Effect.recoverAsync` for an explicit fallback Result;
556
817
  the fallback is evaluated only when the input is an `Err`, and its Service
@@ -1,6 +1,5 @@
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-heuaRmXR.mjs";
2
+ import { v as LayerBackend, x as LayerRegistration, y as LayerBackendDisposeOptions } from "../index-rQhZk3Nt.mjs";
4
3
  //#region src/adapters/iti.d.ts
5
4
  /**
6
5
  * ITI-backed Layer backend.
@@ -12,13 +11,19 @@ declare class ItiLayerBackend implements LayerBackend {
12
11
  private container;
13
12
  private readonly keys;
14
13
  private readonly registered;
14
+ /**
15
+ * Track async gets so disposal cannot reset ITI while a provider is acquiring.
16
+ * ITI caches rejected acquisitions; replacing the container is the explicit
17
+ * retry boundary for that sticky failure behavior.
18
+ */
19
+ private readonly pending;
15
20
  private keyFor;
16
21
  /** Register a Layer provider under its deterministic Service-tag key. */
17
22
  register(registration: LayerRegistration): void;
18
23
  /** Resolve a registered Service through the ITI container. */
19
24
  resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>;
20
- /** Dispose all ITI-managed provider instances. */
21
- disposeAll(): Promise<void>;
25
+ /** Reset container-owned ITI state; Scope owns Layer provider releases. */
26
+ disposeAll(options?: LayerBackendDisposeOptions): Promise<void>;
22
27
  }
23
28
  //#endregion
24
29
  export { ItiLayerBackend };
@@ -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":";;;;;;;;;cAuBa,2BAA2B;UAC9B;mBAES;mBAEA;;;;;;mBAOA;UAET;;EAgBR,SAAS,cAAc;;EAuBvB,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;;EAiCnF,WAAW,UAAU,6BAA6B"}
@@ -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 Map();
16
22
  keyFor(token) {
17
23
  const tag = token.serviceTag;
18
24
  const existing = this.keys.get(tag);
@@ -42,12 +48,31 @@ 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.set(pending, registered);
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. */
49
- async disposeAll() {
50
- await this.container.disposeAll();
59
+ /** Reset container-owned ITI state; Scope owns Layer provider releases. */
60
+ async disposeAll(options) {
61
+ const container = this.container;
62
+ const acquisitions = [...this.pending.keys()];
63
+ try {
64
+ if (acquisitions.length > 0) {
65
+ const observePending = options?.onPendingAcquisitions;
66
+ if (observePending) await observePending(acquisitions);
67
+ await Promise.allSettled(acquisitions);
68
+ }
69
+ await container.disposeAll();
70
+ } finally {
71
+ this.container = createContainer();
72
+ this.registered.clear();
73
+ this.keys.clear();
74
+ this.pending.clear();
75
+ }
51
76
  }
52
77
  };
53
78
  //#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 LayerBackendDisposeOptions,\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 Map<Promise<unknown>, 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 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":";;;;;;;;;;;AAuBA,IAAa,kBAAb,MAAqD;CACnD,YAAyB,gBAAgB;CAEzC,uBAAwB,IAAI,IAAoB;CAEhD,6BAA8B,IAAI,IAA6B;;;;;;CAO/D,0BAA2B,IAAI,IAAuC;CAEtE,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,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,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