@effectionx/effect-ts 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/LICENSE +9 -0
- package/README.md +266 -0
- package/dist/effect-runtime.d.ts +96 -0
- package/dist/effect-runtime.d.ts.map +1 -0
- package/dist/effect-runtime.js +77 -0
- package/dist/effection-runtime.d.ts +100 -0
- package/dist/effection-runtime.d.ts.map +1 -0
- package/dist/effection-runtime.js +82 -0
- package/dist/mod.d.ts +5 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +4 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/effect-runtime.ts +136 -0
- package/effect.test.ts +479 -0
- package/effection-runtime.ts +126 -0
- package/mod.ts +7 -0
- package/package.json +34 -0
- package/tsconfig.json +10 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2024 The Frontside Software, Inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Effect-TS
|
|
2
|
+
|
|
3
|
+
Bidirectional interop between [Effect-TS](https://effect.website/) and [Effection](https://frontside.com/effection).
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
Effect and Effection are both powerful libraries for managing side effects in TypeScript,
|
|
8
|
+
but they have different philosophies and strengths:
|
|
9
|
+
|
|
10
|
+
| Feature | Effect | Effection |
|
|
11
|
+
|---------|--------|-----------|
|
|
12
|
+
| **Concurrency Model** | Fiber-based with supervision | Structured concurrency with scopes |
|
|
13
|
+
| **Error Handling** | Type-safe errors in signature | JavaScript throw/catch |
|
|
14
|
+
| **Dependencies** | Context/Layer system | Context API |
|
|
15
|
+
| **Syntax** | Generator or pipe-based | Generator-based |
|
|
16
|
+
| **Resource Management** | Scope with finalizers | Automatic cleanup on scope exit |
|
|
17
|
+
|
|
18
|
+
This package lets you use both together:
|
|
19
|
+
|
|
20
|
+
- **Use Effect inside Effection** when you want Effect's type-safe error handling
|
|
21
|
+
or need to use Effect-based libraries within Effection's structured concurrency
|
|
22
|
+
- **Use Effection inside Effect** when you want Effection's simple generator syntax
|
|
23
|
+
or need to use Effection-based libraries within an Effect application
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @effectionx/effect-ts
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Peer dependencies:** Both `effect` (^3) and `effection` (^3 || ^4) must be installed.
|
|
32
|
+
|
|
33
|
+
## Effection Host - Effect Guest
|
|
34
|
+
|
|
35
|
+
Use `makeEffectRuntime()` to run Effect programs inside Effection operations.
|
|
36
|
+
|
|
37
|
+
### Basic Usage
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { main } from "effection";
|
|
41
|
+
import { Effect } from "effect";
|
|
42
|
+
import { makeEffectRuntime } from "@effectionx/effect-ts";
|
|
43
|
+
|
|
44
|
+
await main(function* () {
|
|
45
|
+
// Create the Effect runtime (automatically disposed when scope ends)
|
|
46
|
+
const runtime = yield* makeEffectRuntime();
|
|
47
|
+
|
|
48
|
+
// Run Effect programs
|
|
49
|
+
const result = yield* runtime.run(
|
|
50
|
+
Effect.succeed(42).pipe(Effect.map(n => n * 2))
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
console.log(result); // 84
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Error Handling
|
|
58
|
+
|
|
59
|
+
Effect failures are thrown as JavaScript errors when using `run()`:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { main } from "effection";
|
|
63
|
+
import { Effect } from "effect";
|
|
64
|
+
import { makeEffectRuntime } from "@effectionx/effect-ts";
|
|
65
|
+
|
|
66
|
+
await main(function* () {
|
|
67
|
+
const runtime = yield* makeEffectRuntime();
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
yield* runtime.run(Effect.fail(new Error("boom")));
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.log(error.message); // "boom"
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For type-safe error handling, use `runExit()` which returns an `Exit<A, E>`:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { main } from "effection";
|
|
81
|
+
import { Effect, Exit } from "effect";
|
|
82
|
+
import { makeEffectRuntime } from "@effectionx/effect-ts";
|
|
83
|
+
|
|
84
|
+
await main(function* () {
|
|
85
|
+
const runtime = yield* makeEffectRuntime();
|
|
86
|
+
|
|
87
|
+
const exit = yield* runtime.runExit(Effect.fail(new Error("boom")));
|
|
88
|
+
|
|
89
|
+
if (Exit.isFailure(exit)) {
|
|
90
|
+
// Access the full Cause<E> with error details
|
|
91
|
+
console.log(exit.cause);
|
|
92
|
+
} else {
|
|
93
|
+
// Access the success value
|
|
94
|
+
console.log(exit.value);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### With Effect Services
|
|
100
|
+
|
|
101
|
+
You can provide an Effect Layer to pre-configure services:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { main } from "effection";
|
|
105
|
+
import { Effect, Context, Layer } from "effect";
|
|
106
|
+
import { makeEffectRuntime } from "@effectionx/effect-ts";
|
|
107
|
+
|
|
108
|
+
// Define a service
|
|
109
|
+
class Logger extends Context.Tag("Logger")<Logger, {
|
|
110
|
+
log: (msg: string) => Effect.Effect<void>
|
|
111
|
+
}>() {}
|
|
112
|
+
|
|
113
|
+
const LoggerLive = Layer.succeed(Logger, {
|
|
114
|
+
log: (msg) => Effect.sync(() => console.log(msg))
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await main(function* () {
|
|
118
|
+
// Provide layer to the runtime
|
|
119
|
+
const runtime = yield* makeEffectRuntime(LoggerLive);
|
|
120
|
+
|
|
121
|
+
// Effects can now use Logger without explicit provide
|
|
122
|
+
yield* runtime.run(
|
|
123
|
+
Effect.gen(function* () {
|
|
124
|
+
const logger = yield* Logger;
|
|
125
|
+
yield* logger.log("Hello!");
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Compose multiple layers using Effect's primitives:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const AppLayer = Layer.mergeAll(DatabaseLive, LoggerLive, CacheLive);
|
|
135
|
+
const runtime = yield* makeEffectRuntime(AppLayer);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Cancellation
|
|
139
|
+
|
|
140
|
+
When an Effection scope is halted, any running Effect programs are interrupted:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { main, spawn, sleep } from "effection";
|
|
144
|
+
import { Effect } from "effect";
|
|
145
|
+
import { makeEffectRuntime } from "@effectionx/effect-ts";
|
|
146
|
+
|
|
147
|
+
await main(function* () {
|
|
148
|
+
const runtime = yield* makeEffectRuntime();
|
|
149
|
+
|
|
150
|
+
const task = yield* spawn(function* () {
|
|
151
|
+
yield* runtime.run(
|
|
152
|
+
Effect.gen(function* () {
|
|
153
|
+
yield* Effect.addFinalizer(() => Effect.log("Effect interrupted!"));
|
|
154
|
+
yield* Effect.sleep("10 seconds");
|
|
155
|
+
}).pipe(Effect.scoped)
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
yield* sleep(100);
|
|
160
|
+
// Task is automatically halted when main scope ends
|
|
161
|
+
// Effect finalizer runs: "Effect interrupted!"
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Effect Host - Effection Guest
|
|
166
|
+
|
|
167
|
+
Use `makeEffectionRuntime()` to run Effection operations inside Effect programs.
|
|
168
|
+
|
|
169
|
+
### Basic Usage
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { Effect } from "effect";
|
|
173
|
+
import { sleep } from "effection";
|
|
174
|
+
import { makeEffectionRuntime, EffectionRuntime } from "@effectionx/effect-ts";
|
|
175
|
+
|
|
176
|
+
const program = Effect.gen(function* () {
|
|
177
|
+
const runtime = yield* EffectionRuntime;
|
|
178
|
+
|
|
179
|
+
const result = yield* runtime.run(function* () {
|
|
180
|
+
yield* sleep(100);
|
|
181
|
+
return "hello from effection";
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return result.toUpperCase();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const result = await Effect.runPromise(
|
|
188
|
+
program.pipe(
|
|
189
|
+
Effect.provide(makeEffectionRuntime()),
|
|
190
|
+
Effect.scoped
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
console.log(result); // "HELLO FROM EFFECTION"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Error Handling
|
|
198
|
+
|
|
199
|
+
Errors thrown in Effection operations become `UnknownException` in Effect:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { Effect, Exit } from "effect";
|
|
203
|
+
import { makeEffectionRuntime, EffectionRuntime } from "@effectionx/effect-ts";
|
|
204
|
+
|
|
205
|
+
const program = Effect.gen(function* () {
|
|
206
|
+
const runtime = yield* EffectionRuntime;
|
|
207
|
+
|
|
208
|
+
return yield* runtime.run(function* () {
|
|
209
|
+
throw new Error("boom");
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const exit = await Effect.runPromiseExit(
|
|
214
|
+
program.pipe(
|
|
215
|
+
Effect.provide(makeEffectionRuntime()),
|
|
216
|
+
Effect.scoped
|
|
217
|
+
)
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
// exit is Exit.Failure with UnknownException containing the error
|
|
221
|
+
if (Exit.isFailure(exit)) {
|
|
222
|
+
console.log("Failed:", exit.cause);
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Cancellation
|
|
227
|
+
|
|
228
|
+
When the Effect scope ends or is interrupted, the Effection scope is closed:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { Effect, Fiber } from "effect";
|
|
232
|
+
import { suspend } from "effection";
|
|
233
|
+
import { makeEffectionRuntime, EffectionRuntime } from "@effectionx/effect-ts";
|
|
234
|
+
|
|
235
|
+
const program = Effect.gen(function* () {
|
|
236
|
+
const runtime = yield* EffectionRuntime;
|
|
237
|
+
|
|
238
|
+
const fiber = yield* runtime.run(function* () {
|
|
239
|
+
try {
|
|
240
|
+
yield* suspend();
|
|
241
|
+
} finally {
|
|
242
|
+
console.log("Effection cleanup!");
|
|
243
|
+
}
|
|
244
|
+
}).pipe(Effect.fork);
|
|
245
|
+
|
|
246
|
+
yield* Effect.sleep("100 millis");
|
|
247
|
+
yield* Fiber.interrupt(fiber);
|
|
248
|
+
// Logs: "Effection cleanup!"
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
await Effect.runPromise(
|
|
252
|
+
program.pipe(
|
|
253
|
+
Effect.provide(makeEffectionRuntime()),
|
|
254
|
+
Effect.scoped
|
|
255
|
+
)
|
|
256
|
+
);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Comparison
|
|
260
|
+
|
|
261
|
+
| Aspect | Effection Host | | Effect Host |
|
|
262
|
+
|--------|----------------|---|-------------|
|
|
263
|
+
| **Method** | `EffectRuntime.run()` | `EffectRuntime.runExit()` | `EffectionRuntime.run()` |
|
|
264
|
+
| **Error Handling** | Throws JS error | Returns `Exit<A, E>` | Returns `UnknownException` |
|
|
265
|
+
| **Use When** | Simple cases | Need typed errors | Using Effection in Effect |
|
|
266
|
+
| **Cancellation** | Effection halt → Effect interrupt | Same | Effect interrupt → Effection halt |
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type Effect, type Exit, Layer } from "effect";
|
|
2
|
+
import { type Operation } from "effection";
|
|
3
|
+
/**
|
|
4
|
+
* A runtime for executing Effect programs inside Effection operations.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam R - The services/context provided by this runtime (from the layer)
|
|
7
|
+
*/
|
|
8
|
+
export interface EffectRuntime<R = never> {
|
|
9
|
+
/**
|
|
10
|
+
* Run an Effect program and return its result as an Effection Operation.
|
|
11
|
+
*
|
|
12
|
+
* Effect failures will be thrown as JavaScript errors.
|
|
13
|
+
*
|
|
14
|
+
* @param effect - The Effect program to run
|
|
15
|
+
* @returns An Operation that yields the Effect's success value
|
|
16
|
+
* @throws The Effect's error `E` if it fails
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const runtime = yield* makeEffectRuntime();
|
|
21
|
+
* const result = yield* runtime.run(Effect.succeed(42));
|
|
22
|
+
* // result = 42
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
run<A, E>(effect: Effect.Effect<A, E, R>): Operation<A>;
|
|
26
|
+
/**
|
|
27
|
+
* Run an Effect program and return its Exit (success or failure).
|
|
28
|
+
*
|
|
29
|
+
* Unlike `run()`, this does not throw on failure. Instead, it returns
|
|
30
|
+
* an `Exit<A, E>` that you can inspect to determine success or failure.
|
|
31
|
+
* This preserves Effect's full error model including the Cause.
|
|
32
|
+
*
|
|
33
|
+
* @param effect - The Effect program to run
|
|
34
|
+
* @returns An Operation that yields the Effect's Exit
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* const runtime = yield* makeEffectRuntime();
|
|
39
|
+
* const exit = yield* runtime.runExit(Effect.fail(new Error("boom")));
|
|
40
|
+
* if (Exit.isFailure(exit)) {
|
|
41
|
+
* console.log(exit.cause); // Full Cause<E> with error details
|
|
42
|
+
* } else {
|
|
43
|
+
* console.log(exit.value); // Success value
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
runExit<A, E>(effect: Effect.Effect<A, E, R>): Operation<Exit.Exit<A, E>>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create an EffectRuntime resource that manages an Effect ManagedRuntime.
|
|
51
|
+
*
|
|
52
|
+
* The ManagedRuntime is automatically disposed when the Effection scope ends,
|
|
53
|
+
* ensuring proper cleanup of Effect resources.
|
|
54
|
+
*
|
|
55
|
+
* @param layer - Optional Effect Layer to provide services. Defaults to `Layer.empty`.
|
|
56
|
+
* Users can compose multiple layers using Effect's `Layer.merge()`,
|
|
57
|
+
* `Layer.mergeAll()`, or `Layer.provide()` before passing.
|
|
58
|
+
* @returns An Operation that yields the EffectRuntime
|
|
59
|
+
*
|
|
60
|
+
* @example Basic usage
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { run } from "effection";
|
|
63
|
+
* import { Effect } from "effect";
|
|
64
|
+
* import { makeEffectRuntime } from "@effectionx/effect";
|
|
65
|
+
*
|
|
66
|
+
* await run(function* () {
|
|
67
|
+
* const runtime = yield* makeEffectRuntime();
|
|
68
|
+
* const result = yield* runtime.run(Effect.succeed(42));
|
|
69
|
+
* console.log(result); // 42
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example With services
|
|
74
|
+
* ```ts
|
|
75
|
+
* import { Layer, Context, Effect } from "effect";
|
|
76
|
+
*
|
|
77
|
+
* class Logger extends Context.Tag("Logger")<Logger, { log: (msg: string) => Effect.Effect<void> }>() {}
|
|
78
|
+
* const LoggerLive = Layer.succeed(Logger, { log: (msg) => Effect.log(msg) });
|
|
79
|
+
*
|
|
80
|
+
* await run(function* () {
|
|
81
|
+
* const runtime = yield* makeEffectRuntime(LoggerLive);
|
|
82
|
+
* yield* runtime.run(Effect.gen(function* () {
|
|
83
|
+
* const logger = yield* Logger;
|
|
84
|
+
* yield* logger.log("Hello!");
|
|
85
|
+
* }));
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @example Composing multiple layers
|
|
90
|
+
* ```ts
|
|
91
|
+
* const AppLayer = Layer.mergeAll(DatabaseLive, LoggerLive, CacheLive);
|
|
92
|
+
* const runtime = yield* makeEffectRuntime(AppLayer);
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function makeEffectRuntime<R = never>(layer?: Layer.Layer<R, never, never>): Operation<EffectRuntime<R>>;
|
|
96
|
+
//# sourceMappingURL=effect-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effect-runtime.d.ts","sourceRoot":"","sources":["../effect-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE,KAAK,EAAkB,MAAM,QAAQ,CAAC;AACvE,OAAO,EAAE,KAAK,SAAS,EAA0B,MAAM,WAAW,CAAC;AAEnE;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,KAAK;IACtC;;;;;;;;;;;;;;;OAeG;IACH,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IAExD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,KAAK,EACzC,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,GACnC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAoC7B"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Layer, ManagedRuntime } from "effect";
|
|
2
|
+
import { action, call, resource } from "effection";
|
|
3
|
+
/**
|
|
4
|
+
* Create an EffectRuntime resource that manages an Effect ManagedRuntime.
|
|
5
|
+
*
|
|
6
|
+
* The ManagedRuntime is automatically disposed when the Effection scope ends,
|
|
7
|
+
* ensuring proper cleanup of Effect resources.
|
|
8
|
+
*
|
|
9
|
+
* @param layer - Optional Effect Layer to provide services. Defaults to `Layer.empty`.
|
|
10
|
+
* Users can compose multiple layers using Effect's `Layer.merge()`,
|
|
11
|
+
* `Layer.mergeAll()`, or `Layer.provide()` before passing.
|
|
12
|
+
* @returns An Operation that yields the EffectRuntime
|
|
13
|
+
*
|
|
14
|
+
* @example Basic usage
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { run } from "effection";
|
|
17
|
+
* import { Effect } from "effect";
|
|
18
|
+
* import { makeEffectRuntime } from "@effectionx/effect";
|
|
19
|
+
*
|
|
20
|
+
* await run(function* () {
|
|
21
|
+
* const runtime = yield* makeEffectRuntime();
|
|
22
|
+
* const result = yield* runtime.run(Effect.succeed(42));
|
|
23
|
+
* console.log(result); // 42
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example With services
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { Layer, Context, Effect } from "effect";
|
|
30
|
+
*
|
|
31
|
+
* class Logger extends Context.Tag("Logger")<Logger, { log: (msg: string) => Effect.Effect<void> }>() {}
|
|
32
|
+
* const LoggerLive = Layer.succeed(Logger, { log: (msg) => Effect.log(msg) });
|
|
33
|
+
*
|
|
34
|
+
* await run(function* () {
|
|
35
|
+
* const runtime = yield* makeEffectRuntime(LoggerLive);
|
|
36
|
+
* yield* runtime.run(Effect.gen(function* () {
|
|
37
|
+
* const logger = yield* Logger;
|
|
38
|
+
* yield* logger.log("Hello!");
|
|
39
|
+
* }));
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example Composing multiple layers
|
|
44
|
+
* ```ts
|
|
45
|
+
* const AppLayer = Layer.mergeAll(DatabaseLive, LoggerLive, CacheLive);
|
|
46
|
+
* const runtime = yield* makeEffectRuntime(AppLayer);
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function makeEffectRuntime(layer) {
|
|
50
|
+
return resource(function* (provide) {
|
|
51
|
+
const managedRuntime = ManagedRuntime.make(layer ?? Layer.empty);
|
|
52
|
+
const run = (effect) => {
|
|
53
|
+
return action((resolve, reject) => {
|
|
54
|
+
const controller = new AbortController();
|
|
55
|
+
managedRuntime
|
|
56
|
+
.runPromise(effect, { signal: controller.signal })
|
|
57
|
+
.then(resolve, reject);
|
|
58
|
+
return () => controller.abort();
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const runExit = (effect) => {
|
|
62
|
+
return action((resolve, reject) => {
|
|
63
|
+
const controller = new AbortController();
|
|
64
|
+
managedRuntime
|
|
65
|
+
.runPromiseExit(effect, { signal: controller.signal })
|
|
66
|
+
.then(resolve, reject);
|
|
67
|
+
return () => controller.abort();
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
try {
|
|
71
|
+
yield* provide({ run, runExit });
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
yield* call(() => managedRuntime.dispose());
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import type { UnknownException } from "effect/Cause";
|
|
3
|
+
import { type Operation, type Scope } from "effection";
|
|
4
|
+
/**
|
|
5
|
+
* A runtime for executing Effection operations inside Effect programs.
|
|
6
|
+
*/
|
|
7
|
+
export interface EffectionRuntime {
|
|
8
|
+
/**
|
|
9
|
+
* Run an Effection operation and return its result as an Effect.
|
|
10
|
+
*
|
|
11
|
+
* Errors thrown in the operation become `UnknownException` in Effect.
|
|
12
|
+
* The Effection scope is automatically cleaned up when the Effect completes
|
|
13
|
+
* or is interrupted.
|
|
14
|
+
*
|
|
15
|
+
* @param operation - The Effection operation (generator function) to run
|
|
16
|
+
* @returns An Effect that yields the operation's result
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const program = Effect.gen(function* () {
|
|
21
|
+
* const runtime = yield* EffectionRuntime;
|
|
22
|
+
* return yield* runtime.run(function* () {
|
|
23
|
+
* yield* sleep(100);
|
|
24
|
+
* return "hello";
|
|
25
|
+
* });
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
run<T>(operation: () => Operation<T>): Effect.Effect<T, UnknownException>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Effect Context Tag for accessing the EffectionRuntime.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const program = Effect.gen(function* () {
|
|
37
|
+
* const runtime = yield* EffectionRuntime;
|
|
38
|
+
* // use runtime.run(...)
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare const EffectionRuntime: Context.Tag<EffectionRuntime, EffectionRuntime>;
|
|
43
|
+
/**
|
|
44
|
+
* Create an Effect Layer that provides an EffectionRuntime.
|
|
45
|
+
*
|
|
46
|
+
* The Effection scope is automatically closed when the Effect scope ends,
|
|
47
|
+
* ensuring proper cleanup of Effection resources.
|
|
48
|
+
*
|
|
49
|
+
* @param parent - Optional parent Effection scope. If provided, the runtime's
|
|
50
|
+
* scope will inherit all contexts from the parent scope.
|
|
51
|
+
* @returns An Effect Layer providing EffectionRuntime
|
|
52
|
+
*
|
|
53
|
+
* @example Basic usage
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { Effect } from "effect";
|
|
56
|
+
* import { sleep } from "effection";
|
|
57
|
+
* import { makeEffectionRuntime, EffectionRuntime } from "@effectionx/effect";
|
|
58
|
+
*
|
|
59
|
+
* const program = Effect.gen(function* () {
|
|
60
|
+
* const runtime = yield* EffectionRuntime;
|
|
61
|
+
* const result = yield* runtime.run(function* () {
|
|
62
|
+
* yield* sleep(100);
|
|
63
|
+
* return "hello from effection";
|
|
64
|
+
* });
|
|
65
|
+
* return result;
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* await Effect.runPromise(
|
|
69
|
+
* program.pipe(
|
|
70
|
+
* Effect.provide(makeEffectionRuntime()),
|
|
71
|
+
* Effect.scoped
|
|
72
|
+
* )
|
|
73
|
+
* );
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @example With parent scope (to inherit Effection contexts)
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { Effect } from "effect";
|
|
79
|
+
* import { useScope } from "effection";
|
|
80
|
+
* import { makeEffectionRuntime, EffectionRuntime } from "@effectionx/effect";
|
|
81
|
+
*
|
|
82
|
+
* function* myOperation() {
|
|
83
|
+
* const scope = yield* useScope();
|
|
84
|
+
* const result = yield* call(() =>
|
|
85
|
+
* Effect.runPromise(
|
|
86
|
+
* Effect.gen(function* () {
|
|
87
|
+
* const runtime = yield* EffectionRuntime;
|
|
88
|
+
* return yield* runtime.run(function* () {
|
|
89
|
+
* // Can access Effection contexts from parent scope
|
|
90
|
+
* return "hello";
|
|
91
|
+
* });
|
|
92
|
+
* }).pipe(Effect.provide(makeEffectionRuntime(scope)), Effect.scoped)
|
|
93
|
+
* )
|
|
94
|
+
* );
|
|
95
|
+
* return result;
|
|
96
|
+
* }
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export declare function makeEffectionRuntime(parent?: Scope): Layer.Layer<EffectionRuntime>;
|
|
100
|
+
//# sourceMappingURL=effection-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effection-runtime.d.ts","sourceRoot":"","sources":["../effection-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAe,MAAM,WAAW,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,iDAC6B,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,CAAC,EAAE,KAAK,GACb,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAqB/B"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { createScope } from "effection";
|
|
3
|
+
/**
|
|
4
|
+
* Effect Context Tag for accessing the EffectionRuntime.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const program = Effect.gen(function* () {
|
|
9
|
+
* const runtime = yield* EffectionRuntime;
|
|
10
|
+
* // use runtime.run(...)
|
|
11
|
+
* });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export const EffectionRuntime = Context.GenericTag("EffectionRuntime");
|
|
15
|
+
/**
|
|
16
|
+
* Create an Effect Layer that provides an EffectionRuntime.
|
|
17
|
+
*
|
|
18
|
+
* The Effection scope is automatically closed when the Effect scope ends,
|
|
19
|
+
* ensuring proper cleanup of Effection resources.
|
|
20
|
+
*
|
|
21
|
+
* @param parent - Optional parent Effection scope. If provided, the runtime's
|
|
22
|
+
* scope will inherit all contexts from the parent scope.
|
|
23
|
+
* @returns An Effect Layer providing EffectionRuntime
|
|
24
|
+
*
|
|
25
|
+
* @example Basic usage
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { Effect } from "effect";
|
|
28
|
+
* import { sleep } from "effection";
|
|
29
|
+
* import { makeEffectionRuntime, EffectionRuntime } from "@effectionx/effect";
|
|
30
|
+
*
|
|
31
|
+
* const program = Effect.gen(function* () {
|
|
32
|
+
* const runtime = yield* EffectionRuntime;
|
|
33
|
+
* const result = yield* runtime.run(function* () {
|
|
34
|
+
* yield* sleep(100);
|
|
35
|
+
* return "hello from effection";
|
|
36
|
+
* });
|
|
37
|
+
* return result;
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* await Effect.runPromise(
|
|
41
|
+
* program.pipe(
|
|
42
|
+
* Effect.provide(makeEffectionRuntime()),
|
|
43
|
+
* Effect.scoped
|
|
44
|
+
* )
|
|
45
|
+
* );
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @example With parent scope (to inherit Effection contexts)
|
|
49
|
+
* ```ts
|
|
50
|
+
* import { Effect } from "effect";
|
|
51
|
+
* import { useScope } from "effection";
|
|
52
|
+
* import { makeEffectionRuntime, EffectionRuntime } from "@effectionx/effect";
|
|
53
|
+
*
|
|
54
|
+
* function* myOperation() {
|
|
55
|
+
* const scope = yield* useScope();
|
|
56
|
+
* const result = yield* call(() =>
|
|
57
|
+
* Effect.runPromise(
|
|
58
|
+
* Effect.gen(function* () {
|
|
59
|
+
* const runtime = yield* EffectionRuntime;
|
|
60
|
+
* return yield* runtime.run(function* () {
|
|
61
|
+
* // Can access Effection contexts from parent scope
|
|
62
|
+
* return "hello";
|
|
63
|
+
* });
|
|
64
|
+
* }).pipe(Effect.provide(makeEffectionRuntime(scope)), Effect.scoped)
|
|
65
|
+
* )
|
|
66
|
+
* );
|
|
67
|
+
* return result;
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export function makeEffectionRuntime(parent) {
|
|
72
|
+
return Layer.scoped(EffectionRuntime, Effect.gen(function* () {
|
|
73
|
+
const [scope, close] = createScope(parent);
|
|
74
|
+
const run = (operation) => {
|
|
75
|
+
return Effect.tryPromise(() => scope.run(operation));
|
|
76
|
+
};
|
|
77
|
+
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
78
|
+
yield* Effect.tryPromise(() => close()).pipe(Effect.exit);
|
|
79
|
+
}));
|
|
80
|
+
return { run };
|
|
81
|
+
}));
|
|
82
|
+
}
|
package/dist/mod.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { makeEffectRuntime } from "./effect-runtime.ts";
|
|
2
|
+
export type { EffectRuntime } from "./effect-runtime.ts";
|
|
3
|
+
export { EffectionRuntime, makeEffectionRuntime } from "./effection-runtime.ts";
|
|
4
|
+
export type { EffectionRuntime as EffectionRuntimeType } from "./effection-runtime.ts";
|
|
5
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAChF,YAAY,EAAE,gBAAgB,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/mod.js
ADDED