better-effect 0.1.0 → 0.3.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 CHANGED
@@ -6,7 +6,8 @@ Lightweight Effect-inspired primitives built around [`better-result`](https://ww
6
6
 
7
7
  - **Service** — contextual dependency access with `yield*`
8
8
  - **Layer** — declarative composition of live/test environments
9
- - **Resource** — safe acquire/use/release lifecycle
9
+ - **Scope** — contextual lifetime and finalizer management
10
+ - **Resource** — standalone Result-oriented acquire/use/release helper
10
11
  - **DI adapters** — dependency resolution is delegated to an external container instead of being reimplemented by the library
11
12
 
12
13
  The goal is not to recreate Effect. The goal is to provide a small, composable layer on top of `better-result`.
@@ -29,7 +30,7 @@ A service is a class that also acts as its own dependency token.
29
30
 
30
31
  ```ts
31
32
  import { Result } from 'better-result'
32
- import { Service } from 'better-effect'
33
+ import { Effect, Service } from 'better-effect'
33
34
 
34
35
  export class Database extends Service<Database>() {
35
36
  findUser(email: string) {
@@ -42,7 +43,7 @@ export class Database extends Service<Database>() {
42
43
 
43
44
  export class UserRepository extends Service<UserRepository>() {
44
45
  findByEmail(email: string) {
45
- return Result.gen(async function* () {
46
+ return Effect.gen(async function* () {
46
47
  const database = yield* Database
47
48
 
48
49
  const user = await database.findUser(email)
@@ -53,6 +54,65 @@ export class UserRepository extends Service<UserRepository>() {
53
54
  }
54
55
  ```
55
56
 
57
+ Use `Effect.gen` when a generator accesses Services. It delegates execution to
58
+ `better-result` while carrying the required Service tokens in a type-only
59
+ metadata channel:
60
+
61
+ ```ts
62
+ export class AuthService extends Service<AuthService>() {
63
+ login(email: string) {
64
+ return Effect.gen(async function* () {
65
+ const users = yield* UserRepository
66
+ const user = yield* Result.await(users.findByEmail(email))
67
+
68
+ return Result.ok(user)
69
+ })
70
+ }
71
+ }
72
+ ```
73
+
74
+ `Layer.make` derives the requirements from those method return types. A
75
+ `Runtime` rejects a merged Layer at compile time when one of its required
76
+ Services is missing; `Layer.override` remains the explicit replacement API.
77
+
78
+ `Runtime.make()` and `buildLayer()` preserve the exact Service-token union
79
+ provided by the Layer. Every `run()` boundary checks the final
80
+ `EffectRequirements` against that union, so a program that yields an unavailable
81
+ Service is rejected at compile time and the diagnostic names
82
+ `__betterEffectMissingRuntimeServices`. Programs with no Service requirements
83
+ remain valid in any environment:
84
+
85
+ ```ts
86
+ const runtime = await Runtime.make(AppLive, backend)
87
+ // inferred as Runtime<typeof Database | typeof UserRepository>
88
+
89
+ type AppRuntime = RuntimeFor<typeof AppLive>
90
+ // Runtime<typeof Database | typeof UserRepository>
91
+
92
+ const built = await buildLayer(AppLive, backend)
93
+ // inferred as BuiltLayer<typeof Database | typeof UserRepository>
94
+
95
+ const result = await runtime.run(() =>
96
+ Effect.gen(async function* () {
97
+ const database = yield* Database
98
+
99
+ return Result.ok(database.query())
100
+ })
101
+ )
102
+ ```
103
+
104
+ Use `RuntimeFor<typeof AppLive>` when a Runtime handle inferred from a Layer must
105
+ be named in a function signature. This avoids manually repeating
106
+ `Runtime<LayerProvided<typeof AppLive>>` while preserving the same checked
107
+ Service environment.
108
+
109
+ Use an unparameterized `Runtime` or `BuiltLayer` annotation only when an
110
+ intentionally erased, unchecked environment is needed:
111
+
112
+ ```ts
113
+ const erased: Runtime = runtime
114
+ ```
115
+
56
116
  There are no string tokens:
57
117
 
58
118
  ```ts
@@ -118,10 +178,56 @@ The core Layer API intentionally stays small:
118
178
  Layer.make(Service, acquire)
119
179
  Layer.succeed(Service, instance)
120
180
  Layer.scoped(Service, acquire, release)
181
+ Layer.gen(Service, factory)
182
+ Layer.scopedGen(Service, factory, release)
121
183
  Layer.merge(...layers)
122
184
  Layer.override(base, ...overrides)
123
185
  ```
124
186
 
187
+ Use `Layer.gen` when constructing a provider requires contextual Services. The
188
+ requirements yielded by the factory remain part of the Layer's compile-time
189
+ contract:
190
+
191
+ ```ts
192
+ const UserRepositoryLive = Layer.gen(UserRepository, async function* () {
193
+ const database = yield* Database
194
+
195
+ return new UserRepository(database)
196
+ })
197
+ ```
198
+
199
+ Use `Layer.scopedGen` when acquisition both needs contextual Services and owns a
200
+ resource that must be released with the Runtime root Scope:
201
+
202
+ ```ts
203
+ class DatabaseSession extends Service<DatabaseSession>() {
204
+ constructor(readonly database: Database) {
205
+ super()
206
+ }
207
+
208
+ close(outcome: ScopeOutcome) {
209
+ return this.database.closeSession(this, outcome)
210
+ }
211
+ }
212
+
213
+ const DatabaseSessionLive = Layer.scopedGen(
214
+ DatabaseSession,
215
+ async function* () {
216
+ const database = yield* Database
217
+
218
+ return new DatabaseSession(database)
219
+ },
220
+ (session, outcome) => session.close(outcome)
221
+ )
222
+ ```
223
+
224
+ `Layer.gen` expresses contextual acquisition without registering cleanup;
225
+ `Layer.scoped` registers cleanup for a dependency-free factory; `Layer.scopedGen`
226
+ combines both. The factory remains lazy, the acquired instance is shared according
227
+ to backend caching, and its release runs once when the Runtime root closes. The
228
+ release callback receives the root `ScopeOutcome`, including the final outcome of
229
+ one-shot `Runtime.run()`.
230
+
125
231
  ### Test environments
126
232
 
127
233
  Layers make implementation replacement explicit:
@@ -139,7 +245,7 @@ const AppTest = Layer.override(AppLive, DatabaseTest)
139
245
  ```ts
140
246
  import { buildLayer } from 'better-effect'
141
247
 
142
- import { ItiLayerBackend } from 'better-effect/iti'
248
+ import { ItiLayerBackend } from 'better-effect/adapters/iti'
143
249
 
144
250
  const runtime = await buildLayer(AppLive, new ItiLayerBackend())
145
251
 
@@ -154,9 +260,184 @@ This keeps application code independent from the DI container.
154
260
 
155
261
  A different backend can implement the same resolver/backend contracts without changing Services or Layers.
156
262
 
157
- ## Resource
263
+ ## Scope
264
+
265
+ `Scope` manages a dynamic lifetime containing multiple resources. Resources acquired
266
+ inside `runtime.run()` belong to that execution and are released automatically when it
267
+ finishes.
268
+
269
+ The contextual `Scope` is a non-owning capability: it can acquire resources, register
270
+ finalizers, and fork children, but it cannot close the lifetime that owns it. Owners
271
+ receive a `CloseableScope` from `Scope.make()` or `scope.fork()` and remain responsible
272
+ for closing it:
273
+
274
+ ```ts
275
+ const scope = yield * Scope
276
+ const child = scope.fork()
277
+
278
+ try {
279
+ await Scope.provide(child, () => processBatch())
280
+ } finally {
281
+ await child.close()
282
+ }
283
+ ```
284
+
285
+ For the common acquire-and-release case, `Effect.acquireRelease()` keeps the same
286
+ lifetime semantics without exposing `Scope` in the program. It is an async yieldable
287
+ for `Effect.gen`:
288
+
289
+ ```ts
290
+ const result = await runtime.run(() =>
291
+ Effect.gen(async function* () {
292
+ const connection = yield* Effect.acquireRelease(
293
+ () => database.reserve(),
294
+ (connection, outcome) => {
295
+ void outcome
296
+ return connection.release()
297
+ }
298
+ )
299
+
300
+ return Result.ok(await useConnection(connection))
301
+ })
302
+ )
303
+ ```
304
+
305
+ Acquisition failures are returned through the Effect `Result` error channel. The
306
+ release callback is registered in the current Scope and remains Scope cleanup, so it
307
+ runs when the owning execution closes.
308
+
309
+ When a resource is already acquired and implements `Symbol.dispose` or
310
+ `Symbol.asyncDispose`, `Effect.add()` registers it in the current Scope and yields the
311
+ same object back to the program:
312
+
313
+ ```ts
314
+ const result = await runtime.run(() =>
315
+ Effect.gen(async function* () {
316
+ const file = await createTemporaryFile()
317
+ const ownedFile = yield* Effect.add(file)
318
+
319
+ return Result.ok(await readFile(ownedFile))
320
+ })
321
+ )
322
+ ```
323
+
324
+ `Effect.add()` does not acquire the resource or create a Scope. It must run inside a
325
+ managed execution such as `runtime.run()` or `Scope.run()`; without a current Scope,
326
+ it preserves the existing missing-Scope failure. The resource is disposed when that
327
+ Scope closes, with `Symbol.asyncDispose` preferred when both protocols exist.
158
328
 
159
- `Resource.acquireUseRelease()` handles local resource lifecycle while preserving typed `Result` errors.
329
+ Use `Effect.acquireRelease()` when acquisition belongs in the Effect and cleanup needs
330
+ an explicit callback or the final `ScopeOutcome`; use `Effect.add()` for an
331
+ already-acquired JavaScript disposable.
332
+
333
+ ```ts
334
+ const result = await runtime.run(() =>
335
+ Effect.gen(async function* () {
336
+ const scope = yield* Scope
337
+
338
+ const connection = await scope.acquire(
339
+ () => database.reserve(),
340
+ (connection, outcome) => {
341
+ void outcome
342
+ return connection.release()
343
+ }
344
+ )
345
+
346
+ return Result.ok(await useConnection(connection))
347
+ })
348
+ )
349
+ ```
350
+
351
+ Disposable objects can be registered directly:
352
+
353
+ ```ts
354
+ const file = await scope.add(await createTemporaryFile())
355
+ ```
356
+
357
+ `DisposableResource` requires a callable `Symbol.dispose` or `Symbol.asyncDispose`,
358
+ so plain or weakly typed objects must be narrowed before registration. Dynamic values
359
+ that cross an unsafe boundary are still checked at runtime and rejected with
360
+ `ResourceNotDisposableError` when neither protocol is present.
361
+
362
+ Finalizers run sequentially in reverse registration order. `Layer.scoped` and
363
+ `Layer.scopedGen` resources belong to the Runtime root scope and remain alive between
364
+ executions; they are released when the Runtime is disposed. `Resource.acquireUseRelease()`
365
+ remains available as a standalone compatibility helper implemented on top of Scope.
366
+
367
+ Scopes can also form explicit child lifetimes. `fork()` registers a child with its
368
+ parent, while `Scope.provide()` uses an existing scope without taking ownership of its
369
+ closure:
370
+
371
+ ```ts
372
+ const parent = Scope.make()
373
+ const batch = parent.fork()
374
+
375
+ try {
376
+ await Scope.provide(batch, () => processBatch())
377
+ } finally {
378
+ await batch.close()
379
+ await parent.close()
380
+ }
381
+ ```
382
+
383
+ `runtime.run()` creates one child scope per execution. Concurrent executions receive
384
+ isolated scopes, and `runtime.dispose()` stops accepting new executions, waits for
385
+ active executions to finish, then closes the Runtime root scope and its Layer resources.
386
+
387
+ The final result is classified only at the execution boundary. Plain values and
388
+ `Result.ok` close the execution with `{ status: 'success' }`; `Result.err` and thrown
389
+ exceptions close it with `{ status: 'failure', cause }`. Intermediate Results do not
390
+ change the outcome. Release callbacks receive this outcome, which makes commit/rollback
391
+ cleanup possible without adding a transaction abstraction.
392
+
393
+ For example, a transaction can choose its final action from the outcome:
394
+
395
+ ```ts
396
+ const transaction =
397
+ yield *
398
+ Effect.acquireRelease(
399
+ () => database.begin(),
400
+ (transaction, outcome) =>
401
+ outcome.status === 'success' ? transaction.commit() : transaction.rollback()
402
+ )
403
+ ```
404
+
405
+ The ownership tree is:
406
+
407
+ ```text
408
+ Runtime
409
+ └── root Scope
410
+ ├── Layer resources
411
+ └── execution Scope
412
+ └── operation resources
413
+ ```
414
+
415
+ Graceful disposal follows this order:
416
+
417
+ ```text
418
+ stop accepting executions
419
+
420
+ wait for active executions
421
+
422
+ close the root Scope
423
+
424
+ dispose the backend
425
+ ```
426
+
427
+ An execution Scope is always closed before its execution Promise settles. The precedence
428
+ is `program failure > cleanup failure > program success`: a failed program preserves its
429
+ exact `Result.err` or exception, while a successful program rejects with a cleanup error.
430
+ Configure `onCleanupFailure` on `buildLayer` or `Runtime.make` to receive one best-effort
431
+ diagnostic for suppressed execution or shutdown cleanup failures. Calls to `runtime.run()`
432
+ after disposal begins fail with `BuiltLayerDisposedError` without invoking the program.
433
+ Shutdown has no cancellation or timeout mechanism and is intended to be initiated outside
434
+ the execution being awaited.
435
+
436
+ ## Standalone resource helper
437
+
438
+ `Resource.acquireUseRelease()` remains useful for local workflows that want a
439
+ Result-oriented acquire/use/release API without constructing a Runtime. It is
440
+ implemented on top of Scope and remains fully supported; it is not deprecated.
160
441
 
161
442
  ```ts
162
443
  import { Resource } from 'better-effect'
@@ -172,18 +453,15 @@ const result = await Resource.acquireUseRelease({
172
453
  })
173
454
  ```
174
455
 
175
- When `release` is omitted, `Resource` attempts to use the JavaScript explicit resource management protocol:
456
+ When `release` is omitted, Resource prefers the JavaScript explicit resource
457
+ management protocol:
176
458
 
177
459
  ```ts
178
460
  Symbol.asyncDispose
179
461
  Symbol.dispose
180
462
  ```
181
463
 
182
- ### Error precedence
183
-
184
- If both `use` and `release` fail, the error produced by `use` is preserved.
185
-
186
- The precedence is:
464
+ If both `use` and `release` fail, the `use` error is preserved. The precedence is:
187
465
 
188
466
  ```text
189
467
  1. use failure
@@ -191,16 +469,18 @@ The precedence is:
191
469
  3. successful use value
192
470
  ```
193
471
 
194
- Acquisition exceptions, rejected promises, and unexpected failures are normalized through `better-result`.
472
+ Acquisition exceptions, rejected promises, and unexpected failures are normalized
473
+ through `better-result`; release failures use `ResourceReleaseFailure`.
195
474
 
196
- ## Service vs Layer vs Resource
475
+ ## Service vs Layer vs Scope vs Resource
197
476
 
198
477
  | Primitive | Responsibility |
199
478
  | --------------- | ----------------------------------------------------- |
200
479
  | `Service` | Request a contextual dependency |
201
480
  | `Layer` | Describe the implementations that form an environment |
202
- | `Resource` | Manage a resource local to one operation |
203
- | DI backend | Resolve, cache and dispose service instances |
481
+ | `Scope` | Manage dynamic lifetimes and finalizers |
482
+ | `Resource` | Standalone Result-oriented acquire/use/release helper |
483
+ | DI backend | Resolve and cache service instances |
204
484
  | `better-result` | Typed failures and generator control flow |
205
485
 
206
486
  ## Complete example
@@ -220,8 +500,9 @@ It demonstrates:
220
500
  - TODO CRUD
221
501
  - `Service` dependency access
222
502
  - Layer composition
503
+ - Runtime root and execution scopes
223
504
  - scoped database lifecycle
224
- - `Resource.acquireUseRelease()`
505
+ - Standalone `Resource` compatibility API
225
506
  - ITI as the DI backend
226
507
 
227
508
  Run it from the repository root:
@@ -280,12 +561,12 @@ The core intentionally does not implement:
280
561
  - a dependency graph runtime
281
562
  - a custom DI container
282
563
  - a custom Context
283
- - a custom Scope runtime
284
564
  - `Effect<A, E, R>`
285
565
 
286
566
  ### Delegate instead of rebuilding
287
567
 
288
- Dependency resolution, caching and container lifecycle belong to DI backends.
568
+ Dependency resolution and caching belong to DI backends. Service release lifecycle is
569
+ owned by `Scope`.
289
570
 
290
571
  `better-effect` supplies the protocol and composition primitives.
291
572
 
@@ -324,6 +605,7 @@ The initial scope is:
324
605
  ```text
325
606
  Service
326
607
  Layer
608
+ Scope
327
609
  Resource
328
610
  DI adapters
329
611
  better-result integration
@@ -1,2 +1,15 @@
1
- import { t as ItiLayerBackend } from "../iti-xXAb9-Qs.mjs";
2
- export { ItiLayerBackend };
1
+ import { U as AnyServiceToken, b as LayerProvider, r as LayerBackend } from "../index-Bg6ofRE1.mjs";
2
+ //#region src/adapters/iti.d.ts
3
+ declare class ItiLayerBackend implements LayerBackend {
4
+ private container;
5
+ private readonly keys;
6
+ private readonly registered;
7
+ private nextId;
8
+ private keyFor;
9
+ register(provider: LayerProvider): void;
10
+ resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>;
11
+ disposeAll(): Promise<void>;
12
+ }
13
+ //#endregion
14
+ export { ItiLayerBackend };
15
+ //# sourceMappingURL=iti.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iti.d.mts","names":[],"sources":["../../src/adapters/iti.ts"],"mappings":";;cAMa,2BAA2B;UAC9B;mBAES;mBAEA;UAET;UAEA;EAgBR,SAAS,UAAU;EAgBnB,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;EAUnF,cAAc"}
@@ -1,8 +1,10 @@
1
+ import { n as DuplicateServiceError, o as ServiceNotFoundError } from "../errors-CnvKqBpb.mjs";
1
2
  import { createContainer } from "iti";
2
3
  //#region src/adapters/iti.ts
3
4
  var ItiLayerBackend = class {
4
5
  container = createContainer();
5
6
  keys = /* @__PURE__ */ new WeakMap();
7
+ registered = /* @__PURE__ */ new WeakSet();
6
8
  nextId = 0;
7
9
  keyFor(token) {
8
10
  const existing = this.keys.get(token);
@@ -12,11 +14,14 @@ var ItiLayerBackend = class {
12
14
  return key;
13
15
  }
14
16
  register(provider) {
15
- const key = this.keyFor(provider.service);
17
+ const token = provider.service;
18
+ if (this.registered.has(token)) throw new DuplicateServiceError(token);
19
+ const key = this.keyFor(token);
16
20
  this.container = this.container.add({ [key]: provider.acquire });
17
- if (provider.release) this.container = this.container.addDisposer({ [key]: provider.release });
21
+ this.registered.add(token);
18
22
  }
19
23
  resolve(token) {
24
+ if (!this.registered.has(token)) throw new ServiceNotFoundError(token);
20
25
  const key = this.keyFor(token);
21
26
  return this.container.get(key);
22
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"iti.mjs","names":[],"sources":["../../src/adapters/iti.ts"],"sourcesContent":["import { createContainer } from 'iti'\n\nimport type { LayerBackend } from '../layer'\n\nimport type { AnyServiceToken, ServiceToken } from '../service'\nimport type { LayerProvider } from '../layer/types'\n\nexport class ItiLayerBackend implements LayerBackend {\n private container: any = createContainer()\n\n private readonly keys = new WeakMap<ServiceToken<any>, string>()\n\n private nextId = 0\n\n private keyFor(token: AnyServiceToken): string {\n const existing = this.keys.get(token)\n\n if (existing) {\n return existing\n }\n\n const name = (token as Function).name || 'Service'\n\n const key = `better-effect:${name}:${this.nextId++}`\n\n this.keys.set(token, key)\n\n return key\n }\n\n register(provider: LayerProvider): void {\n const key = this.keyFor(provider.service)\n\n this.container = this.container.add({\n [key]: provider.acquire\n })\n\n if (provider.release) {\n this.container = this.container.addDisposer({\n [key]: provider.release\n })\n }\n }\n\n resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>> {\n const key = this.keyFor(token)\n\n return this.container.get(key) as InstanceType<T> | PromiseLike<InstanceType<T>>\n }\n\n async disposeAll(): Promise<void> {\n await this.container.disposeAll()\n }\n}\n"],"mappings":";;AAOA,IAAa,kBAAb,MAAqD;CACnD,YAAyB,gBAAgB;CAEzC,uBAAwB,IAAI,QAAmC;CAE/D,SAAiB;CAEjB,OAAe,OAAgC;EAC7C,MAAM,WAAW,KAAK,KAAK,IAAI,KAAK;EAEpC,IAAI,UACF,OAAO;EAKT,MAAM,MAAM,iBAFE,MAAmB,QAAQ,UAEP,GAAG,KAAK;EAE1C,KAAK,KAAK,IAAI,OAAO,GAAG;EAExB,OAAO;CACT;CAEA,SAAS,UAA+B;EACtC,MAAM,MAAM,KAAK,OAAO,SAAS,OAAO;EAExC,KAAK,YAAY,KAAK,UAAU,IAAI,GACjC,MAAM,SAAS,QAClB,CAAC;EAED,IAAI,SAAS,SACX,KAAK,YAAY,KAAK,UAAU,YAAY,GACzC,MAAM,SAAS,QAClB,CAAC;CAEL;CAEA,QAAmC,OAA0D;EAC3F,MAAM,MAAM,KAAK,OAAO,KAAK;EAE7B,OAAO,KAAK,UAAU,IAAI,GAAG;CAC/B;CAEA,MAAM,aAA4B;EAChC,MAAM,KAAK,UAAU,WAAW;CAClC;AACF"}
1
+ {"version":3,"file":"iti.mjs","names":[],"sources":["../../src/adapters/iti.ts"],"sourcesContent":["import { createContainer } from 'iti'\n\nimport { DuplicateServiceError, type LayerBackend, type LayerProvider } from '../layer'\n\nimport { ServiceNotFoundError, type AnyServiceToken } from '../service'\n\nexport class ItiLayerBackend implements LayerBackend {\n private container: any = createContainer()\n\n private readonly keys = new WeakMap<AnyServiceToken, string>()\n\n private readonly registered = new WeakSet<AnyServiceToken>()\n\n private nextId = 0\n\n private keyFor(token: AnyServiceToken): string {\n const existing = this.keys.get(token)\n\n if (existing) {\n return existing\n }\n\n const name = token.name || 'Service'\n\n const key = `better-effect:${name}:${this.nextId++}`\n\n this.keys.set(token, key)\n\n return key\n }\n\n register(provider: LayerProvider): void {\n const token = provider.service\n\n if (this.registered.has(token)) {\n throw new DuplicateServiceError(token)\n }\n\n const key = this.keyFor(token)\n\n this.container = this.container.add({\n [key]: provider.acquire\n })\n\n this.registered.add(token)\n }\n\n resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>> {\n if (!this.registered.has(token)) {\n throw new ServiceNotFoundError(token)\n }\n\n const key = this.keyFor(token)\n\n return this.container.get(key) as InstanceType<T> | PromiseLike<InstanceType<T>>\n }\n\n async disposeAll(): Promise<void> {\n await this.container.disposeAll()\n }\n}\n"],"mappings":";;;AAMA,IAAa,kBAAb,MAAqD;CACnD,YAAyB,gBAAgB;CAEzC,uBAAwB,IAAI,QAAiC;CAE7D,6BAA8B,IAAI,QAAyB;CAE3D,SAAiB;CAEjB,OAAe,OAAgC;EAC7C,MAAM,WAAW,KAAK,KAAK,IAAI,KAAK;EAEpC,IAAI,UACF,OAAO;EAKT,MAAM,MAAM,iBAFC,MAAM,QAAQ,UAEO,GAAG,KAAK;EAE1C,KAAK,KAAK,IAAI,OAAO,GAAG;EAExB,OAAO;CACT;CAEA,SAAS,UAA+B;EACtC,MAAM,QAAQ,SAAS;EAEvB,IAAI,KAAK,WAAW,IAAI,KAAK,GAC3B,MAAM,IAAI,sBAAsB,KAAK;EAGvC,MAAM,MAAM,KAAK,OAAO,KAAK;EAE7B,KAAK,YAAY,KAAK,UAAU,IAAI,GACjC,MAAM,SAAS,QAClB,CAAC;EAED,KAAK,WAAW,IAAI,KAAK;CAC3B;CAEA,QAAmC,OAA0D;EAC3F,IAAI,CAAC,KAAK,WAAW,IAAI,KAAK,GAC5B,MAAM,IAAI,qBAAqB,KAAK;EAGtC,MAAM,MAAM,KAAK,OAAO,KAAK;EAE7B,OAAO,KAAK,UAAU,IAAI,GAAG;CAC/B;CAEA,MAAM,aAA4B;EAChC,MAAM,KAAK,UAAU,WAAW;CAClC;AACF"}
@@ -0,0 +1,63 @@
1
+ //#region src/service/errors.ts
2
+ var ServiceRuntimeNotConfiguredError = class extends Error {
3
+ constructor() {
4
+ super("No ServiceResolver is available in the current runtime context");
5
+ this.name = "ServiceRuntimeNotConfiguredError";
6
+ }
7
+ };
8
+ var ServiceNotFoundError = class extends Error {
9
+ service;
10
+ constructor(service) {
11
+ super(`Service "${service.name}" was not provided`);
12
+ this.service = service;
13
+ this.name = "ServiceNotFoundError";
14
+ }
15
+ };
16
+ //#endregion
17
+ //#region src/layer/errors.ts
18
+ var DuplicateServiceError = class extends Error {
19
+ service;
20
+ constructor(service) {
21
+ super(`Duplicate service "${service.name}"`);
22
+ this.service = service;
23
+ this.name = "DuplicateServiceError";
24
+ }
25
+ };
26
+ var LayerRegistrationError = class extends Error {
27
+ service;
28
+ registrationCause;
29
+ cleanupCause;
30
+ constructor(service, registrationCause, cleanupCause) {
31
+ super(service ? `Failed to register service "${service.name}"` : "Failed to build Layer", { cause: registrationCause });
32
+ this.service = service;
33
+ this.registrationCause = registrationCause;
34
+ this.cleanupCause = cleanupCause;
35
+ this.name = "LayerRegistrationError";
36
+ }
37
+ };
38
+ var LayerDisposeError = class extends Error {
39
+ causes;
40
+ constructor(causes) {
41
+ super(`Failed to dispose Layer (${causes.length} error${causes.length === 1 ? "" : "s"})`);
42
+ this.causes = causes;
43
+ this.name = "LayerDisposeError";
44
+ }
45
+ };
46
+ var LayerGeneratorYieldError = class extends Error {
47
+ service;
48
+ constructor(service) {
49
+ super(`Layer.gen("${service.name}") yielded an unsupported value`);
50
+ this.service = service;
51
+ this.name = "LayerGeneratorYieldError";
52
+ }
53
+ };
54
+ var BuiltLayerDisposedError = class extends Error {
55
+ constructor() {
56
+ super("Cannot run a program using a disposed Layer");
57
+ this.name = "BuiltLayerDisposedError";
58
+ }
59
+ };
60
+ //#endregion
61
+ export { LayerRegistrationError as a, LayerGeneratorYieldError as i, DuplicateServiceError as n, ServiceNotFoundError as o, LayerDisposeError as r, ServiceRuntimeNotConfiguredError as s, BuiltLayerDisposedError as t };
62
+
63
+ //# sourceMappingURL=errors-CnvKqBpb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-CnvKqBpb.mjs","names":[],"sources":["../src/service/errors.ts","../src/layer/errors.ts"],"sourcesContent":["import type { AnyServiceToken } from './types'\n\nexport class ServiceRuntimeNotConfiguredError extends Error {\n constructor() {\n super('No ServiceResolver is available in the current runtime context')\n\n this.name = 'ServiceRuntimeNotConfiguredError'\n }\n}\n\nexport class ServiceNotFoundError extends Error {\n constructor(readonly service: AnyServiceToken) {\n super(`Service \"${service.name}\" was not provided`)\n\n this.name = 'ServiceNotFoundError'\n }\n}\n","import type { ServiceClass } from '../service'\n\nexport class DuplicateServiceError extends Error {\n constructor(readonly service: ServiceClass<any>) {\n super(`Duplicate service \"${service.name}\"`)\n\n this.name = 'DuplicateServiceError'\n }\n}\n\nexport class LayerRegistrationError extends Error {\n constructor(\n readonly service: ServiceClass<any> | undefined,\n readonly registrationCause: unknown,\n readonly cleanupCause?: unknown\n ) {\n super(service ? `Failed to register service \"${service.name}\"` : 'Failed to build Layer', {\n cause: registrationCause\n })\n\n this.name = 'LayerRegistrationError'\n }\n}\n\nexport class LayerDisposeError extends Error {\n constructor(readonly causes: readonly unknown[]) {\n super(`Failed to dispose Layer (${causes.length} error${causes.length === 1 ? '' : 's'})`)\n\n this.name = 'LayerDisposeError'\n }\n}\n\nexport class LayerGeneratorYieldError extends Error {\n constructor(readonly service: ServiceClass<any>) {\n super(`Layer.gen(\"${service.name}\") yielded an unsupported value`)\n\n this.name = 'LayerGeneratorYieldError'\n }\n}\n\nexport class BuiltLayerDisposedError extends Error {\n constructor() {\n super('Cannot run a program using a disposed Layer')\n\n this.name = 'BuiltLayerDisposedError'\n }\n}\n"],"mappings":";AAEA,IAAa,mCAAb,cAAsD,MAAM;CAC1D,cAAc;EACZ,MAAM,gEAAgE;EAEtE,KAAK,OAAO;CACd;AACF;AAEA,IAAa,uBAAb,cAA0C,MAAM;CACzB;CAArB,YAAY,SAAmC;EAC7C,MAAM,YAAY,QAAQ,KAAK,mBAAmB;EAD/B,KAAA,UAAA;EAGnB,KAAK,OAAO;CACd;AACF;;;ACdA,IAAa,wBAAb,cAA2C,MAAM;CAC1B;CAArB,YAAY,SAAqC;EAC/C,MAAM,sBAAsB,QAAQ,KAAK,EAAE;EADxB,KAAA,UAAA;EAGnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,yBAAb,cAA4C,MAAM;CAErC;CACA;CACA;CAHX,YACE,SACA,mBACA,cACA;EACA,MAAM,UAAU,+BAA+B,QAAQ,KAAK,KAAK,yBAAyB,EACxF,OAAO,kBACT,CAAC;EANQ,KAAA,UAAA;EACA,KAAA,oBAAA;EACA,KAAA,eAAA;EAMT,KAAK,OAAO;CACd;AACF;AAEA,IAAa,oBAAb,cAAuC,MAAM;CACtB;CAArB,YAAY,QAAqC;EAC/C,MAAM,4BAA4B,OAAO,OAAO,QAAQ,OAAO,WAAW,IAAI,KAAK,IAAI,EAAE;EADtE,KAAA,SAAA;EAGnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,2BAAb,cAA8C,MAAM;CAC7B;CAArB,YAAY,SAAqC;EAC/C,MAAM,cAAc,QAAQ,KAAK,gCAAgC;EAD9C,KAAA,UAAA;EAGnB,KAAK,OAAO;CACd;AACF;AAEA,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;EACZ,MAAM,6CAA6C;EAEnD,KAAK,OAAO;CACd;AACF"}