better-effect 0.1.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 +336 -0
- package/dist/adapters/iti.d.mts +2 -0
- package/dist/adapters/iti.mjs +30 -0
- package/dist/adapters/iti.mjs.map +1 -0
- package/dist/index.d.mts +79 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +139 -0
- package/dist/index.mjs.map +1 -0
- package/dist/iti-xXAb9-Qs.d.mts +45 -0
- package/dist/iti-xXAb9-Qs.d.mts.map +1 -0
- package/package.json +74 -0
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
# better-effect
|
|
2
|
+
|
|
3
|
+
Lightweight Effect-inspired primitives built around [`better-result`](https://www.npmjs.com/package/better-result).
|
|
4
|
+
|
|
5
|
+
`better-effect` focuses on a small set of ideas that are useful in application code without bringing in a full effect runtime:
|
|
6
|
+
|
|
7
|
+
- **Service** — contextual dependency access with `yield*`
|
|
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
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun add better-effect better-result
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If you want the ITI adapter:
|
|
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.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { Result } from 'better-result'
|
|
32
|
+
import { Service } from 'better-effect'
|
|
33
|
+
|
|
34
|
+
export class Database extends Service<Database>() {
|
|
35
|
+
findUser(email: string) {
|
|
36
|
+
return Promise.resolve({
|
|
37
|
+
id: '1',
|
|
38
|
+
email
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class UserRepository extends Service<UserRepository>() {
|
|
44
|
+
findByEmail(email: string) {
|
|
45
|
+
return Result.gen(async function* () {
|
|
46
|
+
const database = yield* Database
|
|
47
|
+
|
|
48
|
+
const user = await database.findUser(email)
|
|
49
|
+
|
|
50
|
+
return Result.ok(user)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
There are no string tokens:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const database = yield * Database
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The class itself is the identity used by the resolver, and the result is inferred as `Database`.
|
|
63
|
+
|
|
64
|
+
### Why classes are tokens
|
|
65
|
+
|
|
66
|
+
Using constructors directly avoids duplicated identifiers such as:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
Service<AuthService>()('authService')
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
and prevents string-key collisions or typos.
|
|
73
|
+
|
|
74
|
+
Conceptually:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
yield* AuthService
|
|
78
|
+
│
|
|
79
|
+
▼
|
|
80
|
+
ServiceRuntime
|
|
81
|
+
│
|
|
82
|
+
▼
|
|
83
|
+
ServiceResolver
|
|
84
|
+
│
|
|
85
|
+
▼
|
|
86
|
+
AuthService instance
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Layer
|
|
90
|
+
|
|
91
|
+
A Layer describes which implementations form an application environment.
|
|
92
|
+
|
|
93
|
+
It does **not** implement dependency resolution. That remains the responsibility of the configured backend.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { Layer } from 'better-effect'
|
|
97
|
+
|
|
98
|
+
export const DatabaseLive = Layer.scoped(
|
|
99
|
+
Database,
|
|
100
|
+
async () => {
|
|
101
|
+
const database = new Database()
|
|
102
|
+
|
|
103
|
+
await database.connect()
|
|
104
|
+
|
|
105
|
+
return database
|
|
106
|
+
},
|
|
107
|
+
(database) => database.close()
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
export const UserRepositoryLive = Layer.make(UserRepository, () => new UserRepository())
|
|
111
|
+
|
|
112
|
+
export const AppLive = Layer.merge(DatabaseLive, UserRepositoryLive)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The core Layer API intentionally stays small:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
Layer.make(Service, acquire)
|
|
119
|
+
Layer.succeed(Service, instance)
|
|
120
|
+
Layer.scoped(Service, acquire, release)
|
|
121
|
+
Layer.merge(...layers)
|
|
122
|
+
Layer.override(base, ...overrides)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Test environments
|
|
126
|
+
|
|
127
|
+
Layers make implementation replacement explicit:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const DatabaseTest = Layer.succeed(Database, new InMemoryDatabase())
|
|
131
|
+
|
|
132
|
+
const AppTest = Layer.override(AppLive, DatabaseTest)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## ITI adapter
|
|
136
|
+
|
|
137
|
+
`better-effect` does not depend on ITI in its core. ITI is just one possible backend.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { buildLayer } from 'better-effect'
|
|
141
|
+
|
|
142
|
+
import { ItiLayerBackend } from 'better-effect/iti'
|
|
143
|
+
|
|
144
|
+
const runtime = await buildLayer(AppLive, new ItiLayerBackend())
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
await main()
|
|
148
|
+
} finally {
|
|
149
|
+
await runtime.dispose()
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
This keeps application code independent from the DI container.
|
|
154
|
+
|
|
155
|
+
A different backend can implement the same resolver/backend contracts without changing Services or Layers.
|
|
156
|
+
|
|
157
|
+
## Resource
|
|
158
|
+
|
|
159
|
+
`Resource.acquireUseRelease()` handles local resource lifecycle while preserving typed `Result` errors.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { Resource } from 'better-effect'
|
|
163
|
+
|
|
164
|
+
const result = await Resource.acquireUseRelease({
|
|
165
|
+
name: 'transaction',
|
|
166
|
+
|
|
167
|
+
acquire: () => database.begin(),
|
|
168
|
+
|
|
169
|
+
use: (transaction) => executeCommand(transaction),
|
|
170
|
+
|
|
171
|
+
release: (transaction) => transaction.close()
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
When `release` is omitted, `Resource` attempts to use the JavaScript explicit resource management protocol:
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
Symbol.asyncDispose
|
|
179
|
+
Symbol.dispose
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Error precedence
|
|
183
|
+
|
|
184
|
+
If both `use` and `release` fail, the error produced by `use` is preserved.
|
|
185
|
+
|
|
186
|
+
The precedence is:
|
|
187
|
+
|
|
188
|
+
```text
|
|
189
|
+
1. use failure
|
|
190
|
+
2. release failure
|
|
191
|
+
3. successful use value
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Acquisition exceptions, rejected promises, and unexpected failures are normalized through `better-result`.
|
|
195
|
+
|
|
196
|
+
## Service vs Layer vs Resource
|
|
197
|
+
|
|
198
|
+
| Primitive | Responsibility |
|
|
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 |
|
|
205
|
+
|
|
206
|
+
## Complete example
|
|
207
|
+
|
|
208
|
+
The repository contains a TODO API example under:
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
examples/todo-api
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
It demonstrates:
|
|
215
|
+
|
|
216
|
+
- Bun HTTP server
|
|
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
|
|
226
|
+
|
|
227
|
+
Run it from the repository root:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
bun examples/todo-api/index.ts
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Development
|
|
234
|
+
|
|
235
|
+
Install dependencies:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
bun install
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Run tests:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
bun test
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Typecheck:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
bun run typecheck
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Run the full quality gate:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
bun run check
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
The project uses:
|
|
260
|
+
|
|
261
|
+
- Bun as package manager and test runner
|
|
262
|
+
- TypeScript
|
|
263
|
+
- tsdown for library builds
|
|
264
|
+
- Oxlint
|
|
265
|
+
- Oxfmt
|
|
266
|
+
- publint
|
|
267
|
+
|
|
268
|
+
## Design principles
|
|
269
|
+
|
|
270
|
+
### Keep the core small
|
|
271
|
+
|
|
272
|
+
`better-effect` should not grow into a second Effect runtime.
|
|
273
|
+
|
|
274
|
+
The core intentionally does not implement:
|
|
275
|
+
|
|
276
|
+
- fibers
|
|
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>`
|
|
285
|
+
|
|
286
|
+
### Delegate instead of rebuilding
|
|
287
|
+
|
|
288
|
+
Dependency resolution, caching and container lifecycle belong to DI backends.
|
|
289
|
+
|
|
290
|
+
`better-effect` supplies the protocol and composition primitives.
|
|
291
|
+
|
|
292
|
+
### Preserve inference at public boundaries
|
|
293
|
+
|
|
294
|
+
Type safety is part of the API.
|
|
295
|
+
|
|
296
|
+
For example:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
const auth = yield * AuthService
|
|
300
|
+
// AuthService
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
and:
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
const database = await ServiceRuntime.resolve(Database)
|
|
307
|
+
// Database
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
must preserve exact instance types.
|
|
311
|
+
|
|
312
|
+
### Type erasure stays internal
|
|
313
|
+
|
|
314
|
+
Layers may store heterogeneous providers internally using erased types.
|
|
315
|
+
|
|
316
|
+
The public constructors (`Layer.make`, `Layer.succeed`, `Layer.scoped`) are responsible for preserving the relationship between a Service token and its instance type.
|
|
317
|
+
|
|
318
|
+
## Current scope
|
|
319
|
+
|
|
320
|
+
The project is intentionally small and experimental.
|
|
321
|
+
|
|
322
|
+
The initial scope is:
|
|
323
|
+
|
|
324
|
+
```text
|
|
325
|
+
Service
|
|
326
|
+
Layer
|
|
327
|
+
Resource
|
|
328
|
+
DI adapters
|
|
329
|
+
better-result integration
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
New abstractions should only be added when they solve a concrete problem without duplicating responsibilities already handled well by another library.
|
|
333
|
+
|
|
334
|
+
## License
|
|
335
|
+
|
|
336
|
+
MIT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createContainer } from "iti";
|
|
2
|
+
//#region src/adapters/iti.ts
|
|
3
|
+
var ItiLayerBackend = class {
|
|
4
|
+
container = createContainer();
|
|
5
|
+
keys = /* @__PURE__ */ new WeakMap();
|
|
6
|
+
nextId = 0;
|
|
7
|
+
keyFor(token) {
|
|
8
|
+
const existing = this.keys.get(token);
|
|
9
|
+
if (existing) return existing;
|
|
10
|
+
const key = `better-effect:${token.name || "Service"}:${this.nextId++}`;
|
|
11
|
+
this.keys.set(token, key);
|
|
12
|
+
return key;
|
|
13
|
+
}
|
|
14
|
+
register(provider) {
|
|
15
|
+
const key = this.keyFor(provider.service);
|
|
16
|
+
this.container = this.container.add({ [key]: provider.acquire });
|
|
17
|
+
if (provider.release) this.container = this.container.addDisposer({ [key]: provider.release });
|
|
18
|
+
}
|
|
19
|
+
resolve(token) {
|
|
20
|
+
const key = this.keyFor(token);
|
|
21
|
+
return this.container.get(key);
|
|
22
|
+
}
|
|
23
|
+
async disposeAll() {
|
|
24
|
+
await this.container.disposeAll();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ItiLayerBackend };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=iti.mjs.map
|
|
@@ -0,0 +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"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { a as LayerProvider, c as ServiceClass, i as ServiceRuntime, l as ServiceInstance, n as LayerBackend, o as MaybePromise$1, r as ServiceResolver, s as AnyServiceToken, t as ItiLayerBackend } from "./iti-xXAb9-Qs.mjs";
|
|
2
|
+
import { Result, UnhandledException } from "better-result";
|
|
3
|
+
//#region src/layer/layer.d.ts
|
|
4
|
+
declare class Layer {
|
|
5
|
+
readonly providers: readonly LayerProvider[];
|
|
6
|
+
private constructor();
|
|
7
|
+
static make<S extends ServiceClass<any>>(service: S, acquire: () => MaybePromise$1<InstanceType<S>>): Layer;
|
|
8
|
+
static succeed<S extends ServiceClass<any>>(service: S, instance: InstanceType<S>): Layer;
|
|
9
|
+
static scoped<S extends ServiceClass<any>>(service: S, acquire: () => MaybePromise$1<InstanceType<S>>, release: (instance: InstanceType<S>) => MaybePromise$1<void>): Layer;
|
|
10
|
+
static merge(...layers: readonly Layer[]): Layer;
|
|
11
|
+
static override(base: Layer, ...overrides: readonly Layer[]): Layer;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/layer/runtime.d.ts
|
|
15
|
+
interface BuiltLayer {
|
|
16
|
+
dispose(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
declare const buildLayer: (layer: Layer, backend: LayerBackend) => Promise<BuiltLayer>;
|
|
19
|
+
declare class RuntimeLayer {
|
|
20
|
+
static build: typeof buildLayer;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/service/service.d.ts
|
|
24
|
+
declare function Service<Self>(): {
|
|
25
|
+
new (): {};
|
|
26
|
+
[Symbol.asyncIterator](): AsyncGenerator<never, Self, unknown>;
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/resource/errors.d.ts
|
|
30
|
+
declare const ResourceReleaseFailure_base: import("better-result").TaggedErrorClass<"ResourceReleaseFailure">;
|
|
31
|
+
declare class ResourceReleaseFailure extends ResourceReleaseFailure_base<{
|
|
32
|
+
readonly resource: string;
|
|
33
|
+
readonly cause: unknown;
|
|
34
|
+
readonly message: string;
|
|
35
|
+
}> {}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/resource/types.d.ts
|
|
38
|
+
type MaybePromise<T> = T | PromiseLike<T>;
|
|
39
|
+
type AsyncResult<T, E> = MaybePromise<Result<T, E>>;
|
|
40
|
+
type ReleaseOutcome = void | Result<void, unknown>;
|
|
41
|
+
type DisposableResource = {
|
|
42
|
+
[Symbol.dispose]?: () => void;
|
|
43
|
+
[Symbol.asyncDispose]?: () => MaybePromise<void>;
|
|
44
|
+
};
|
|
45
|
+
type AcquireUseReleaseOptions<R, A, AcquireError, UseError> = {
|
|
46
|
+
/**
|
|
47
|
+
* Nome utilizado para identificar o recurso
|
|
48
|
+
* em eventuais erros de release.
|
|
49
|
+
*/
|
|
50
|
+
readonly name: string;
|
|
51
|
+
/**
|
|
52
|
+
* Adquire o recurso.
|
|
53
|
+
*/
|
|
54
|
+
readonly acquire: () => AsyncResult<R, AcquireError>;
|
|
55
|
+
/**
|
|
56
|
+
* Executa a operação usando o recurso.
|
|
57
|
+
*/
|
|
58
|
+
readonly use: (resource: R) => AsyncResult<A, UseError>;
|
|
59
|
+
/**
|
|
60
|
+
* Libera o recurso.
|
|
61
|
+
*
|
|
62
|
+
* Se não informado, Resource tentará usar:
|
|
63
|
+
*
|
|
64
|
+
* Symbol.asyncDispose
|
|
65
|
+
* Symbol.dispose
|
|
66
|
+
*
|
|
67
|
+
* nessa ordem.
|
|
68
|
+
*/
|
|
69
|
+
readonly release?: (resource: R) => MaybePromise<ReleaseOutcome>;
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/resource/resource.d.ts
|
|
73
|
+
declare const acquireUseRelease: <R, A, AcquireError, UseError>({ name, acquire, use, release }: AcquireUseReleaseOptions<R, A, AcquireError, UseError>) => Promise<Result<A, AcquireError | UseError | UnhandledException | ResourceReleaseFailure>>;
|
|
74
|
+
declare const Resource: {
|
|
75
|
+
readonly acquireUseRelease: typeof acquireUseRelease;
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
export { type AcquireUseReleaseOptions, type AnyServiceToken, type AsyncResult, type BuiltLayer, type DisposableResource, ItiLayerBackend, Layer, type LayerBackend, type MaybePromise, type ReleaseOutcome, Resource, ResourceReleaseFailure, RuntimeLayer, Service, type ServiceInstance, type ServiceResolver, ServiceRuntime, buildLayer };
|
|
79
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/layer/layer.ts","../src/layer/runtime.ts","../src/service/service.ts","../src/resource/errors.ts","../src/resource/types.ts","../src/resource/resource.ts"],"mappings":";;;cAIa;WACkB,oBAAoB;UAA1C;SAEA,KAAK,UAAU,mBACpB,SAAS,GACT,eAAe,eAAa,aAAa,MACxC;SASI,QAAQ,UAAU,mBAAmB,SAAS,GAAG,UAAU,aAAa,KAAK;SAI7E,OAAO,UAAU,mBACtB,SAAS,GACT,eAAe,eAAa,aAAa,KACzC,UAAU,UAAU,aAAa,OAAO,uBACvC;SAWI,SAAS,iBAAiB,UAAU;SAkBpC,SAAS,MAAM,UAAU,oBAAoB,UAAU;;;;UCnD/C;EACf,WAAW;;cAGA,aAAU,OAAiB,OAAK,SAAW,iBAAe,QAAQ;cAkBlE;SACJ,cAAK;;;;iBCxBE,QAAQ;;GAGoB,OAAA,kBAAA,sBAAsB;;;;;cCLrD,+BAA+B;WACjC;WACA;WACA;;;;KCHC,aAAa,KAAK,IAAI,YAAY;KAElC,YAAY,GAAG,KAAK,aAAa,OAAW,GAAG;KAE/C,wBAAwB;KAExB;GACT,OAAO;GAEP,OAAO,sBAAsB;;KAGpB,yBAAyB,GAAG,GAAG,cAAc;;;;;WAK9C;;;;WAKA,eAAe,YAAY,GAAG;;;;WAK9B,MAAM,UAAU,MAAM,YAAY,GAAG;;;;;;;;;;;WAYrC,WAAW,UAAU,MAAM,aAAa;;;;cCjC7C,oBAAqB,GAAG,GAAG,cAAc,YAAQ,MAAA,SAAA,KAAA,WAKpD,yBAAyB,GAAG,GAAG,cAAc,cAAY,QAC1D,OAAW,GAAG,eAAe,WAAW,qBAAqB;cAmBlD;qCAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ItiLayerBackend } from "./adapters/iti.mjs";
|
|
2
|
+
import { Result, TaggedError } from "better-result";
|
|
3
|
+
//#region src/service/runtime.ts
|
|
4
|
+
let currentResolver;
|
|
5
|
+
var ServiceRuntime = class {
|
|
6
|
+
static configure(resolver) {
|
|
7
|
+
currentResolver = resolver;
|
|
8
|
+
}
|
|
9
|
+
static async resolve(token) {
|
|
10
|
+
if (!currentResolver) throw new Error("ServiceRuntime has not been configured");
|
|
11
|
+
return await currentResolver.resolve(token);
|
|
12
|
+
}
|
|
13
|
+
static reset() {
|
|
14
|
+
currentResolver = void 0;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/service/service.ts
|
|
19
|
+
function Service() {
|
|
20
|
+
return class {
|
|
21
|
+
static async *[Symbol.asyncIterator]() {
|
|
22
|
+
return await ServiceRuntime.resolve(this);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/layer/layer.ts
|
|
28
|
+
var Layer = class Layer {
|
|
29
|
+
providers;
|
|
30
|
+
constructor(providers) {
|
|
31
|
+
this.providers = providers;
|
|
32
|
+
}
|
|
33
|
+
static make(service, acquire) {
|
|
34
|
+
return new Layer([{
|
|
35
|
+
service,
|
|
36
|
+
acquire
|
|
37
|
+
}]);
|
|
38
|
+
}
|
|
39
|
+
static succeed(service, instance) {
|
|
40
|
+
return Layer.make(service, () => instance);
|
|
41
|
+
}
|
|
42
|
+
static scoped(service, acquire, release) {
|
|
43
|
+
return new Layer([{
|
|
44
|
+
service,
|
|
45
|
+
acquire,
|
|
46
|
+
release: (instance) => release(instance)
|
|
47
|
+
}]);
|
|
48
|
+
}
|
|
49
|
+
static merge(...layers) {
|
|
50
|
+
const providers = /* @__PURE__ */ new Map();
|
|
51
|
+
for (const layer of layers) for (const provider of layer.providers) {
|
|
52
|
+
const service = provider.service;
|
|
53
|
+
if (providers.has(service)) throw new Error(`Duplicate service "${service.name}" in Layer.merge()`);
|
|
54
|
+
providers.set(service, provider);
|
|
55
|
+
}
|
|
56
|
+
return new Layer([...providers.values()]);
|
|
57
|
+
}
|
|
58
|
+
static override(base, ...overrides) {
|
|
59
|
+
const providers = /* @__PURE__ */ new Map();
|
|
60
|
+
for (const provider of base.providers) providers.set(provider.service, provider);
|
|
61
|
+
for (const layer of overrides) for (const provider of layer.providers) providers.set(provider.service, provider);
|
|
62
|
+
return new Layer([...providers.values()]);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/layer/runtime.ts
|
|
67
|
+
const buildLayer = async (layer, backend) => {
|
|
68
|
+
for (const provider of layer.providers) await backend.register(provider);
|
|
69
|
+
ServiceRuntime.configure(backend);
|
|
70
|
+
return { async dispose() {
|
|
71
|
+
try {
|
|
72
|
+
await backend.disposeAll();
|
|
73
|
+
} finally {
|
|
74
|
+
ServiceRuntime.reset();
|
|
75
|
+
}
|
|
76
|
+
} };
|
|
77
|
+
};
|
|
78
|
+
var RuntimeLayer = class {
|
|
79
|
+
static build = buildLayer;
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/resource/errors.ts
|
|
83
|
+
var ResourceReleaseFailure = class extends TaggedError("ResourceReleaseFailure") {};
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/resource/internal.ts
|
|
86
|
+
const disposeResource = (resource) => {
|
|
87
|
+
const candidate = Object(resource);
|
|
88
|
+
return (candidate[Symbol.asyncDispose] ?? candidate[Symbol.dispose])?.call(candidate);
|
|
89
|
+
};
|
|
90
|
+
const toReleaseFailure = (resource, cause) => new ResourceReleaseFailure({
|
|
91
|
+
resource,
|
|
92
|
+
cause,
|
|
93
|
+
message: `Failed to release resource: ${resource}`
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* Executa uma operação que retorna Result e converte
|
|
97
|
+
* exceptions/rejections inesperadas para UnhandledException.
|
|
98
|
+
*/
|
|
99
|
+
const runResult = async (operation) => {
|
|
100
|
+
return (await Result.tryPromise(() => Promise.resolve(operation()))).andThen((result) => result);
|
|
101
|
+
};
|
|
102
|
+
const normalizeReleaseOutcome = (name, outcome) => {
|
|
103
|
+
if (outcome === void 0) return Result.ok();
|
|
104
|
+
return outcome.mapError((cause) => toReleaseFailure(name, cause));
|
|
105
|
+
};
|
|
106
|
+
const runRelease = async (name, resource, release) => {
|
|
107
|
+
return (await Result.tryPromise({
|
|
108
|
+
try: () => Promise.resolve(release(resource)),
|
|
109
|
+
catch: (cause) => toReleaseFailure(name, cause)
|
|
110
|
+
})).andThen((outcome) => normalizeReleaseOutcome(name, outcome));
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* Precedência:
|
|
114
|
+
*
|
|
115
|
+
* 1. erro ocorrido durante use
|
|
116
|
+
* 2. erro ocorrido durante release
|
|
117
|
+
* 3. valor produzido por use
|
|
118
|
+
*/
|
|
119
|
+
const combineUseAndRelease = (used, released) => used.andThen((value) => released.map(() => value));
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/resource/resource.ts
|
|
122
|
+
const acquireUseRelease = ({ name, acquire, use, release = disposeResource }) => Result.gen(async function* () {
|
|
123
|
+
const resource = yield* Result.await(runResult(acquire));
|
|
124
|
+
/**
|
|
125
|
+
* Não usamos yield* aqui.
|
|
126
|
+
*
|
|
127
|
+
* Se use retornar Err, o generator seria
|
|
128
|
+
* encerrado imediatamente e o release
|
|
129
|
+
* nunca seria executado.
|
|
130
|
+
*/
|
|
131
|
+
const used = await runResult(() => use(resource));
|
|
132
|
+
const released = await runRelease(name, resource, release);
|
|
133
|
+
return combineUseAndRelease(used, released);
|
|
134
|
+
});
|
|
135
|
+
const Resource = { acquireUseRelease };
|
|
136
|
+
//#endregion
|
|
137
|
+
export { ItiLayerBackend, Layer, Resource, ResourceReleaseFailure, RuntimeLayer, Service, ServiceRuntime, buildLayer };
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/service/runtime.ts","../src/service/service.ts","../src/layer/layer.ts","../src/layer/runtime.ts","../src/resource/errors.ts","../src/resource/internal.ts","../src/resource/resource.ts"],"sourcesContent":["import type { AnyServiceToken } from './types'\n\nexport interface ServiceResolver {\n resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>\n}\n\nlet currentResolver: ServiceResolver | undefined\n\nexport class ServiceRuntime {\n static configure(resolver: ServiceResolver): void {\n currentResolver = resolver\n }\n\n static async resolve<T extends AnyServiceToken>(token: T): Promise<InstanceType<T>> {\n if (!currentResolver) {\n throw new Error('ServiceRuntime has not been configured')\n }\n\n return await currentResolver.resolve(token)\n }\n\n static reset(): void {\n currentResolver = undefined\n }\n}\n","import { ServiceRuntime } from './runtime'\n\nimport type { ServiceToken } from './types'\n\nexport function Service<Self>() {\n return class {\n // oxlint-disable-next-line require-yield\n static async *[Symbol.asyncIterator](): AsyncGenerator<never, Self, unknown> {\n return await ServiceRuntime.resolve(this as unknown as ServiceToken<Self>)\n }\n }\n}\n","import type { ServiceClass } from '../service/types'\n\nimport type { LayerProvider, MaybePromise } from './types'\n\nexport class Layer {\n private constructor(readonly providers: readonly LayerProvider[]) {}\n\n static make<S extends ServiceClass<any>>(\n service: S,\n acquire: () => MaybePromise<InstanceType<S>>\n ): Layer {\n return new Layer([\n {\n service,\n acquire\n }\n ])\n }\n\n static succeed<S extends ServiceClass<any>>(service: S, instance: InstanceType<S>): Layer {\n return Layer.make(service, () => instance)\n }\n\n static scoped<S extends ServiceClass<any>>(\n service: S,\n acquire: () => MaybePromise<InstanceType<S>>,\n release: (instance: InstanceType<S>) => MaybePromise<void>\n ): Layer {\n return new Layer([\n {\n service,\n acquire,\n\n release: (instance) => release(instance as InstanceType<S>)\n }\n ])\n }\n\n static merge(...layers: readonly Layer[]): Layer {\n const providers = new Map<ServiceClass<any>, LayerProvider>()\n\n for (const layer of layers) {\n for (const provider of layer.providers) {\n const service = provider.service\n\n if (providers.has(service)) {\n throw new Error(`Duplicate service \"${service.name}\" in Layer.merge()`)\n }\n\n providers.set(service, provider)\n }\n }\n\n return new Layer([...providers.values()])\n }\n\n static override(base: Layer, ...overrides: readonly Layer[]): Layer {\n const providers = new Map<ServiceClass<any>, LayerProvider>()\n\n for (const provider of base.providers) {\n providers.set(provider.service, provider)\n }\n\n for (const layer of overrides) {\n for (const provider of layer.providers) {\n providers.set(provider.service, provider)\n }\n }\n\n return new Layer([...providers.values()])\n }\n}\n","import { ServiceRuntime } from '../service/runtime'\n\nimport type { LayerBackend } from './backend'\nimport type { Layer } from './layer'\n\nexport interface BuiltLayer {\n dispose(): Promise<void>\n}\n\nexport const buildLayer = async (layer: Layer, backend: LayerBackend): Promise<BuiltLayer> => {\n for (const provider of layer.providers) {\n await backend.register(provider)\n }\n\n ServiceRuntime.configure(backend)\n\n return {\n async dispose() {\n try {\n await backend.disposeAll()\n } finally {\n ServiceRuntime.reset()\n }\n }\n }\n}\n\nexport class RuntimeLayer {\n static build = buildLayer\n}\n","import { TaggedError } from 'better-result'\n\nexport class ResourceReleaseFailure extends TaggedError('ResourceReleaseFailure')<{\n readonly resource: string\n readonly cause: unknown\n readonly message: string\n}> {}\n","import { Result, type Result as ResultType, type UnhandledException } from 'better-result'\n\nimport { ResourceReleaseFailure } from './errors'\n\nimport type { AsyncResult, DisposableResource, MaybePromise, ReleaseOutcome } from './types'\n\nexport const disposeResource = (resource: unknown): MaybePromise<void> => {\n const candidate = Object(resource) as DisposableResource\n\n const dispose = candidate[Symbol.asyncDispose] ?? candidate[Symbol.dispose]\n\n return dispose?.call(candidate)\n}\n\nconst toReleaseFailure = (resource: string, cause: unknown): ResourceReleaseFailure =>\n new ResourceReleaseFailure({\n resource,\n cause,\n message: `Failed to release resource: ${resource}`\n })\n\n/**\n * Executa uma operação que retorna Result e converte\n * exceptions/rejections inesperadas para UnhandledException.\n */\nexport const runResult = async <T, E>(\n operation: () => AsyncResult<T, E>\n): Promise<ResultType<T, E | UnhandledException>> => {\n const execution = await Result.tryPromise(() => Promise.resolve(operation()))\n\n return execution.andThen((result) => result)\n}\n\nconst normalizeReleaseOutcome = (\n name: string,\n outcome: ReleaseOutcome\n): ResultType<void, ResourceReleaseFailure> => {\n if (outcome === undefined) {\n return Result.ok()\n }\n\n return outcome.mapError((cause) => toReleaseFailure(name, cause))\n}\n\nexport const runRelease = async <R>(\n name: string,\n resource: R,\n release: (resource: R) => MaybePromise<ReleaseOutcome>\n): Promise<ResultType<void, ResourceReleaseFailure>> => {\n const execution = await Result.tryPromise({\n try: () => Promise.resolve(release(resource)),\n\n catch: (cause) => toReleaseFailure(name, cause)\n })\n\n return execution.andThen((outcome) => normalizeReleaseOutcome(name, outcome))\n}\n\n/**\n * Precedência:\n *\n * 1. erro ocorrido durante use\n * 2. erro ocorrido durante release\n * 3. valor produzido por use\n */\nexport const combineUseAndRelease = <A, E>(\n used: ResultType<A, E>,\n released: ResultType<void, ResourceReleaseFailure>\n): ResultType<A, E | ResourceReleaseFailure> => used.andThen((value) => released.map(() => value))\n","import { Result, type Result as ResultType, type UnhandledException } from 'better-result'\n\nimport { ResourceReleaseFailure } from './errors'\n\nimport { combineUseAndRelease, disposeResource, runRelease, runResult } from './internal'\n\nimport type { AcquireUseReleaseOptions } from './types'\n\nconst acquireUseRelease = <R, A, AcquireError, UseError>({\n name,\n acquire,\n use,\n release = disposeResource\n}: AcquireUseReleaseOptions<R, A, AcquireError, UseError>): Promise<\n ResultType<A, AcquireError | UseError | UnhandledException | ResourceReleaseFailure>\n> =>\n Result.gen(async function* () {\n const resource = yield* Result.await(runResult(acquire))\n\n /**\n * Não usamos yield* aqui.\n *\n * Se use retornar Err, o generator seria\n * encerrado imediatamente e o release\n * nunca seria executado.\n */\n const used = await runResult(() => use(resource))\n\n const released = await runRelease(name, resource, release)\n\n return combineUseAndRelease(used, released)\n })\n\nexport const Resource = {\n acquireUseRelease\n} as const\n"],"mappings":";;;AAMA,IAAI;AAEJ,IAAa,iBAAb,MAA4B;CAC1B,OAAO,UAAU,UAAiC;EAChD,kBAAkB;CACpB;CAEA,aAAa,QAAmC,OAAoC;EAClF,IAAI,CAAC,iBACH,MAAM,IAAI,MAAM,wCAAwC;EAG1D,OAAO,MAAM,gBAAgB,QAAQ,KAAK;CAC5C;CAEA,OAAO,QAAc;EACnB,kBAAkB,KAAA;CACpB;AACF;;;ACpBA,SAAgB,UAAgB;CAC9B,OAAO,MAAM;EAEX,eAAe,OAAO,iBAAuD;GAC3E,OAAO,MAAM,eAAe,QAAQ,IAAqC;EAC3E;CACF;AACF;;;ACPA,IAAa,QAAb,MAAa,MAAM;CACY;CAA7B,YAAoB,WAA8C;EAArC,KAAA,YAAA;CAAsC;CAEnE,OAAO,KACL,SACA,SACO;EACP,OAAO,IAAI,MAAM,CACf;GACE;GACA;EACF,CACF,CAAC;CACH;CAEA,OAAO,QAAqC,SAAY,UAAkC;EACxF,OAAO,MAAM,KAAK,eAAe,QAAQ;CAC3C;CAEA,OAAO,OACL,SACA,SACA,SACO;EACP,OAAO,IAAI,MAAM,CACf;GACE;GACA;GAEA,UAAU,aAAa,QAAQ,QAA2B;EAC5D,CACF,CAAC;CACH;CAEA,OAAO,MAAM,GAAG,QAAiC;EAC/C,MAAM,4BAAY,IAAI,IAAsC;EAE5D,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,YAAY,MAAM,WAAW;GACtC,MAAM,UAAU,SAAS;GAEzB,IAAI,UAAU,IAAI,OAAO,GACvB,MAAM,IAAI,MAAM,sBAAsB,QAAQ,KAAK,mBAAmB;GAGxE,UAAU,IAAI,SAAS,QAAQ;EACjC;EAGF,OAAO,IAAI,MAAM,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC;CAC1C;CAEA,OAAO,SAAS,MAAa,GAAG,WAAoC;EAClE,MAAM,4BAAY,IAAI,IAAsC;EAE5D,KAAK,MAAM,YAAY,KAAK,WAC1B,UAAU,IAAI,SAAS,SAAS,QAAQ;EAG1C,KAAK,MAAM,SAAS,WAClB,KAAK,MAAM,YAAY,MAAM,WAC3B,UAAU,IAAI,SAAS,SAAS,QAAQ;EAI5C,OAAO,IAAI,MAAM,CAAC,GAAG,UAAU,OAAO,CAAC,CAAC;CAC1C;AACF;;;AC9DA,MAAa,aAAa,OAAO,OAAc,YAA+C;CAC5F,KAAK,MAAM,YAAY,MAAM,WAC3B,MAAM,QAAQ,SAAS,QAAQ;CAGjC,eAAe,UAAU,OAAO;CAEhC,OAAO,EACL,MAAM,UAAU;EACd,IAAI;GACF,MAAM,QAAQ,WAAW;EAC3B,UAAU;GACR,eAAe,MAAM;EACvB;CACF,EACF;AACF;AAEA,IAAa,eAAb,MAA0B;CACxB,OAAO,QAAQ;AACjB;;;AC3BA,IAAa,yBAAb,cAA4C,YAAY,wBAAwB,CAAC,CAI9E,CAAC;;;ACAJ,MAAa,mBAAmB,aAA0C;CACxE,MAAM,YAAY,OAAO,QAAQ;CAIjC,QAFgB,UAAU,OAAO,iBAAiB,UAAU,OAAO,SAAA,EAEnD,KAAK,SAAS;AAChC;AAEA,MAAM,oBAAoB,UAAkB,UAC1C,IAAI,uBAAuB;CACzB;CACA;CACA,SAAS,+BAA+B;AAC1C,CAAC;;;;;AAMH,MAAa,YAAY,OACvB,cACmD;CAGnD,QAAO,MAFiB,OAAO,iBAAiB,QAAQ,QAAQ,UAAU,CAAC,CAAC,EAAA,CAE3D,SAAS,WAAW,MAAM;AAC7C;AAEA,MAAM,2BACJ,MACA,YAC6C;CAC7C,IAAI,YAAY,KAAA,GACd,OAAO,OAAO,GAAG;CAGnB,OAAO,QAAQ,UAAU,UAAU,iBAAiB,MAAM,KAAK,CAAC;AAClE;AAEA,MAAa,aAAa,OACxB,MACA,UACA,YACsD;CAOtD,QAAO,MANiB,OAAO,WAAW;EACxC,WAAW,QAAQ,QAAQ,QAAQ,QAAQ,CAAC;EAE5C,QAAQ,UAAU,iBAAiB,MAAM,KAAK;CAChD,CAAC,EAAA,CAEgB,SAAS,YAAY,wBAAwB,MAAM,OAAO,CAAC;AAC9E;;;;;;;;AASA,MAAa,wBACX,MACA,aAC8C,KAAK,SAAS,UAAU,SAAS,UAAU,KAAK,CAAC;;;AC5DjG,MAAM,qBAAmD,EACvD,MACA,SACA,KACA,UAAU,sBAIV,OAAO,IAAI,mBAAmB;CAC5B,MAAM,WAAW,OAAO,OAAO,MAAM,UAAU,OAAO,CAAC;;;;;;;;CASvD,MAAM,OAAO,MAAM,gBAAgB,IAAI,QAAQ,CAAC;CAEhD,MAAM,WAAW,MAAM,WAAW,MAAM,UAAU,OAAO;CAEzD,OAAO,qBAAqB,MAAM,QAAQ;AAC5C,CAAC;AAEH,MAAa,WAAW,EACtB,kBACF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/service/types.d.ts
|
|
2
|
+
type ServiceToken<A = unknown> = (abstract new (...args: any[]) => A) & {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
};
|
|
5
|
+
type ServiceClass<A = unknown> = ServiceToken<A>;
|
|
6
|
+
type AnyServiceToken = ServiceToken<any>;
|
|
7
|
+
type ServiceInstance<T extends AnyServiceToken> = InstanceType<T>;
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/layer/types.d.ts
|
|
10
|
+
type MaybePromise<T> = T | PromiseLike<T>;
|
|
11
|
+
interface LayerProvider {
|
|
12
|
+
readonly service: ServiceClass<any>;
|
|
13
|
+
readonly acquire: () => MaybePromise<unknown>;
|
|
14
|
+
readonly release?: (instance: unknown) => MaybePromise<void>;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/service/runtime.d.ts
|
|
18
|
+
interface ServiceResolver {
|
|
19
|
+
resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>;
|
|
20
|
+
}
|
|
21
|
+
declare class ServiceRuntime {
|
|
22
|
+
static configure(resolver: ServiceResolver): void;
|
|
23
|
+
static resolve<T extends AnyServiceToken>(token: T): Promise<InstanceType<T>>;
|
|
24
|
+
static reset(): void;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/layer/backend.d.ts
|
|
28
|
+
interface LayerBackend extends ServiceResolver {
|
|
29
|
+
register(provider: LayerProvider): void | PromiseLike<void>;
|
|
30
|
+
disposeAll(): void | PromiseLike<void>;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/adapters/iti.d.ts
|
|
34
|
+
declare class ItiLayerBackend implements LayerBackend {
|
|
35
|
+
private container;
|
|
36
|
+
private readonly keys;
|
|
37
|
+
private nextId;
|
|
38
|
+
private keyFor;
|
|
39
|
+
register(provider: LayerProvider): void;
|
|
40
|
+
resolve<T extends AnyServiceToken>(token: T): InstanceType<T> | PromiseLike<InstanceType<T>>;
|
|
41
|
+
disposeAll(): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { LayerProvider as a, ServiceClass as c, ServiceRuntime as i, ServiceInstance as l, LayerBackend as n, MaybePromise as o, ServiceResolver as r, AnyServiceToken as s, ItiLayerBackend as t };
|
|
45
|
+
//# sourceMappingURL=iti-xXAb9-Qs.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iti-xXAb9-Qs.d.mts","names":[],"sources":["../src/service/types.ts","../src/layer/types.ts","../src/service/runtime.ts","../src/layer/backend.ts","../src/adapters/iti.ts"],"mappings":";KAAY,aAAa,iCAAiC,gBAAgB;WAC/D;;KAGC,aAAa,eAAe,aAAa;KAEzC,kBAAkB;KAElB,gBAAgB,UAAU,mBAAmB,aAAa;;;KCN1D,aAAa,KAAK,IAAI,YAAY;UAE7B;WACN,SAAS;WAET,eAAe;WAEf,WAAW,sBAAsB;;;;UCP3B;EACf,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;;cAK9E;SACJ,UAAU,UAAU;SAId,QAAQ,UAAU,iBAAiB,OAAO,IAAI,QAAQ,aAAa;SAQzE;;;;UCjBQ,qBAAqB;EACpC,SAAS,UAAU,uBAAuB;EAE1C,qBAAqB;;;;cCAV,2BAA2B;UAC9B;mBAES;UAET;UAEA;EAgBR,SAAS,UAAU;EAcnB,QAAQ,UAAU,iBAAiB,OAAO,IAAI,aAAa,KAAK,YAAY,aAAa;EAMnF,cAAc"}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "better-effect",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight Effect-inspired primitives for better-result",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"better-result",
|
|
7
|
+
"dependency-injection",
|
|
8
|
+
"effect",
|
|
9
|
+
"layer",
|
|
10
|
+
"resource",
|
|
11
|
+
"result",
|
|
12
|
+
"typescript"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/nitoba/better-effect#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/nitoba/better-effect/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/nitoba/better-effect.git"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"exports": {
|
|
29
|
+
".": "./dist/index.mjs",
|
|
30
|
+
"./adapters/iti": "./dist/adapters/iti.mjs",
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public",
|
|
35
|
+
"registry": "https://registry.npmjs.org/"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsdown",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"test": "bun test",
|
|
41
|
+
"test:coverage": "bun test --coverage",
|
|
42
|
+
"lint": "oxlint --type-aware .",
|
|
43
|
+
"lint:fix": "oxlint --type-aware --fix .",
|
|
44
|
+
"format": "oxfmt .",
|
|
45
|
+
"format:check": "oxfmt .",
|
|
46
|
+
"publint": "publint",
|
|
47
|
+
"check": "bun run typecheck && bun test && bun run lint && bun run format:check && bun run build && bun run publint",
|
|
48
|
+
"prepublishOnly": "bun run check",
|
|
49
|
+
"release:dry": "bun publish --dry-run"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/bun": "latest",
|
|
53
|
+
"iti": "^0.8.0",
|
|
54
|
+
"lefthook": "^2.1.10",
|
|
55
|
+
"oxfmt": "^0.63.0",
|
|
56
|
+
"oxlint": "^1.78.0",
|
|
57
|
+
"oxlint-tsgolint": "^7.0.2001",
|
|
58
|
+
"publint": "^0.3.23",
|
|
59
|
+
"tsdown": "^0.22.14"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"better-result": "^3.0.0",
|
|
63
|
+
"iti": "^0.8.0",
|
|
64
|
+
"typescript": "^7.0.2"
|
|
65
|
+
},
|
|
66
|
+
"peerDependenciesMeta": {
|
|
67
|
+
"iti": {
|
|
68
|
+
"optional": true
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"optionalDependencies": {
|
|
72
|
+
"iti": "^0.8.0"
|
|
73
|
+
}
|
|
74
|
+
}
|