better-effect 0.2.0 → 0.4.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 +156 -220
- package/dist/adapters/iti.d.mts +1 -1
- package/dist/adapters/iti.d.mts.map +1 -1
- package/dist/adapters/iti.mjs +1 -2
- package/dist/adapters/iti.mjs.map +1 -1
- package/dist/{errors-CnvKqBpb.mjs → errors-DlHCwICc.mjs} +5 -1
- package/dist/{errors-CnvKqBpb.mjs.map → errors-DlHCwICc.mjs.map} +1 -1
- package/dist/index-BYQKfyeJ.d.mts +235 -0
- package/dist/index-BYQKfyeJ.d.mts.map +1 -0
- package/dist/index.d.mts +106 -63
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +473 -32
- package/dist/index.mjs.map +1 -1
- package/dist/testing.d.mts +1 -2
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.mjs +2 -18
- package/dist/testing.mjs.map +1 -1
- package/package.json +6 -9
- package/dist/backend-CNAls62W.d.mts +0 -39
- package/dist/backend-CNAls62W.d.mts.map +0 -1
package/README.md
CHANGED
|
@@ -1,336 +1,272 @@
|
|
|
1
1
|
# better-effect
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Effect-like dependency safety for better-result.**
|
|
4
4
|
|
|
5
|
-
`better-
|
|
5
|
+
Type your errors with `better-result`. Typecheck the rest of your application wiring with `better-effect`.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Layer** — declarative composition of live/test environments
|
|
9
|
-
- **Resource** — safe acquire/use/release lifecycle
|
|
10
|
-
- **DI adapters** — dependency resolution is delegated to an external container instead of being reimplemented by the library
|
|
11
|
-
|
|
12
|
-
The goal is not to recreate Effect. The goal is to provide a small, composable layer on top of `better-result`.
|
|
13
|
-
|
|
14
|
-
## Installation
|
|
7
|
+
Use Services directly inside `Effect.gen`, compose implementations into application environments, and let TypeScript catch missing dependencies before your application starts — while keeping Promises, `better-result`, and your DI backend.
|
|
15
8
|
|
|
16
9
|
```bash
|
|
17
10
|
bun add better-effect better-result
|
|
18
11
|
```
|
|
19
12
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
bun add iti
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
## Service
|
|
27
|
-
|
|
28
|
-
A service is a class that also acts as its own dependency token.
|
|
13
|
+
## TypeScript knows what your application needs
|
|
29
14
|
|
|
30
15
|
```ts
|
|
31
16
|
import { Result } from 'better-result'
|
|
32
|
-
import { Service } from 'better-effect'
|
|
17
|
+
import { Effect, Layer, Runtime, Service } from 'better-effect'
|
|
33
18
|
|
|
34
|
-
|
|
35
|
-
findUser(
|
|
36
|
-
|
|
37
|
-
id: '1',
|
|
38
|
-
email
|
|
39
|
-
})
|
|
19
|
+
class Database extends Service<Database>() {
|
|
20
|
+
findUser(id: string) {
|
|
21
|
+
// ...
|
|
40
22
|
}
|
|
41
23
|
}
|
|
42
24
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return
|
|
25
|
+
class UserRepository extends Service<UserRepository>() {
|
|
26
|
+
findUser(id: string) {
|
|
27
|
+
return Effect.gen(async function* () {
|
|
46
28
|
const database = yield* Database
|
|
47
29
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return Result.ok(user)
|
|
30
|
+
return Result.ok(await database.findUser(id))
|
|
51
31
|
})
|
|
52
32
|
}
|
|
53
33
|
}
|
|
54
|
-
```
|
|
55
34
|
|
|
56
|
-
|
|
35
|
+
const UserRepositoryLive = Layer.make(UserRepository, () => new UserRepository())
|
|
57
36
|
|
|
58
|
-
|
|
59
|
-
|
|
37
|
+
await Runtime.make(UserRepositoryLive, backend)
|
|
38
|
+
// ^^^^^^^^^^^^^^^^^^
|
|
39
|
+
// Type error: Database is required but not provided
|
|
60
40
|
```
|
|
61
41
|
|
|
62
|
-
|
|
42
|
+
`UserRepository` used `Database`, so `Database` became part of its environment requirements.
|
|
63
43
|
|
|
64
|
-
|
|
44
|
+
No dependency list was written manually.
|
|
65
45
|
|
|
66
|
-
|
|
46
|
+
Provide it and the environment becomes complete:
|
|
67
47
|
|
|
68
48
|
```ts
|
|
69
|
-
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
and prevents string-key collisions or typos.
|
|
49
|
+
const DatabaseLive = Layer.make(Database, () => new Database())
|
|
73
50
|
|
|
74
|
-
|
|
51
|
+
const AppLive = Layer.merge(DatabaseLive, UserRepositoryLive)
|
|
75
52
|
|
|
76
|
-
|
|
77
|
-
yield* AuthService
|
|
78
|
-
│
|
|
79
|
-
▼
|
|
80
|
-
ServiceRuntime
|
|
81
|
-
│
|
|
82
|
-
▼
|
|
83
|
-
ServiceResolver
|
|
84
|
-
│
|
|
85
|
-
▼
|
|
86
|
-
AuthService instance
|
|
53
|
+
const runtime = await Runtime.make(AppLive, backend)
|
|
87
54
|
```
|
|
88
55
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
A Layer describes which implementations form an application environment.
|
|
56
|
+
And the contract does not disappear after startup.
|
|
92
57
|
|
|
93
|
-
|
|
58
|
+
A Runtime also knows which Services exist in its environment:
|
|
94
59
|
|
|
95
60
|
```ts
|
|
96
|
-
|
|
61
|
+
await runtime.run(() =>
|
|
62
|
+
Effect.gen(async function* () {
|
|
63
|
+
const database = yield* Database
|
|
97
64
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
async () => {
|
|
101
|
-
const database = new Database()
|
|
102
|
-
|
|
103
|
-
await database.connect()
|
|
104
|
-
|
|
105
|
-
return database
|
|
106
|
-
},
|
|
107
|
-
(database) => database.close()
|
|
65
|
+
return Result.ok(database)
|
|
66
|
+
})
|
|
108
67
|
)
|
|
109
|
-
|
|
110
|
-
export const UserRepositoryLive = Layer.make(UserRepository, () => new UserRepository())
|
|
111
|
-
|
|
112
|
-
export const AppLive = Layer.merge(DatabaseLive, UserRepositoryLive)
|
|
113
68
|
```
|
|
114
69
|
|
|
115
|
-
|
|
70
|
+
If a program asks that Runtime for a Service its environment does not provide, TypeScript rejects the call.
|
|
116
71
|
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
72
|
+
```text
|
|
73
|
+
yield* Database
|
|
74
|
+
│
|
|
75
|
+
▼
|
|
76
|
+
program requires Database
|
|
77
|
+
│
|
|
78
|
+
▼
|
|
79
|
+
Layer provides Database?
|
|
80
|
+
│
|
|
81
|
+
no ├──────────► TypeScript error
|
|
82
|
+
│
|
|
83
|
+
yes
|
|
84
|
+
▼
|
|
85
|
+
Runtime can execute it
|
|
123
86
|
```
|
|
124
87
|
|
|
125
|
-
|
|
88
|
+
We call this **typechecked wiring**.
|
|
126
89
|
|
|
127
|
-
Layers
|
|
90
|
+
The Services your code uses, the implementations your Layers provide, and the programs your Runtime executes participate in the same type-level contract.
|
|
128
91
|
|
|
129
|
-
|
|
130
|
-
const DatabaseTest = Layer.succeed(Database, new InMemoryDatabase())
|
|
92
|
+
---
|
|
131
93
|
|
|
132
|
-
|
|
133
|
-
```
|
|
94
|
+
## Why better-effect?
|
|
134
95
|
|
|
135
|
-
|
|
96
|
+
`better-result` already gives TypeScript applications an excellent model for typed failures.
|
|
136
97
|
|
|
137
|
-
|
|
98
|
+
But typed errors are only one part of a growing application.
|
|
138
99
|
|
|
139
|
-
|
|
140
|
-
import { buildLayer } from 'better-effect'
|
|
100
|
+
Eventually you also need to answer:
|
|
141
101
|
|
|
142
|
-
|
|
102
|
+
- What does this service depend on?
|
|
103
|
+
- Did the application provide every dependency?
|
|
104
|
+
- Can this program run in this environment?
|
|
105
|
+
- How do I replace implementations in tests?
|
|
106
|
+
- Who owns this database connection?
|
|
107
|
+
- When should this resource be released?
|
|
143
108
|
|
|
144
|
-
|
|
109
|
+
Those problems are often discovered through container errors, startup failures, test setup, or manual composition-root maintenance.
|
|
145
110
|
|
|
146
|
-
|
|
147
|
-
await main()
|
|
148
|
-
} finally {
|
|
149
|
-
await runtime.dispose()
|
|
150
|
-
}
|
|
151
|
-
```
|
|
111
|
+
Effect has powerful ideas for solving them.
|
|
152
112
|
|
|
153
|
-
|
|
113
|
+
`better-effect` explores a smaller path:
|
|
154
114
|
|
|
155
|
-
|
|
115
|
+
**keep `better-result`, Promises and normal TypeScript — borrow the architectural ideas that make dependencies and resource lifetimes easier to reason about.**
|
|
156
116
|
|
|
157
|
-
|
|
117
|
+
### Know your dependencies before runtime
|
|
158
118
|
|
|
159
|
-
|
|
119
|
+
Services can be requested directly:
|
|
160
120
|
|
|
161
121
|
```ts
|
|
162
|
-
|
|
122
|
+
const database = yield * Database
|
|
123
|
+
```
|
|
163
124
|
|
|
164
|
-
|
|
165
|
-
name: 'transaction',
|
|
125
|
+
That access is also captured by the type system.
|
|
166
126
|
|
|
167
|
-
|
|
127
|
+
Layers know both what they provide and what their Services require. Incomplete environments can therefore fail during typechecking instead of application startup.
|
|
168
128
|
|
|
169
|
-
|
|
129
|
+
Runtime keeps that environment information and checks programs against it when they run.
|
|
170
130
|
|
|
171
|
-
|
|
172
|
-
})
|
|
173
|
-
```
|
|
131
|
+
### Compose application environments
|
|
174
132
|
|
|
175
|
-
|
|
133
|
+
Layers describe implementations without making your application code depend on a specific DI container.
|
|
176
134
|
|
|
177
135
|
```ts
|
|
178
|
-
|
|
179
|
-
Symbol.dispose
|
|
136
|
+
const AppLive = Layer.merge(DatabaseLive, UserRepositoryLive, AuthServiceLive)
|
|
180
137
|
```
|
|
181
138
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
If both `use` and `release` fail, the error produced by `use` is preserved.
|
|
139
|
+
Testing can replace implementations explicitly:
|
|
185
140
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
```text
|
|
189
|
-
1. use failure
|
|
190
|
-
2. release failure
|
|
191
|
-
3. successful use value
|
|
141
|
+
```ts
|
|
142
|
+
const AppTest = Layer.override(AppLive, DatabaseTest)
|
|
192
143
|
```
|
|
193
144
|
|
|
194
|
-
|
|
145
|
+
The environment contract remains typed after the override.
|
|
195
146
|
|
|
196
|
-
|
|
147
|
+
### Own resource lifetimes
|
|
197
148
|
|
|
198
|
-
|
|
199
|
-
| --------------- | ----------------------------------------------------- |
|
|
200
|
-
| `Service` | Request a contextual dependency |
|
|
201
|
-
| `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 |
|
|
204
|
-
| `better-result` | Typed failures and generator control flow |
|
|
149
|
+
Some dependencies are values.
|
|
205
150
|
|
|
206
|
-
|
|
151
|
+
Others own connections, sessions, files or other resources.
|
|
207
152
|
|
|
208
|
-
|
|
153
|
+
`Layer.scoped`, `Layer.scopedGen`, `Effect.acquireRelease`, `Effect.add` and `Scope` make their lifetime explicit.
|
|
209
154
|
|
|
210
|
-
```
|
|
211
|
-
|
|
155
|
+
```ts
|
|
156
|
+
const DatabaseLive = Layer.scoped(
|
|
157
|
+
Database,
|
|
158
|
+
() => Database.connect(),
|
|
159
|
+
(database) => database.close()
|
|
160
|
+
)
|
|
212
161
|
```
|
|
213
162
|
|
|
214
|
-
|
|
163
|
+
Runtime owns the application lifetime and safely releases scoped resources when that lifetime ends.
|
|
215
164
|
|
|
216
|
-
|
|
217
|
-
- SQLite in memory with `Bun.SQL`
|
|
218
|
-
- user login
|
|
219
|
-
- session authentication
|
|
220
|
-
- TODO CRUD
|
|
221
|
-
- `Service` dependency access
|
|
222
|
-
- Layer composition
|
|
223
|
-
- scoped database lifecycle
|
|
224
|
-
- `Resource.acquireUseRelease()`
|
|
225
|
-
- ITI as the DI backend
|
|
165
|
+
Resources acquired during an individual execution belong to that execution instead.
|
|
226
166
|
|
|
227
|
-
|
|
167
|
+
### Keep your runtime choices
|
|
228
168
|
|
|
229
|
-
|
|
230
|
-
bun examples/todo-api/index.ts
|
|
231
|
-
```
|
|
169
|
+
`better-effect` is not a replacement implementation of Effect.
|
|
232
170
|
|
|
233
|
-
|
|
171
|
+
It does not introduce a fiber runtime, scheduler, streams, queues or a public `Effect<A, E, R>` abstraction.
|
|
234
172
|
|
|
235
|
-
|
|
173
|
+
`Effect.gen` builds on `better-result` generator composition while carrying Service requirements through the TypeScript type system.
|
|
236
174
|
|
|
237
|
-
|
|
238
|
-
bun install
|
|
239
|
-
```
|
|
175
|
+
Dependency resolution stays behind a pluggable backend.
|
|
240
176
|
|
|
241
|
-
|
|
177
|
+
Your application can keep using ordinary Promises and existing libraries.
|
|
242
178
|
|
|
243
|
-
|
|
244
|
-
bun test
|
|
245
|
-
```
|
|
179
|
+
---
|
|
246
180
|
|
|
247
|
-
|
|
181
|
+
## How it compares
|
|
248
182
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
183
|
+
| | better-result | better-effect | Effect |
|
|
184
|
+
| ----------------------------- | ------------- | ------------------- | --------------- |
|
|
185
|
+
| Typed success/failure | ✓ | ✓ via better-result | ✓ |
|
|
186
|
+
| Generator composition | ✓ | ✓ | ✓ |
|
|
187
|
+
| Contextual Services | — | ✓ | ✓ |
|
|
188
|
+
| Dependency requirements | — | ✓ | ✓ |
|
|
189
|
+
| Checked environments | — | ✓ | ✓ |
|
|
190
|
+
| Scoped resource lifetimes | — | ✓ | ✓ |
|
|
191
|
+
| Pluggable external DI backend | — | ✓ | different model |
|
|
192
|
+
| Fiber runtime | — | — | ✓ |
|
|
193
|
+
| Structured concurrency | — | — | ✓ |
|
|
194
|
+
| Streams / queues / schedules | — | — | ✓ |
|
|
195
|
+
| Full effect ecosystem | — | — | ✓ |
|
|
252
196
|
|
|
253
|
-
|
|
197
|
+
### Choose `better-result`
|
|
254
198
|
|
|
255
|
-
|
|
256
|
-
bun run check
|
|
257
|
-
```
|
|
199
|
+
When typed error handling and Result composition are enough.
|
|
258
200
|
|
|
259
|
-
|
|
201
|
+
### Add `better-effect`
|
|
260
202
|
|
|
261
|
-
-
|
|
262
|
-
- TypeScript
|
|
263
|
-
- tsdown for library builds
|
|
264
|
-
- Oxlint
|
|
265
|
-
- Oxfmt
|
|
266
|
-
- publint
|
|
203
|
+
When your Result-based application also needs contextual Services, typechecked application wiring, composable environments, or resource lifetime management.
|
|
267
204
|
|
|
268
|
-
|
|
205
|
+
### Choose Effect
|
|
269
206
|
|
|
270
|
-
|
|
207
|
+
When you want a complete effect system and its runtime, concurrency model, dependency model, resource management and broader ecosystem.
|
|
271
208
|
|
|
272
|
-
`better-effect`
|
|
209
|
+
`better-effect` is inspired by some of those ideas. It is intentionally not a reimplementation of the whole system.
|
|
273
210
|
|
|
274
|
-
|
|
211
|
+
---
|
|
275
212
|
|
|
276
|
-
|
|
277
|
-
- schedules
|
|
278
|
-
- streams
|
|
279
|
-
- queues
|
|
280
|
-
- a dependency graph runtime
|
|
281
|
-
- a custom DI container
|
|
282
|
-
- a custom Context
|
|
283
|
-
- a custom Scope runtime
|
|
284
|
-
- `Effect<A, E, R>`
|
|
213
|
+
## Core ideas
|
|
285
214
|
|
|
286
|
-
###
|
|
215
|
+
### Typechecked wiring
|
|
287
216
|
|
|
288
|
-
|
|
217
|
+
**Service requirements → Layer completeness → Runtime validation**
|
|
289
218
|
|
|
290
|
-
|
|
219
|
+
Use a Service and its requirement follows the program.
|
|
291
220
|
|
|
292
|
-
|
|
221
|
+
Build an incomplete environment and TypeScript tells you what is missing.
|
|
293
222
|
|
|
294
|
-
|
|
223
|
+
Run a program against an incompatible Runtime and the mismatch remains visible at compile time.
|
|
295
224
|
|
|
296
|
-
|
|
225
|
+
### Composable environments
|
|
297
226
|
|
|
298
|
-
|
|
299
|
-
const auth = yield * AuthService
|
|
300
|
-
// AuthService
|
|
301
|
-
```
|
|
227
|
+
**Layer → merge → override → DI backend**
|
|
302
228
|
|
|
303
|
-
|
|
229
|
+
Describe application implementations independently from the container responsible for resolving them.
|
|
304
230
|
|
|
305
|
-
|
|
306
|
-
const database = await ServiceRuntime.resolve(Database)
|
|
307
|
-
// Database
|
|
308
|
-
```
|
|
231
|
+
Compose production environments and replace selected implementations for tests.
|
|
309
232
|
|
|
310
|
-
|
|
233
|
+
### Scoped lifetimes
|
|
311
234
|
|
|
312
|
-
|
|
235
|
+
**Scope → scoped Layers → acquire/release → graceful Runtime disposal**
|
|
313
236
|
|
|
314
|
-
|
|
237
|
+
Make ownership explicit for resources that need cleanup.
|
|
315
238
|
|
|
316
|
-
|
|
239
|
+
Application resources live with the Runtime. Execution resources live with the execution.
|
|
317
240
|
|
|
318
|
-
|
|
241
|
+
### better-result underneath
|
|
319
242
|
|
|
320
|
-
|
|
243
|
+
**Result → Result.gen → Effect.gen → pipe**
|
|
321
244
|
|
|
322
|
-
|
|
245
|
+
Keep `better-result` as the source of truth for typed successes, failures, short-circuiting
|
|
246
|
+
and generator control flow. `Effect.gen` delegates to `Result.gen`; it adds only the
|
|
247
|
+
phantom Service requirements that TypeScript needs to check the application environment.
|
|
248
|
+
At runtime, an `EffectResult` is still a `better-result` Result; the requirements exist
|
|
249
|
+
only in the type.
|
|
323
250
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
Layer
|
|
327
|
-
Resource
|
|
328
|
-
DI adapters
|
|
329
|
-
better-result integration
|
|
330
|
-
```
|
|
251
|
+
For a linear workflow, `pipe` composes the same kind of program without introducing a
|
|
252
|
+
second Result model or a lazy Effect runtime:
|
|
331
253
|
|
|
332
|
-
|
|
254
|
+
```ts
|
|
255
|
+
import { Effect, pipe } from 'better-effect'
|
|
256
|
+
|
|
257
|
+
const program = pipe(
|
|
258
|
+
findUser(id),
|
|
259
|
+
Effect.map((user: User) => user.email),
|
|
260
|
+
Effect.andThen(loadPermissions),
|
|
261
|
+
Effect.mapError((cause: LoadUserError | PermissionError) => new ApplicationError({ cause }))
|
|
262
|
+
)
|
|
263
|
+
```
|
|
333
264
|
|
|
334
|
-
|
|
265
|
+
The combinators keep the `better-result` semantics: `Effect.map` changes the success
|
|
266
|
+
type, `Effect.mapError` changes the error type, and `Effect.andThen` only calls the next
|
|
267
|
+
step after an `Ok`. The pipeline carries the requirements of every step, so Runtime
|
|
268
|
+
still rejects it when its Layer does not provide every required Service.
|
|
335
269
|
|
|
336
|
-
|
|
270
|
+
Use `Effect.gen` for larger workflows with several intermediate values, branches or
|
|
271
|
+
procedural logic. Use `pipe` for concise, linear composition; both are ways to compose
|
|
272
|
+
`better-result` programs while keeping dependency checking in the `better-effect` layer.
|
package/dist/adapters/iti.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { V as AnyServiceToken, t as LayerBackend, v as LayerProvider } from "../index-BYQKfyeJ.mjs";
|
|
2
2
|
//#region src/adapters/iti.d.ts
|
|
3
3
|
declare class ItiLayerBackend implements LayerBackend {
|
|
4
4
|
private container;
|
|
@@ -1 +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;
|
|
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"}
|
package/dist/adapters/iti.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as DuplicateServiceError, o as ServiceNotFoundError } from "../errors-
|
|
1
|
+
import { n as DuplicateServiceError, o as ServiceNotFoundError } from "../errors-DlHCwICc.mjs";
|
|
2
2
|
import { createContainer } from "iti";
|
|
3
3
|
//#region src/adapters/iti.ts
|
|
4
4
|
var ItiLayerBackend = class {
|
|
@@ -18,7 +18,6 @@ var ItiLayerBackend = class {
|
|
|
18
18
|
if (this.registered.has(token)) throw new DuplicateServiceError(token);
|
|
19
19
|
const key = this.keyFor(token);
|
|
20
20
|
this.container = this.container.add({ [key]: provider.acquire });
|
|
21
|
-
if (provider.release) this.container = this.container.addDisposer({ [key]: provider.release });
|
|
22
21
|
this.registered.add(token);
|
|
23
22
|
}
|
|
24
23
|
resolve(token) {
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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"}
|
|
@@ -51,6 +51,10 @@ var LayerGeneratorYieldError = class extends Error {
|
|
|
51
51
|
this.name = "LayerGeneratorYieldError";
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* @deprecated The public managed handle is `Runtime`; this error remains for
|
|
56
|
+
* compatibility with low-level Layer integrations.
|
|
57
|
+
*/
|
|
54
58
|
var BuiltLayerDisposedError = class extends Error {
|
|
55
59
|
constructor() {
|
|
56
60
|
super("Cannot run a program using a disposed Layer");
|
|
@@ -60,4 +64,4 @@ var BuiltLayerDisposedError = class extends Error {
|
|
|
60
64
|
//#endregion
|
|
61
65
|
export { LayerRegistrationError as a, LayerGeneratorYieldError as i, DuplicateServiceError as n, ServiceNotFoundError as o, LayerDisposeError as r, ServiceRuntimeNotConfiguredError as s, BuiltLayerDisposedError as t };
|
|
62
66
|
|
|
63
|
-
//# sourceMappingURL=errors-
|
|
67
|
+
//# sourceMappingURL=errors-DlHCwICc.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors-
|
|
1
|
+
{"version":3,"file":"errors-DlHCwICc.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\n/**\n * @deprecated The public managed handle is `Runtime`; this error remains for\n * compatibility with low-level Layer integrations.\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;;;;;AAMA,IAAa,0BAAb,cAA6C,MAAM;CACjD,cAAc;EACZ,MAAM,6CAA6C;EAEnD,KAAK,OAAO;CACd;AACF"}
|