@akterjs/cli 0.0.0-stage → 0.1.1
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 +201 -0
- package/README.md +47 -2
- package/dist/Argument-D81dqizB.mjs +57 -0
- package/dist/Array-B4kuTQeW.mjs +2323 -0
- package/dist/Boolean-kILIvmhE.mjs +26 -0
- package/dist/ChildProcess-BCIwxPGc.mjs +577 -0
- package/dist/CliError-Cf5xqkkj.mjs +633 -0
- package/dist/Command-Dzq9KM-h.mjs +5135 -0
- package/dist/Config-B6OND9WR.mjs +854 -0
- package/dist/Console-BKHUkH6a.mjs +141 -0
- package/dist/Data-CeCULr0-.mjs +251 -0
- package/dist/Digest-CBd3uv5B.mjs +348 -0
- package/dist/Duration-EH9Vy4U6.mjs +791 -0
- package/dist/Effect-CKFpqbmw.mjs +12911 -0
- package/dist/FileSystem-4pPcEb_i.mjs +1366 -0
- package/dist/HttpClientError-BUeMWx70.mjs +303 -0
- package/dist/HttpClientResponse-D5ZKICVV.mjs +855 -0
- package/dist/Match-CGPLZYrV.mjs +434 -0
- package/dist/MutableHashMap-f8nW8AJR.mjs +376 -0
- package/dist/MutableHashSet-_HI572Ya.mjs +155 -0
- package/dist/MutableRef-D4Qv592C.mjs +107 -0
- package/dist/Number-BvkRkfc-.mjs +137 -0
- package/dist/Option-DkxSnBhp.mjs +3967 -0
- package/dist/Order-C1JC991I.mjs +481 -0
- package/dist/Path-Djdv_eF5.mjs +130 -0
- package/dist/Queue-CO5UQ6os.mjs +1394 -0
- package/dist/Redacted-RkJrG5Me.mjs +160 -0
- package/dist/Ref-BIPOxJNH.mjs +267 -0
- package/dist/Runtime-BInIAwK3.mjs +297 -0
- package/dist/Schema-MiKt5K1v.mjs +10999 -0
- package/dist/SecureRandom-B_U4ysO3.mjs +279 -0
- package/dist/Semaphore-DOVJ4ZGS.mjs +177 -0
- package/dist/Stdio-KylmOBX_.mjs +74 -0
- package/dist/Stream-bK9QIETN.mjs +4012 -0
- package/dist/String-q5dcsVe4.mjs +351 -0
- package/dist/TerminalCapabilities-w_44MAca.mjs +1769 -0
- package/dist/WebCrypto-ZI_GNVlw.mjs +33 -0
- package/dist/actor-call.service-CGM_vD9u.mjs +1269 -0
- package/dist/actor-client.adapter-Coem7gF1.mjs +3691 -0
- package/dist/akter-program.config-D8yb9xQP.mjs +7034 -0
- package/dist/akter.cli.mjs +5 -0
- package/dist/app-cli.config.mjs +49 -0
- package/dist/app-entry.pure.mjs +31 -0
- package/dist/archive/akter-archive.schema.mjs +65 -0
- package/dist/archive/akter-archive.service.mjs +309 -0
- package/dist/archive/archive-cli.config.mjs +45 -0
- package/dist/archive/archive-invalid.errors.mjs +5 -0
- package/dist/archive/archived-lineage.pure.mjs +13 -0
- package/dist/archive/tar.pure.mjs +291 -0
- package/dist/archive/zstd.adapter.mjs +46 -0
- package/dist/bench/bench-report.pure.mjs +120 -0
- package/dist/bench/bench-runner.pure.mjs +89 -0
- package/dist/bench/bench.service.mjs +301 -0
- package/dist/cli-runtime.layer-BqK2mmVL.mjs +1191 -0
- package/dist/cli-runtime.layer.mjs +1 -0
- package/dist/cloud-client.adapter-Cy1BjDbG.mjs +3186 -0
- package/dist/cloud-client.adapter-DOVMoYAj.mjs +304 -0
- package/dist/command/akter-commands.config.mjs +32 -0
- package/dist/command/akter-program.config.mjs +1 -0
- package/dist/credentials/credentials.service.mjs +226 -0
- package/dist/credentials/keychain.adapter.mjs +92 -0
- package/dist/credentials/login.config.mjs +251 -0
- package/dist/deploy/auth/api-attempt.service.mjs +30 -0
- package/dist/deploy/auth/token-refresh.errors.mjs +4 -0
- package/dist/deploy/build-bundle.adapter.mjs +133 -0
- package/dist/deploy/build-manifest.pure.mjs +44 -0
- package/dist/deploy/cloud-client.adapter.mjs +1 -0
- package/dist/deploy/cloud-url.pure.mjs +17 -0
- package/dist/deploy/deploy-failed.errors.mjs +17 -0
- package/dist/deploy/deploy.service.mjs +156 -0
- package/dist/deploy/request-failure.pure.mjs +87 -0
- package/dist/dev-runner.pure.mjs +30 -0
- package/dist/docs/doc-topics.pure.mjs +21 -0
- package/dist/docs/docs.config.mjs +1 -0
- package/dist/docs.config-D2amjsje.mjs +190 -0
- package/dist/docs.config-D3zzLzLR.mjs +190 -0
- package/dist/export/actor-export.service.mjs +267 -0
- package/dist/export/export-failed.errors.mjs +4 -0
- package/dist/export/export-failure.pure.mjs +18 -0
- package/dist/export/ordered-lines.pure.mjs +43 -0
- package/dist/export/ranged-download.pure.mjs +66 -0
- package/dist/export/snapshot-download.pure.mjs +86 -0
- package/dist/history-runner.pure.mjs +186 -0
- package/dist/history-since.pure.mjs +17 -0
- package/dist/hosted-runtime.layer-Cx3IPsEd.mjs +16358 -0
- package/dist/local/environment-cli.config.mjs +90 -0
- package/dist/local/environment-runner.pure.mjs +75 -0
- package/dist/local/local-cli.config.mjs +159 -0
- package/dist/logs/log-follow.config.mjs +21 -0
- package/dist/logs/log-follow.service.mjs +87 -0
- package/dist/logs/log-lines.pure.mjs +44 -0
- package/dist/migrate-runner.pure.mjs +28 -0
- package/dist/operation-input.pure-CS9iUhqK.mjs +1383 -0
- package/dist/operation-registry.pure-DVC8EDgj.mjs +283 -0
- package/dist/registry/actor-call.service.mjs +1 -0
- package/dist/registry/actor-commands.config.mjs +127 -0
- package/dist/registry/actor-refusal.pure.mjs +34 -0
- package/dist/registry/approval.service.mjs +80 -0
- package/dist/registry/cloud-commands.config.mjs +151 -0
- package/dist/registry/cloud-served.pure.mjs +31 -0
- package/dist/registry/operation-call.service.mjs +262 -0
- package/dist/registry/operation-input.pure.mjs +1 -0
- package/dist/registry/operation-output.pure.mjs +91 -0
- package/dist/registry/served-command.config.mjs +57 -0
- package/dist/runner-exited.errors.mjs +10 -0
- package/dist/terminal/cli-exit.errors.mjs +38 -0
- package/dist/terminal/cli-failure.pure.mjs +31 -0
- package/dist/terminal/confirm.adapter.mjs +27 -0
- package/package.json +61 -3
|
@@ -0,0 +1,4012 @@
|
|
|
1
|
+
import { $ as isFailure, F as of, b as isArray, ct as normalizeNonEmpty, j as map$2, o as empty$2, q as headUnsafe, st as normalize, tt as isSuccess, w as isReadonlyArrayNonEmpty, x as isArrayNonEmpty } from "./Array-B4kuTQeW.mjs";
|
|
2
|
+
import { $t as suspend$2, A as ensuring$2, Ai as getCurrentFiber, At as provideContext$2, B as flatMap$2, Br as done, Bt as runPromise, C as catch_, Ci as fiberInterruptAll, Ct as onExitPrimitive, D as delay, Di as findRecoverableError, Dr as isFailure$1, Ei as fiberRunIn, Fn as catchDone, H as flatten$1, In as doneExitFromCause, J as forkChild, Kt as scopedWith, Ln as filterDone, M as fail$2, Mr as void_$1, N as failCause$1, Ni as makeLatch, Nr as Done, Nt as raceFirst, O as die$3, Or as isSuccess$1, Ot as promise, Pi as makeLatchUnsafe, Qn as close, Rn as isDoneCause, Rt as runFork, Si as fiberInterrupt, Sn as yieldNow, Sr as failCause$2, St as onExit$1, T as context, Ti as fiberJoin, Tr as interrupt$1, V as flatMapEager, W as fnUntraced, Wr as fromReasons, Xn as addFinalizer, Xt as succeed$2, Y as forkIn, Yr as isCause, Zn as addFinalizerExit, Zr as isDone, bt as never, ci as squash, cn as tryPromise, d as asVoid, dt as map$3, en as sync, et as gen, gn as void_, ht as mapError$2, ir as provide, j as exit, jn as toStepWithMetadata, jt as provideService$2, l as as, m as catchCause, nt as ignoreCause, o as andThen, q as forever, rn as tapError$2, rr as makeUnsafe$1, tn as tap$1, tr as forkUnsafe, vn as whileLoop, vt as matchCauseEffect, wi as fiberInterruptAs, wn as CurrentMetadata, xi as fiberAwait, xr as fail$3, xt as onError, yr as asVoidAll, zn as matchEffect, zr as die$2, zt as runForkWith } from "./Effect-CKFpqbmw.mjs";
|
|
3
|
+
import { On as pipeArguments, S as none, Sn as identity, bn as dual, gn as constTrue, h as isNone, qt as hasProperty, vn as constVoid, w as some, yn as constant } from "./Option-DkxSnBhp.mjs";
|
|
4
|
+
import { t as make$2 } from "./MutableRef-D4Qv592C.mjs";
|
|
5
|
+
import { _ as takeAll, c as make$3, g as take$1, l as offer, o as failCause$3, p as shutdown, s as failCauseUnsafe, t as bounded, u as offerAll } from "./Queue-CO5UQ6os.mjs";
|
|
6
|
+
import { n as makeUnsafe$2 } from "./Semaphore-DOVJ4ZGS.mjs";
|
|
7
|
+
// ../../node_modules/.pnpm/effect@4.1.2_patch_hash=0e61176203bb1256381ce1c5b488d7951983ce38ff698647d3f4617383abbf07/node_modules/effect/dist/Fiber.js
|
|
8
|
+
const await_ = fiberAwait;
|
|
9
|
+
/**
|
|
10
|
+
* Joins a fiber, blocking until it completes. If the fiber succeeds,
|
|
11
|
+
* returns its value. If it fails, the error is propagated.
|
|
12
|
+
*
|
|
13
|
+
* **When to use**
|
|
14
|
+
*
|
|
15
|
+
* Use when you need a forked fiber's failure to fail the current Effect because
|
|
16
|
+
* that fiber is part of the current workflow.
|
|
17
|
+
*
|
|
18
|
+
* **Gotchas**
|
|
19
|
+
*
|
|
20
|
+
* Joining a failed fiber propagates the fiber's Cause. Use {@link await_ await} when
|
|
21
|
+
* you need to inspect the `Exit` instead of failing.
|
|
22
|
+
*
|
|
23
|
+
* **Example** (Joining a fiber)
|
|
24
|
+
*
|
|
25
|
+
* ```ts import.meta.vitest
|
|
26
|
+
* import { Effect, Fiber } from "effect"
|
|
27
|
+
*
|
|
28
|
+
* const program = Effect.gen(function*() {
|
|
29
|
+
* const fiber = yield* Effect.forkChild(Effect.succeed(42))
|
|
30
|
+
* return yield* Fiber.join(fiber)
|
|
31
|
+
* })
|
|
32
|
+
*
|
|
33
|
+
* const actual = await Effect.runPromise(program)
|
|
34
|
+
* actual // => 42
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @see {@link await_ await} for inspecting the fiber outcome as an Exit
|
|
38
|
+
*
|
|
39
|
+
* @stability stable
|
|
40
|
+
* @category combinators
|
|
41
|
+
* @since 2.0.0
|
|
42
|
+
*/
|
|
43
|
+
const join = fiberJoin;
|
|
44
|
+
/**
|
|
45
|
+
* Interrupts a fiber, causing it to stop executing and clean up any
|
|
46
|
+
* acquired resources.
|
|
47
|
+
*
|
|
48
|
+
* **When to use**
|
|
49
|
+
*
|
|
50
|
+
* Use when you need to cancel a forked fiber and wait for its cleanup to
|
|
51
|
+
* complete.
|
|
52
|
+
*
|
|
53
|
+
* **Details**
|
|
54
|
+
*
|
|
55
|
+
* The returned Effect completes only after the interrupted fiber has completed.
|
|
56
|
+
*
|
|
57
|
+
* **Gotchas**
|
|
58
|
+
*
|
|
59
|
+
* Interruption is cooperative. A fiber can continue running while it is inside
|
|
60
|
+
* uninterruptible work or finalizers.
|
|
61
|
+
*
|
|
62
|
+
* **Example** (Interrupting a fiber)
|
|
63
|
+
*
|
|
64
|
+
* ```ts import.meta.vitest
|
|
65
|
+
* import { Effect, Fiber } from "effect"
|
|
66
|
+
*
|
|
67
|
+
* const program = Effect.gen(function*() {
|
|
68
|
+
* const fiber = yield* Effect.forkChild(
|
|
69
|
+
* Effect.delay("1 second")(Effect.succeed(42))
|
|
70
|
+
* )
|
|
71
|
+
* yield* Fiber.interrupt(fiber)
|
|
72
|
+
* })
|
|
73
|
+
*
|
|
74
|
+
* await Effect.runPromise(program)
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @see {@link interruptAs} for specifying the interrupting fiber ID
|
|
78
|
+
* @see {@link await_ await} for observing the interrupted fiber's Exit
|
|
79
|
+
*
|
|
80
|
+
* @stability stable
|
|
81
|
+
* @category interruption
|
|
82
|
+
* @since 2.0.0
|
|
83
|
+
*/
|
|
84
|
+
const interrupt = fiberInterrupt;
|
|
85
|
+
/**
|
|
86
|
+
* Interrupts a fiber with a specific fiber ID as the interruptor. This allows
|
|
87
|
+
* tracking which fiber initiated the interruption.
|
|
88
|
+
*
|
|
89
|
+
* **When to use**
|
|
90
|
+
*
|
|
91
|
+
* Use when runtime diagnostics or tracing should attribute the interruption to
|
|
92
|
+
* a specific fiber ID.
|
|
93
|
+
*
|
|
94
|
+
* **Details**
|
|
95
|
+
*
|
|
96
|
+
* The returned Effect completes only after the interrupted fiber has completed.
|
|
97
|
+
*
|
|
98
|
+
* **Gotchas**
|
|
99
|
+
*
|
|
100
|
+
* The supplied ID affects the recorded interruptor. It does not make
|
|
101
|
+
* interruption synchronous or force uninterruptible regions to stop early.
|
|
102
|
+
*
|
|
103
|
+
* **Example** (Interrupting a fiber as another fiber)
|
|
104
|
+
*
|
|
105
|
+
* ```ts import.meta.vitest
|
|
106
|
+
* import { Effect, Fiber } from "effect"
|
|
107
|
+
*
|
|
108
|
+
* const program = Effect.gen(function*() {
|
|
109
|
+
* const targetFiber = yield* Effect.forkChild(
|
|
110
|
+
* Effect.delay("5 seconds")(Effect.succeed("task completed"))
|
|
111
|
+
* )
|
|
112
|
+
*
|
|
113
|
+
* // Interrupt the fiber, specifying fiber ID 123 as the interruptor
|
|
114
|
+
* yield* Fiber.interruptAs(targetFiber, 123)
|
|
115
|
+
* })
|
|
116
|
+
*
|
|
117
|
+
* await Effect.runPromise(program)
|
|
118
|
+
* ```
|
|
119
|
+
*
|
|
120
|
+
* @see {@link interrupt} for using the current fiber as the interruptor
|
|
121
|
+
*
|
|
122
|
+
* @stability stable
|
|
123
|
+
* @category interruption
|
|
124
|
+
* @since 2.0.0
|
|
125
|
+
*/
|
|
126
|
+
const interruptAs = fiberInterruptAs;
|
|
127
|
+
/**
|
|
128
|
+
* Interrupts all fibers in the provided iterable, causing them to stop executing
|
|
129
|
+
* and clean up any acquired resources.
|
|
130
|
+
*
|
|
131
|
+
* **When to use**
|
|
132
|
+
*
|
|
133
|
+
* Use when you need to cancel several forked fibers and wait for their cleanup
|
|
134
|
+
* to complete.
|
|
135
|
+
*
|
|
136
|
+
* **Details**
|
|
137
|
+
*
|
|
138
|
+
* The current fiber is recorded as the interruptor. The returned Effect
|
|
139
|
+
* completes only after all interrupted fibers have completed.
|
|
140
|
+
*
|
|
141
|
+
* **Gotchas**
|
|
142
|
+
*
|
|
143
|
+
* Interruption is cooperative for each fiber. The returned Effect can wait for
|
|
144
|
+
* uninterruptible work and finalizers in any interrupted fiber.
|
|
145
|
+
*
|
|
146
|
+
* **Example** (Interrupting multiple fibers)
|
|
147
|
+
*
|
|
148
|
+
* ```ts import.meta.vitest
|
|
149
|
+
* import { Effect, Fiber } from "effect"
|
|
150
|
+
*
|
|
151
|
+
* const program = Effect.gen(function*() {
|
|
152
|
+
* const fiber1 = yield* Effect.forkChild(Effect.never)
|
|
153
|
+
* const fiber2 = yield* Effect.forkChild(Effect.never)
|
|
154
|
+
* const fiber3 = yield* Effect.forkChild(Effect.never)
|
|
155
|
+
* yield* Fiber.interruptAll([fiber1, fiber2, fiber3])
|
|
156
|
+
* })
|
|
157
|
+
*
|
|
158
|
+
* await Effect.runPromise(program)
|
|
159
|
+
* ```
|
|
160
|
+
*
|
|
161
|
+
* @see {@link interruptAllAs} for specifying the interrupting fiber ID
|
|
162
|
+
*
|
|
163
|
+
* @stability stable
|
|
164
|
+
* @category interruption
|
|
165
|
+
* @since 2.0.0
|
|
166
|
+
*/
|
|
167
|
+
const interruptAll = fiberInterruptAll;
|
|
168
|
+
/**
|
|
169
|
+
* Returns the current fiber if called from within a fiber context,
|
|
170
|
+
* otherwise returns `undefined`.
|
|
171
|
+
*
|
|
172
|
+
* **When to use**
|
|
173
|
+
*
|
|
174
|
+
* Use when you need low-level runtime integrations that need access to the currently
|
|
175
|
+
* executing fiber.
|
|
176
|
+
*
|
|
177
|
+
* **Gotchas**
|
|
178
|
+
*
|
|
179
|
+
* This is a synchronous accessor, not an Effect. It returns `undefined` outside
|
|
180
|
+
* an active fiber runtime context.
|
|
181
|
+
*
|
|
182
|
+
* **Example** (Getting the current fiber)
|
|
183
|
+
*
|
|
184
|
+
* ```ts import.meta.vitest
|
|
185
|
+
* import { Effect, Fiber } from "effect"
|
|
186
|
+
*
|
|
187
|
+
* const program = Effect.gen(function*() {
|
|
188
|
+
* const current = Fiber.getCurrent()
|
|
189
|
+
* return current !== undefined
|
|
190
|
+
* })
|
|
191
|
+
*
|
|
192
|
+
* const actual = await Effect.runPromise(program)
|
|
193
|
+
* actual // => true
|
|
194
|
+
* ```
|
|
195
|
+
*
|
|
196
|
+
* @stability stable
|
|
197
|
+
* @category getters
|
|
198
|
+
* @since 4.0.0
|
|
199
|
+
*/
|
|
200
|
+
const getCurrent = getCurrentFiber;
|
|
201
|
+
/**
|
|
202
|
+
* Adds a fiber to a `Scope` and returns the same fiber.
|
|
203
|
+
*
|
|
204
|
+
* **When to use**
|
|
205
|
+
*
|
|
206
|
+
* Use when a manually managed fiber should be interrupted when a Scope closes.
|
|
207
|
+
*
|
|
208
|
+
* **Details**
|
|
209
|
+
*
|
|
210
|
+
* When the scope is closed, the fiber is interrupted. If the scope is already
|
|
211
|
+
* closed, the fiber is interrupted immediately.
|
|
212
|
+
*
|
|
213
|
+
* **Gotchas**
|
|
214
|
+
*
|
|
215
|
+
* This does not wait for the fiber to complete. It only registers the
|
|
216
|
+
* interruption finalizer and returns the same fiber.
|
|
217
|
+
*
|
|
218
|
+
* @see {@link interrupt} for interrupting and waiting for completion
|
|
219
|
+
*
|
|
220
|
+
* @stability stable
|
|
221
|
+
* @category resource management
|
|
222
|
+
* @since 4.0.0
|
|
223
|
+
*/
|
|
224
|
+
const runIn = fiberRunIn;
|
|
225
|
+
// ../../node_modules/.pnpm/effect@4.1.2_patch_hash=0e61176203bb1256381ce1c5b488d7951983ce38ff698647d3f4617383abbf07/node_modules/effect/dist/Latch.js
|
|
226
|
+
/**
|
|
227
|
+
* Creates a `Latch` synchronously, outside of `Effect`.
|
|
228
|
+
*
|
|
229
|
+
* **When to use**
|
|
230
|
+
*
|
|
231
|
+
* Use when you need to allocate a `Latch` synchronously outside an Effect
|
|
232
|
+
* workflow.
|
|
233
|
+
*
|
|
234
|
+
* **Details**
|
|
235
|
+
*
|
|
236
|
+
* The latch starts closed by default; pass `true` to create it open.
|
|
237
|
+
*
|
|
238
|
+
* **Example** (Creating a latch unsafely)
|
|
239
|
+
*
|
|
240
|
+
* ```ts import.meta.vitest
|
|
241
|
+
* import { Effect, Fiber, Latch } from "effect"
|
|
242
|
+
*
|
|
243
|
+
* const latch = Latch.makeUnsafe(false)
|
|
244
|
+
* const waiter = latch.await.pipe(Effect.as("opened"))
|
|
245
|
+
*
|
|
246
|
+
* const program = Effect.gen(function*() {
|
|
247
|
+
* const fiber = yield* Effect.forkChild(waiter)
|
|
248
|
+
* yield* latch.open
|
|
249
|
+
* return yield* Fiber.join(fiber)
|
|
250
|
+
* })
|
|
251
|
+
*
|
|
252
|
+
* await Effect.runPromise(program) // => "opened"
|
|
253
|
+
* ```
|
|
254
|
+
*
|
|
255
|
+
* @see {@link make} for creating a latch inside Effect code
|
|
256
|
+
*
|
|
257
|
+
* @stability stable
|
|
258
|
+
* @category constructors
|
|
259
|
+
* @since 4.0.0
|
|
260
|
+
*/
|
|
261
|
+
const makeUnsafe = makeLatchUnsafe;
|
|
262
|
+
/**
|
|
263
|
+
* Creates a `Latch` inside `Effect`.
|
|
264
|
+
*
|
|
265
|
+
* **When to use**
|
|
266
|
+
*
|
|
267
|
+
* Use to create a latch for coordinating fibers inside Effect code.
|
|
268
|
+
*
|
|
269
|
+
* **Details**
|
|
270
|
+
*
|
|
271
|
+
* The latch starts closed by default; pass `true` to create it open.
|
|
272
|
+
*
|
|
273
|
+
* **Example** (Creating a latch)
|
|
274
|
+
*
|
|
275
|
+
* ```ts import.meta.vitest
|
|
276
|
+
* import { Effect, Fiber, Latch } from "effect"
|
|
277
|
+
*
|
|
278
|
+
* const program = Effect.gen(function*() {
|
|
279
|
+
* const latch = yield* Latch.make(false)
|
|
280
|
+
* const waiter = latch.await.pipe(Effect.as("opened"))
|
|
281
|
+
*
|
|
282
|
+
* const fiber = yield* Effect.forkChild(waiter)
|
|
283
|
+
* yield* latch.open
|
|
284
|
+
* return yield* Fiber.join(fiber)
|
|
285
|
+
* })
|
|
286
|
+
*
|
|
287
|
+
* await Effect.runPromise(program) // => "opened"
|
|
288
|
+
* ```
|
|
289
|
+
*
|
|
290
|
+
* @see {@link makeUnsafe} for synchronous allocation outside Effect code
|
|
291
|
+
*
|
|
292
|
+
* @stability stable
|
|
293
|
+
* @category constructors
|
|
294
|
+
* @since 4.0.0
|
|
295
|
+
*/
|
|
296
|
+
const make$1 = makeLatch;
|
|
297
|
+
/**
|
|
298
|
+
* Opens the latch and releases fibers waiting on it.
|
|
299
|
+
*
|
|
300
|
+
* **When to use**
|
|
301
|
+
*
|
|
302
|
+
* Use to open a latch and release all fibers that are waiting on it.
|
|
303
|
+
*
|
|
304
|
+
* **Details**
|
|
305
|
+
*
|
|
306
|
+
* The returned effect succeeds with `true` when this call changed the latch
|
|
307
|
+
* from closed to open, or `false` if it was already open.
|
|
308
|
+
*
|
|
309
|
+
* @see {@link openUnsafe} for a synchronous variant
|
|
310
|
+
* @see {@link release} to release waiting fibers without opening the latch
|
|
311
|
+
*
|
|
312
|
+
* @stability stable
|
|
313
|
+
* @category combinators
|
|
314
|
+
* @since 4.0.0
|
|
315
|
+
*/
|
|
316
|
+
const open = (self) => self.open;
|
|
317
|
+
/**
|
|
318
|
+
* Checks whether the latch is currently open or closed.
|
|
319
|
+
*
|
|
320
|
+
* **When to use**
|
|
321
|
+
*
|
|
322
|
+
* Use to check the state of the latch without suspending or changing its state.
|
|
323
|
+
*
|
|
324
|
+
* @stability stable
|
|
325
|
+
* @category predicates
|
|
326
|
+
* @since 4.0.0
|
|
327
|
+
*/
|
|
328
|
+
const isOpen = (self) => self.isOpen();
|
|
329
|
+
// ../../node_modules/.pnpm/effect@4.1.2_patch_hash=0e61176203bb1256381ce1c5b488d7951983ce38ff698647d3f4617383abbf07/node_modules/effect/dist/Channel.js
|
|
330
|
+
/**
|
|
331
|
+
* Provides low-level building blocks for streaming data through Effect.
|
|
332
|
+
*
|
|
333
|
+
* A `Channel` can read input elements, write output elements, fail with a typed
|
|
334
|
+
* error, and finish with a typed result while managing resources safely.
|
|
335
|
+
* Streams and sinks are built on channels, so most application code uses those
|
|
336
|
+
* higher-level modules instead. This module is useful when implementing stream
|
|
337
|
+
* operators or specialized streaming workflows.
|
|
338
|
+
*
|
|
339
|
+
* @stability stable
|
|
340
|
+
* @since 2.0.0
|
|
341
|
+
*/
|
|
342
|
+
/**
|
|
343
|
+
* Runtime identifier stored on `Channel` values and used by `isChannel` to
|
|
344
|
+
* recognize them.
|
|
345
|
+
*
|
|
346
|
+
* @stability stable
|
|
347
|
+
* @category type IDs
|
|
348
|
+
* @since 4.0.0
|
|
349
|
+
*/
|
|
350
|
+
const TypeId$2 = "~effect/Channel";
|
|
351
|
+
/**
|
|
352
|
+
* Checks whether a value is a `Channel`.
|
|
353
|
+
*
|
|
354
|
+
* **Example** (Checking for channels)
|
|
355
|
+
*
|
|
356
|
+
* ```ts import.meta.vitest
|
|
357
|
+
* import { Channel } from "effect"
|
|
358
|
+
*
|
|
359
|
+
* const channel = Channel.succeed(42)
|
|
360
|
+
* Channel.isChannel(channel) // => true
|
|
361
|
+
* Channel.isChannel("not a channel") // => false
|
|
362
|
+
* ```
|
|
363
|
+
*
|
|
364
|
+
* @stability stable
|
|
365
|
+
* @category guards
|
|
366
|
+
* @since 3.5.4
|
|
367
|
+
*/
|
|
368
|
+
const isChannel = (u) => hasProperty(u, TypeId$2);
|
|
369
|
+
const ChannelProto = {
|
|
370
|
+
[TypeId$2]: {
|
|
371
|
+
_Env: identity,
|
|
372
|
+
_InErr: identity,
|
|
373
|
+
_InElem: identity,
|
|
374
|
+
_OutErr: identity,
|
|
375
|
+
_OutElem: identity
|
|
376
|
+
},
|
|
377
|
+
pipe() {
|
|
378
|
+
return pipeArguments(this, arguments);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
/**
|
|
382
|
+
* Creates a `Channel` from a transformation function that operates on upstream pulls.
|
|
383
|
+
*
|
|
384
|
+
* **Example** (Creating channels from transforms)
|
|
385
|
+
*
|
|
386
|
+
* ```ts import.meta.vitest
|
|
387
|
+
* import { Channel, Effect } from "effect"
|
|
388
|
+
*
|
|
389
|
+
* const channel = Channel.fromTransform((upstream, scope) =>
|
|
390
|
+
* Effect.succeed(upstream)
|
|
391
|
+
* )
|
|
392
|
+
* await Effect.runPromise(Channel.runCollect(channel)) // => []
|
|
393
|
+
* ```
|
|
394
|
+
*
|
|
395
|
+
* @stability stable
|
|
396
|
+
* @category constructors
|
|
397
|
+
* @since 4.0.0
|
|
398
|
+
*/
|
|
399
|
+
const fromTransform = (transform) => {
|
|
400
|
+
const self = Object.create(ChannelProto);
|
|
401
|
+
self.transform = (upstream, scope) => catchCause(transform(upstream, scope), (cause) => succeed$2(failCause$1(cause)));
|
|
402
|
+
return self;
|
|
403
|
+
};
|
|
404
|
+
/**
|
|
405
|
+
* Transforms a Channel by applying a function to its Pull implementation.
|
|
406
|
+
*
|
|
407
|
+
* **Example** (Transforming pull behavior)
|
|
408
|
+
*
|
|
409
|
+
* ```ts import.meta.vitest
|
|
410
|
+
* import { Channel, Effect } from "effect"
|
|
411
|
+
*
|
|
412
|
+
* // Transform a channel by modifying its pull behavior
|
|
413
|
+
* const originalChannel = Channel.fromIterable([1, 2, 3])
|
|
414
|
+
*
|
|
415
|
+
* const transformedChannel = Channel.transformPull(
|
|
416
|
+
* originalChannel,
|
|
417
|
+
* (pull, scope) =>
|
|
418
|
+
* Effect.succeed(
|
|
419
|
+
* Effect.map(pull, (value) => value * 2)
|
|
420
|
+
* )
|
|
421
|
+
* )
|
|
422
|
+
* await Effect.runPromise(Channel.runCollect(transformedChannel)) // => [2, 4, 6]
|
|
423
|
+
* ```
|
|
424
|
+
*
|
|
425
|
+
* @stability stable
|
|
426
|
+
* @category constructors
|
|
427
|
+
* @since 4.0.0
|
|
428
|
+
*/
|
|
429
|
+
const transformPull$1 = (self, f) => fromTransform((upstream, scope) => flatMap$2(toTransform(self)(upstream, scope), (pull) => f(pull, scope)));
|
|
430
|
+
/**
|
|
431
|
+
* Creates a `Channel` from an `Effect` that produces a `Pull`.
|
|
432
|
+
*
|
|
433
|
+
* **Example** (Creating channels from pulls)
|
|
434
|
+
*
|
|
435
|
+
* ```ts import.meta.vitest
|
|
436
|
+
* import { Cause, Channel, Effect } from "effect"
|
|
437
|
+
*
|
|
438
|
+
* const channel = Channel.fromPull(Effect.sync(() => {
|
|
439
|
+
* let emitted = false
|
|
440
|
+
* return Effect.suspend(() => {
|
|
441
|
+
* if (emitted) return Cause.done()
|
|
442
|
+
* emitted = true
|
|
443
|
+
* return Effect.succeed(42)
|
|
444
|
+
* })
|
|
445
|
+
* }))
|
|
446
|
+
* await Effect.runPromise(Channel.runCollect(channel)) // => [42]
|
|
447
|
+
* ```
|
|
448
|
+
*
|
|
449
|
+
* @stability stable
|
|
450
|
+
* @category constructors
|
|
451
|
+
* @since 4.0.0
|
|
452
|
+
*/
|
|
453
|
+
const fromPull$1 = (effect) => fromTransform((_, __) => effect);
|
|
454
|
+
/**
|
|
455
|
+
* Creates a `Channel` from a transformation function that operates on upstream
|
|
456
|
+
* pulls, but also provides a forked scope that closes when the resulting
|
|
457
|
+
* Channel completes.
|
|
458
|
+
*
|
|
459
|
+
* **When to use**
|
|
460
|
+
*
|
|
461
|
+
* Use when building channels that require scoped resource lifecycle management,
|
|
462
|
+
* providing both the channel scope and a forked scope that automatically closes
|
|
463
|
+
* when the channel completes.
|
|
464
|
+
*
|
|
465
|
+
* @see {@link fromTransform} for a simpler transformation without a forked scope
|
|
466
|
+
* @stability stable
|
|
467
|
+
* @category constructors
|
|
468
|
+
* @since 4.0.0
|
|
469
|
+
*/
|
|
470
|
+
const fromTransformBracket = (f) => fromTransform(fnUntraced(function* (upstream, scope) {
|
|
471
|
+
const closableScope = forkUnsafe(scope);
|
|
472
|
+
const onCause = (cause) => close(closableScope, doneExitFromCause(cause));
|
|
473
|
+
const pull = yield* onError(f(upstream, scope, closableScope), onCause);
|
|
474
|
+
return onError(pull, onCause);
|
|
475
|
+
}));
|
|
476
|
+
/**
|
|
477
|
+
* Converts a `Channel` back to its underlying transformation function.
|
|
478
|
+
*
|
|
479
|
+
* **Example** (Extracting channel transforms)
|
|
480
|
+
*
|
|
481
|
+
* ```ts import.meta.vitest
|
|
482
|
+
* import { Channel, Effect } from "effect"
|
|
483
|
+
*
|
|
484
|
+
* const channel = Channel.succeed(42)
|
|
485
|
+
* const transform = Channel.toTransform(channel)
|
|
486
|
+
* typeof transform // => "function"
|
|
487
|
+
* Effect.runSync(Channel.runCollect(channel)) // => [42]
|
|
488
|
+
* ```
|
|
489
|
+
*
|
|
490
|
+
* @stability stable
|
|
491
|
+
* @category destructors
|
|
492
|
+
* @since 4.0.0
|
|
493
|
+
*/
|
|
494
|
+
const toTransform = (channel) => channel.transform;
|
|
495
|
+
/**
|
|
496
|
+
* The default chunk size used by channels for batching operations.
|
|
497
|
+
*
|
|
498
|
+
* **Example** (Reading the default chunk size)
|
|
499
|
+
*
|
|
500
|
+
* ```ts import.meta.vitest
|
|
501
|
+
* import { Channel } from "effect"
|
|
502
|
+
*
|
|
503
|
+
* Channel.DefaultChunkSize // => 4096
|
|
504
|
+
* ```
|
|
505
|
+
*
|
|
506
|
+
* @stability stable
|
|
507
|
+
* @category constants
|
|
508
|
+
* @since 4.0.0
|
|
509
|
+
*/
|
|
510
|
+
const DefaultChunkSize = 4096;
|
|
511
|
+
const asyncQueue = (scope, f, options) => make$3({
|
|
512
|
+
capacity: options?.bufferSize,
|
|
513
|
+
strategy: options?.strategy
|
|
514
|
+
}).pipe(tap$1((queue) => addFinalizer(scope, shutdown(queue))), tap$1((queue) => provide(f(queue), scope).pipe(catchCause((cause) => failCause$3(queue, cause)), forkIn(scope, { startImmediately: true }))));
|
|
515
|
+
/**
|
|
516
|
+
* Creates a `Channel` that interacts with a callback function using a queue, emitting arrays.
|
|
517
|
+
*
|
|
518
|
+
* **Example** (Creating array channels from callbacks)
|
|
519
|
+
*
|
|
520
|
+
* ```ts import.meta.vitest
|
|
521
|
+
* import { Channel, Effect, Queue } from "effect"
|
|
522
|
+
*
|
|
523
|
+
* const channel = Channel.callbackArray<number>(Effect.fn(function*(queue) {
|
|
524
|
+
* yield* Queue.offer(queue, 1)
|
|
525
|
+
* yield* Queue.offer(queue, 2)
|
|
526
|
+
* yield* Queue.end(queue)
|
|
527
|
+
* }))
|
|
528
|
+
* await Effect.runPromise(Channel.runCollect(channel)) // => [[1, 2]]
|
|
529
|
+
* ```
|
|
530
|
+
*
|
|
531
|
+
* @stability stable
|
|
532
|
+
* @category constructors
|
|
533
|
+
* @since 4.0.0
|
|
534
|
+
*/
|
|
535
|
+
const callbackArray = (f, options) => fromTransform((_, scope) => map$3(asyncQueue(scope, f, options), takeAll));
|
|
536
|
+
/**
|
|
537
|
+
* Creates a `Channel` that lazily evaluates to another channel.
|
|
538
|
+
*
|
|
539
|
+
* **Example** (Suspending channel creation)
|
|
540
|
+
*
|
|
541
|
+
* ```ts import.meta.vitest
|
|
542
|
+
* import { Channel, Effect } from "effect"
|
|
543
|
+
*
|
|
544
|
+
* const channel = Channel.suspend(() => Channel.succeed(42))
|
|
545
|
+
* Effect.runSync(Channel.runCollect(channel)) // => [42]
|
|
546
|
+
* ```
|
|
547
|
+
*
|
|
548
|
+
* @stability stable
|
|
549
|
+
* @category constructors
|
|
550
|
+
* @since 2.0.0
|
|
551
|
+
*/
|
|
552
|
+
const suspend$1 = (evaluate) => fromTransform((upstream, scope) => suspend$2(() => toTransform(evaluate())(upstream, scope)));
|
|
553
|
+
/**
|
|
554
|
+
* Creates a `Channel` from an iterator.
|
|
555
|
+
*
|
|
556
|
+
* **Example** (Creating channels from iterators)
|
|
557
|
+
*
|
|
558
|
+
* ```ts import.meta.vitest
|
|
559
|
+
* import { Channel, Effect } from "effect"
|
|
560
|
+
*
|
|
561
|
+
* const numbers = [1, 2, 3, 4, 5]
|
|
562
|
+
* const channel = Channel.fromIterator(() => numbers[Symbol.iterator]())
|
|
563
|
+
* Effect.runSync(Channel.runCollect(channel)) // => [1, 2, 3, 4, 5]
|
|
564
|
+
* ```
|
|
565
|
+
*
|
|
566
|
+
* @stability stable
|
|
567
|
+
* @category constructors
|
|
568
|
+
* @since 4.0.0
|
|
569
|
+
*/
|
|
570
|
+
const fromIterator = (iterator) => fromPull$1(sync(() => {
|
|
571
|
+
const iter = iterator();
|
|
572
|
+
return suspend$2(() => {
|
|
573
|
+
const state = iter.next();
|
|
574
|
+
return state.done ? done(state.value) : succeed$2(state.value);
|
|
575
|
+
});
|
|
576
|
+
}));
|
|
577
|
+
/**
|
|
578
|
+
* Creates a `Channel` from an iterator that emits arrays of elements.
|
|
579
|
+
*
|
|
580
|
+
* **Details**
|
|
581
|
+
*
|
|
582
|
+
* Finite fractional `chunkSize` values are rounded down. `NaN` and non-positive
|
|
583
|
+
* values are treated as `1` so every successful pull emits a non-empty array.
|
|
584
|
+
*
|
|
585
|
+
* **Example** (Batching iterator output)
|
|
586
|
+
*
|
|
587
|
+
* ```ts import.meta.vitest
|
|
588
|
+
* import { Channel, Effect } from "effect"
|
|
589
|
+
*
|
|
590
|
+
* // Create a channel from a simple iterator
|
|
591
|
+
* const numberIterator = (): Iterator<number, string> => {
|
|
592
|
+
* let count = 0
|
|
593
|
+
* return {
|
|
594
|
+
* next: () => {
|
|
595
|
+
* if (count < 3) {
|
|
596
|
+
* return { value: count++, done: false }
|
|
597
|
+
* }
|
|
598
|
+
* return { value: "finished", done: true }
|
|
599
|
+
* }
|
|
600
|
+
* }
|
|
601
|
+
* }
|
|
602
|
+
*
|
|
603
|
+
* const channel = Channel.fromIteratorArray(() => numberIterator(), 2)
|
|
604
|
+
* Effect.runSync(Channel.runCollect(channel)) // => [[0, 1], [2]]
|
|
605
|
+
* ```
|
|
606
|
+
*
|
|
607
|
+
* **Example** (Batching generator output)
|
|
608
|
+
*
|
|
609
|
+
* ```ts import.meta.vitest
|
|
610
|
+
* import { Channel, Effect } from "effect"
|
|
611
|
+
*
|
|
612
|
+
* // Create channel from a generator function
|
|
613
|
+
* function* fibonacci(): Generator<number, void, unknown> {
|
|
614
|
+
* let a = 0, b = 1
|
|
615
|
+
* for (let i = 0; i < 5; i++) {
|
|
616
|
+
* yield a
|
|
617
|
+
* ;[a, b] = [b, a + b]
|
|
618
|
+
* }
|
|
619
|
+
* }
|
|
620
|
+
*
|
|
621
|
+
* const fibChannel = Channel.fromIteratorArray(() => fibonacci(), 3)
|
|
622
|
+
* Effect.runSync(Channel.runCollect(fibChannel)) // => [[0, 1, 1], [2, 3]]
|
|
623
|
+
* ```
|
|
624
|
+
*
|
|
625
|
+
* @stability stable
|
|
626
|
+
* @category constructors
|
|
627
|
+
* @since 4.0.0
|
|
628
|
+
*/
|
|
629
|
+
const fromIteratorArray = (iterator, chunkSize = DefaultChunkSize) => {
|
|
630
|
+
const size = normalizeNonEmpty(chunkSize);
|
|
631
|
+
return fromPull$1(sync(() => {
|
|
632
|
+
const iter = iterator();
|
|
633
|
+
let done$8 = none();
|
|
634
|
+
return suspend$2(() => {
|
|
635
|
+
if (done$8._tag === "Some") return done(done$8.value);
|
|
636
|
+
const buffer = [];
|
|
637
|
+
while (buffer.length < size) {
|
|
638
|
+
const state = iter.next();
|
|
639
|
+
if (state.done) {
|
|
640
|
+
if (buffer.length === 0) return done(state.value);
|
|
641
|
+
done$8 = some(state.value);
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
buffer.push(state.value);
|
|
645
|
+
}
|
|
646
|
+
return succeed$2(buffer);
|
|
647
|
+
});
|
|
648
|
+
}));
|
|
649
|
+
};
|
|
650
|
+
/**
|
|
651
|
+
* Creates a `Channel` that emits arrays of elements from an iterable.
|
|
652
|
+
*
|
|
653
|
+
* **Details**
|
|
654
|
+
*
|
|
655
|
+
* Finite fractional `chunkSize` values are rounded down. `NaN` and non-positive
|
|
656
|
+
* values are treated as `1`.
|
|
657
|
+
*
|
|
658
|
+
* **Example** (Batching iterable output)
|
|
659
|
+
*
|
|
660
|
+
* ```ts import.meta.vitest
|
|
661
|
+
* import { Channel, Effect } from "effect"
|
|
662
|
+
*
|
|
663
|
+
* const numbers = [1, 2, 3, 4, 5]
|
|
664
|
+
* const channel = Channel.fromIterableArray(numbers, 4)
|
|
665
|
+
* Effect.runSync(Channel.runCollect(channel)) // => [[1, 2, 3, 4], [5]]
|
|
666
|
+
* ```
|
|
667
|
+
*
|
|
668
|
+
* @stability stable
|
|
669
|
+
* @category constructors
|
|
670
|
+
* @since 4.0.0
|
|
671
|
+
*/
|
|
672
|
+
const fromIterableArray = (iterable, chunkSize = DefaultChunkSize) => fromIteratorArray(() => iterable[Symbol.iterator](), chunkSize);
|
|
673
|
+
/**
|
|
674
|
+
* Creates a `Channel` that emits a single value and then ends.
|
|
675
|
+
*
|
|
676
|
+
* **Example** (Creating channels that succeed)
|
|
677
|
+
*
|
|
678
|
+
* ```ts import.meta.vitest
|
|
679
|
+
* import { Channel, Effect } from "effect"
|
|
680
|
+
*
|
|
681
|
+
* const channel = Channel.succeed(42)
|
|
682
|
+
* Effect.runSync(Channel.runCollect(channel)) // => [42]
|
|
683
|
+
* ```
|
|
684
|
+
*
|
|
685
|
+
* @stability stable
|
|
686
|
+
* @category constructors
|
|
687
|
+
* @since 2.0.0
|
|
688
|
+
*/
|
|
689
|
+
const succeed$1 = (value) => fromEffect$1(succeed$2(value));
|
|
690
|
+
/**
|
|
691
|
+
* Represents a `Channel` that emits no elements.
|
|
692
|
+
*
|
|
693
|
+
* **Example** (Creating empty channels)
|
|
694
|
+
*
|
|
695
|
+
* ```ts import.meta.vitest
|
|
696
|
+
* import { Channel, Effect } from "effect"
|
|
697
|
+
*
|
|
698
|
+
* // Create an empty channel
|
|
699
|
+
* const emptyChannel = Channel.empty
|
|
700
|
+
*
|
|
701
|
+
* // Use empty channel in composition
|
|
702
|
+
* const combined = Channel.concatWith(emptyChannel, () => Channel.succeed(42))
|
|
703
|
+
* // Will immediately provide the second channel's output
|
|
704
|
+
*
|
|
705
|
+
* // Empty channel can be used as a no-op in conditional logic
|
|
706
|
+
* const conditionalChannel = (shouldEmit: boolean) =>
|
|
707
|
+
* shouldEmit ? Channel.succeed("data") : Channel.empty
|
|
708
|
+
*
|
|
709
|
+
* Effect.runSync(Channel.runCollect(conditionalChannel(true))) // => ["data"]
|
|
710
|
+
* ```
|
|
711
|
+
*
|
|
712
|
+
* @stability stable
|
|
713
|
+
* @category constructors
|
|
714
|
+
* @since 4.0.0
|
|
715
|
+
*/
|
|
716
|
+
const empty$1 = /*#__PURE__*/ fromPull$1(/*#__PURE__*/ succeed$2(/*#__PURE__*/ done()));
|
|
717
|
+
/**
|
|
718
|
+
* Constructs a channel that fails immediately with the specified error.
|
|
719
|
+
*
|
|
720
|
+
* **Example** (Failing with an error)
|
|
721
|
+
*
|
|
722
|
+
* ```ts import.meta.vitest
|
|
723
|
+
* import { Channel, Effect, Exit } from "effect"
|
|
724
|
+
*
|
|
725
|
+
* const failedChannel = Channel.fail("Something went wrong")
|
|
726
|
+
* Effect.runSync(Effect.exit(Channel.runCollect(failedChannel))) // => Exit.fail("Something went wrong")
|
|
727
|
+
* ```
|
|
728
|
+
*
|
|
729
|
+
* @stability stable
|
|
730
|
+
* @category constructors
|
|
731
|
+
* @since 2.0.0
|
|
732
|
+
*/
|
|
733
|
+
const fail$1 = (error) => fromPull$1(succeed$2(fail$2(error)));
|
|
734
|
+
/**
|
|
735
|
+
* Constructs a channel that fails immediately with the specified `Cause`.
|
|
736
|
+
*
|
|
737
|
+
* **When to use**
|
|
738
|
+
*
|
|
739
|
+
* Use when the channel failure must preserve a full `Cause`, such as defects,
|
|
740
|
+
* interruptions, or combined failures.
|
|
741
|
+
*
|
|
742
|
+
* **Example** (Failing with causes)
|
|
743
|
+
*
|
|
744
|
+
* ```ts import.meta.vitest
|
|
745
|
+
* import { Cause, Channel, Effect, Exit } from "effect"
|
|
746
|
+
*
|
|
747
|
+
* const simpleCause = Cause.fail("Simple error")
|
|
748
|
+
* const failedChannel = Channel.failCause(simpleCause)
|
|
749
|
+
* Effect.runSync(Effect.exit(Channel.runCollect(failedChannel))) // => Exit.failCause(simpleCause)
|
|
750
|
+
* ```
|
|
751
|
+
*
|
|
752
|
+
* @stability stable
|
|
753
|
+
* @category constructors
|
|
754
|
+
* @since 2.0.0
|
|
755
|
+
*/
|
|
756
|
+
const failCause = (cause) => fromPull$1(failCause$1(cause));
|
|
757
|
+
/**
|
|
758
|
+
* Constructs a channel that fails immediately with the specified defect.
|
|
759
|
+
*
|
|
760
|
+
* **Example** (Dying with defects)
|
|
761
|
+
*
|
|
762
|
+
* ```ts import.meta.vitest
|
|
763
|
+
* import { Cause, Channel, Effect, Exit } from "effect"
|
|
764
|
+
*
|
|
765
|
+
* const defect = "Unrecoverable error"
|
|
766
|
+
* const diedChannel = Channel.die(defect)
|
|
767
|
+
* Effect.runSync(Effect.exit(Channel.runCollect(diedChannel))) // => Exit.failCause(Cause.die(defect))
|
|
768
|
+
* ```
|
|
769
|
+
*
|
|
770
|
+
* @stability stable
|
|
771
|
+
* @category constructors
|
|
772
|
+
* @since 4.0.0
|
|
773
|
+
*/
|
|
774
|
+
const die$1 = (defect) => failCause(die$2(defect));
|
|
775
|
+
/**
|
|
776
|
+
* Uses an effect to write a single value to the channel.
|
|
777
|
+
*
|
|
778
|
+
* **Example** (Creating channels from effects)
|
|
779
|
+
*
|
|
780
|
+
* ```ts import.meta.vitest
|
|
781
|
+
* import { Channel, Effect } from "effect"
|
|
782
|
+
*
|
|
783
|
+
* const successChannel = Channel.fromEffect(
|
|
784
|
+
* Effect.succeed("Hello from effect!")
|
|
785
|
+
* )
|
|
786
|
+
* Effect.runSync(Channel.runCollect(successChannel)) // => ["Hello from effect!"]
|
|
787
|
+
* ```
|
|
788
|
+
*
|
|
789
|
+
* @stability stable
|
|
790
|
+
* @category constructors
|
|
791
|
+
* @since 2.0.0
|
|
792
|
+
*/
|
|
793
|
+
const fromEffect$1 = (effect) => fromPull$1(sync(() => {
|
|
794
|
+
let done$9 = false;
|
|
795
|
+
return suspend$2(() => {
|
|
796
|
+
if (done$9) return done();
|
|
797
|
+
done$9 = true;
|
|
798
|
+
return effect;
|
|
799
|
+
});
|
|
800
|
+
}));
|
|
801
|
+
/**
|
|
802
|
+
* Uses an effect and discards its result.
|
|
803
|
+
*
|
|
804
|
+
* @stability stable
|
|
805
|
+
* @category constructors
|
|
806
|
+
* @since 4.0.0
|
|
807
|
+
*/
|
|
808
|
+
const fromEffectDrain = (effect) => fromPull$1(flatMap$2(effect, () => done()));
|
|
809
|
+
/**
|
|
810
|
+
* Creates a channel from a queue that emits arrays of elements.
|
|
811
|
+
*
|
|
812
|
+
* **Example** (Creating batched channels from queues)
|
|
813
|
+
*
|
|
814
|
+
* ```ts import.meta.vitest
|
|
815
|
+
* import { Cause, Channel, Effect, Queue } from "effect"
|
|
816
|
+
*
|
|
817
|
+
* const program = Effect.gen(function*() {
|
|
818
|
+
* const queue = yield* Queue.bounded<number, Cause.Done>(4)
|
|
819
|
+
* yield* Queue.offerAll(queue, [1, 2, 3, 4])
|
|
820
|
+
* yield* Queue.end(queue)
|
|
821
|
+
* const arrayChannel = Channel.fromQueueArray(queue)
|
|
822
|
+
* return yield* Channel.runCollect(arrayChannel)
|
|
823
|
+
* })
|
|
824
|
+
* await Effect.runPromise(program) // => [[1, 2, 3, 4]]
|
|
825
|
+
* ```
|
|
826
|
+
*
|
|
827
|
+
* @stability stable
|
|
828
|
+
* @category constructors
|
|
829
|
+
* @since 4.0.0
|
|
830
|
+
*/
|
|
831
|
+
const fromQueueArray = (queue) => fromPull$1(succeed$2(takeAll(queue)));
|
|
832
|
+
/**
|
|
833
|
+
* Creates a channel from a lazily supplied Web `ReadableStream`.
|
|
834
|
+
*
|
|
835
|
+
* **Details**
|
|
836
|
+
*
|
|
837
|
+
* Read failures are mapped with `onError`. Finalization cancels the reader and
|
|
838
|
+
* releases its lock, or only releases the lock when `releaseLockOnEnd` is set.
|
|
839
|
+
* A rejected cancel hook becomes a defect.
|
|
840
|
+
*
|
|
841
|
+
* **Example** (Reading from a Web stream)
|
|
842
|
+
*
|
|
843
|
+
* ```ts import.meta.vitest
|
|
844
|
+
* import { Channel, Effect } from "effect"
|
|
845
|
+
*
|
|
846
|
+
* const channel = Channel.fromReadableStream({
|
|
847
|
+
* evaluate: () => new ReadableStream({
|
|
848
|
+
* start(controller) {
|
|
849
|
+
* controller.enqueue(1)
|
|
850
|
+
* controller.close()
|
|
851
|
+
* }
|
|
852
|
+
* }),
|
|
853
|
+
* onError: (cause) => new Error(String(cause))
|
|
854
|
+
* })
|
|
855
|
+
*
|
|
856
|
+
* await Effect.runPromise(Channel.runCollect(channel)) // => [[1]]
|
|
857
|
+
* ```
|
|
858
|
+
*
|
|
859
|
+
* @stability stable
|
|
860
|
+
* @category constructors
|
|
861
|
+
* @since 4.0.0
|
|
862
|
+
*/
|
|
863
|
+
const fromReadableStream$1 = (options) => fromTransform((_, scope) => readableStreamToPullUnsafe({
|
|
864
|
+
scope,
|
|
865
|
+
readable: options.evaluate(),
|
|
866
|
+
onError: options.onError,
|
|
867
|
+
releaseLockOnEnd: options.releaseLockOnEnd
|
|
868
|
+
}));
|
|
869
|
+
const readableStreamToPullUnsafe = (options) => {
|
|
870
|
+
const reader = options.readable.getReader();
|
|
871
|
+
const exit = options.exit ?? make$2(void 0);
|
|
872
|
+
const pull = suspend$2(() => {
|
|
873
|
+
if (exit.current) return exit.current;
|
|
874
|
+
return matchCauseEffect(tryPromise({
|
|
875
|
+
try: () => reader.read(),
|
|
876
|
+
catch: options.onError
|
|
877
|
+
}), {
|
|
878
|
+
onFailure: (cause) => exit.current ?? failCause$1(cause),
|
|
879
|
+
onSuccess: ({ done: done$11, value }) => {
|
|
880
|
+
if (exit.current) return exit.current;
|
|
881
|
+
return done$11 ? done() : succeed$2(of(value));
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
return as(addFinalizer(options.scope, options.releaseLockOnEnd ? sync(() => reader.releaseLock()) : promise(() => reader.cancel().catch((error) => reader.closed.then(() => Promise.reject(error), constVoid))).pipe(ensuring$2(sync(() => reader.releaseLock())))), pull);
|
|
886
|
+
};
|
|
887
|
+
/**
|
|
888
|
+
* Creates a channel that pulls values from an `AsyncIterable`.
|
|
889
|
+
*
|
|
890
|
+
* **Details**
|
|
891
|
+
*
|
|
892
|
+
* Each yielded value is emitted as an output element. The iterator's return
|
|
893
|
+
* value becomes the channel's done value. Thrown or rejected iterator errors
|
|
894
|
+
* are converted with `onError`. If the channel scope closes early and the
|
|
895
|
+
* iterator has a `return` method, that method is called. It is awaited unless
|
|
896
|
+
* a `next()` call is still pending, in which case its result is ignored and an
|
|
897
|
+
* async generator may not run its cleanup until that pull settles.
|
|
898
|
+
*
|
|
899
|
+
* @stability stable
|
|
900
|
+
* @category constructors
|
|
901
|
+
* @since 4.0.0
|
|
902
|
+
*/
|
|
903
|
+
const fromAsyncIterable$1 = (iterable, onError) => fromTransform(fnUntraced(function* (_, scope) {
|
|
904
|
+
const iter = iterable[Symbol.asyncIterator]();
|
|
905
|
+
let pulling = false;
|
|
906
|
+
if (iter.return) yield* addFinalizer(scope, suspend$2(() => pulling ? sync(() => void iter.return().catch(constVoid)) : promise(() => iter.return())));
|
|
907
|
+
return flatMap$2(tryPromise({
|
|
908
|
+
try: () => {
|
|
909
|
+
const promise = iter.next();
|
|
910
|
+
pulling = true;
|
|
911
|
+
return promise.finally(() => {
|
|
912
|
+
pulling = false;
|
|
913
|
+
});
|
|
914
|
+
},
|
|
915
|
+
catch: onError
|
|
916
|
+
}), (result) => result.done ? done(result.value) : succeed$2(result.value));
|
|
917
|
+
}));
|
|
918
|
+
/**
|
|
919
|
+
* Creates a channel from an `AsyncIterable`, emitting each yielded value as a
|
|
920
|
+
* single-element non-empty array.
|
|
921
|
+
*
|
|
922
|
+
* **Details**
|
|
923
|
+
*
|
|
924
|
+
* The iterator's return value becomes the channel's done value. Thrown or
|
|
925
|
+
* rejected iterator errors are converted with `onError`. If the channel scope
|
|
926
|
+
* closes early and the iterator has a `return` method, that method is called.
|
|
927
|
+
* It is awaited unless a `next()` call is still pending, in which case its
|
|
928
|
+
* result is ignored and an async generator may not run its cleanup until that
|
|
929
|
+
* pull settles.
|
|
930
|
+
*
|
|
931
|
+
* @stability stable
|
|
932
|
+
* @category constructors
|
|
933
|
+
* @since 4.0.0
|
|
934
|
+
*/
|
|
935
|
+
const fromAsyncIterableArray = (iterable, onError) => map$1(fromAsyncIterable$1(iterable, onError), of);
|
|
936
|
+
/**
|
|
937
|
+
* Maps the output of this channel using the specified function.
|
|
938
|
+
*
|
|
939
|
+
* **Example** (Mapping channel output)
|
|
940
|
+
*
|
|
941
|
+
* ```ts import.meta.vitest
|
|
942
|
+
* import { Channel, Data, Effect } from "effect"
|
|
943
|
+
*
|
|
944
|
+
* class TransformError extends Data.TaggedError("TransformError")<{
|
|
945
|
+
* readonly reason: string
|
|
946
|
+
* }> {}
|
|
947
|
+
*
|
|
948
|
+
* // Basic mapping of channel values
|
|
949
|
+
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
|
950
|
+
* const doubledChannel = Channel.map(numbersChannel, (n) => n * 2)
|
|
951
|
+
* Effect.runSync(Channel.runCollect(doubledChannel)) // => [2, 4, 6, 8, 10]
|
|
952
|
+
*
|
|
953
|
+
* // Transform string data
|
|
954
|
+
* const wordsChannel = Channel.fromIterable(["hello", "world", "effect"])
|
|
955
|
+
* const upperCaseChannel = Channel.map(wordsChannel, (word) => word.toUpperCase())
|
|
956
|
+
* Effect.runSync(Channel.runCollect(upperCaseChannel)) // => ["HELLO", "WORLD", "EFFECT"]
|
|
957
|
+
*
|
|
958
|
+
* // Complex object transformation
|
|
959
|
+
* type User = { id: number; name: string }
|
|
960
|
+
* type UserDisplay = { displayName: string; isActive: boolean }
|
|
961
|
+
*
|
|
962
|
+
* const usersChannel = Channel.fromIterable([
|
|
963
|
+
* { id: 1, name: "Alice" },
|
|
964
|
+
* { id: 2, name: "Bob" }
|
|
965
|
+
* ])
|
|
966
|
+
* const displayChannel = Channel.map(usersChannel, (user): UserDisplay => ({
|
|
967
|
+
* displayName: `User: ${user.name}`,
|
|
968
|
+
* isActive: true
|
|
969
|
+
* }))
|
|
970
|
+
* Effect.runSync(Channel.runCollect(displayChannel)) // => [{ displayName: "User: Alice", isActive: true }, { displayName: "User: Bob", isActive: true }]
|
|
971
|
+
* ```
|
|
972
|
+
*
|
|
973
|
+
* @stability stable
|
|
974
|
+
* @category sequencing
|
|
975
|
+
* @since 2.0.0
|
|
976
|
+
*/
|
|
977
|
+
const map$1 = /*#__PURE__*/ dual(2, (self, f) => transformPull$1(self, (pull) => sync(() => {
|
|
978
|
+
let i = 0;
|
|
979
|
+
return map$3(pull, (o) => f(o, i++));
|
|
980
|
+
})));
|
|
981
|
+
/**
|
|
982
|
+
* Maps the done value of this channel using the specified function.
|
|
983
|
+
*
|
|
984
|
+
* @stability stable
|
|
985
|
+
* @category sequencing
|
|
986
|
+
* @since 4.0.0
|
|
987
|
+
*/
|
|
988
|
+
const mapDone = /*#__PURE__*/ dual(2, (self, f) => mapDoneEffect(self, (o) => succeed$2(f(o))));
|
|
989
|
+
/**
|
|
990
|
+
* Maps the done value of this channel using the specified effectful function.
|
|
991
|
+
*
|
|
992
|
+
* **When to use**
|
|
993
|
+
*
|
|
994
|
+
* Use when the terminal done value transformation needs services or can fail,
|
|
995
|
+
* while emitted elements should pass through unchanged.
|
|
996
|
+
*
|
|
997
|
+
* @stability stable
|
|
998
|
+
* @category sequencing
|
|
999
|
+
* @since 4.0.0
|
|
1000
|
+
*/
|
|
1001
|
+
const mapDoneEffect = /*#__PURE__*/ dual(2, (self, f) => transformPull$1(self, (pull) => succeed$2(catchDone(pull, (done$12) => flatMap$2(f(done$12), done)))));
|
|
1002
|
+
const concurrencyIsSequential = (concurrency) => concurrency === void 0 || concurrency !== "unbounded" && concurrency <= 1;
|
|
1003
|
+
/**
|
|
1004
|
+
* Maps each output element with an effectful function, preserving the source
|
|
1005
|
+
* channel's done value.
|
|
1006
|
+
*
|
|
1007
|
+
* **When to use**
|
|
1008
|
+
*
|
|
1009
|
+
* Use when transforming each channel output needs an Effect, service
|
|
1010
|
+
* dependency, failure channel, or configured concurrency.
|
|
1011
|
+
*
|
|
1012
|
+
* **Details**
|
|
1013
|
+
*
|
|
1014
|
+
* The mapping function receives the output element and its zero-based index.
|
|
1015
|
+
* By default elements are mapped sequentially. Use `options.concurrency` to
|
|
1016
|
+
* map multiple elements concurrently, and `options.unordered` to allow
|
|
1017
|
+
* concurrently mapped outputs to be emitted as soon as they complete.
|
|
1018
|
+
*
|
|
1019
|
+
* **Example** (Mapping channel output with effects)
|
|
1020
|
+
*
|
|
1021
|
+
* ```ts import.meta.vitest
|
|
1022
|
+
* import { Channel, Effect } from "effect"
|
|
1023
|
+
*
|
|
1024
|
+
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
|
1025
|
+
* const processedChannel = Channel.mapEffect(
|
|
1026
|
+
* numbersChannel,
|
|
1027
|
+
* (n) => Effect.succeed(n * n)
|
|
1028
|
+
* )
|
|
1029
|
+
* await Effect.runPromise(Channel.runCollect(processedChannel)) // => [1, 4, 9, 16, 25]
|
|
1030
|
+
* ```
|
|
1031
|
+
*
|
|
1032
|
+
* @stability stable
|
|
1033
|
+
* @category sequencing
|
|
1034
|
+
* @since 2.0.0
|
|
1035
|
+
*/
|
|
1036
|
+
const mapEffect$1 = /*#__PURE__*/ dual((args) => isChannel(args[0]), (self, f, options) => concurrencyIsSequential(options?.concurrency) ? mapEffectSequential(self, f) : mapEffectConcurrent(self, f, options));
|
|
1037
|
+
const mapEffectSequential = (self, f) => fromTransform((upstream, scope) => {
|
|
1038
|
+
let i = 0;
|
|
1039
|
+
return map$3(toTransform(self)(upstream, scope), flatMap$2((o) => f(o, i++)));
|
|
1040
|
+
});
|
|
1041
|
+
const mapEffectConcurrent = (self, f, options) => fromTransformBracket(fnUntraced(function* (upstream, scope, forkedScope) {
|
|
1042
|
+
let i = 0;
|
|
1043
|
+
const pull = yield* toTransform(self)(upstream, scope);
|
|
1044
|
+
const concurrencyN = options.concurrency === "unbounded" ? Number.MAX_SAFE_INTEGER : Math.min(options.concurrency, Number.MAX_SAFE_INTEGER);
|
|
1045
|
+
const queue = yield* bounded(0);
|
|
1046
|
+
yield* addFinalizer(forkedScope, shutdown(queue));
|
|
1047
|
+
const runFork = runForkWith(yield* context());
|
|
1048
|
+
const trackFiber = runIn(forkedScope);
|
|
1049
|
+
if (options.unordered) {
|
|
1050
|
+
const semaphore = makeUnsafe$2(concurrencyN);
|
|
1051
|
+
const release = constant(semaphore.release(1));
|
|
1052
|
+
const handle = matchCauseEffect({
|
|
1053
|
+
onFailure: (cause) => flatMap$2(failCause$3(queue, cause), release),
|
|
1054
|
+
onSuccess: (value) => flatMap$2(offer(queue, value), release)
|
|
1055
|
+
});
|
|
1056
|
+
yield* semaphore.take(1).pipe(flatMap$2(() => pull), flatMap$2((value) => {
|
|
1057
|
+
const index = i++;
|
|
1058
|
+
trackFiber(runFork(handle(suspend$2(() => f(value, index)))));
|
|
1059
|
+
return void_;
|
|
1060
|
+
}), forever({ disableYield: true }), catchCause((cause) => semaphore.withPermits(concurrencyN - 1)(failCause$3(queue, cause))), forkIn(forkedScope));
|
|
1061
|
+
} else {
|
|
1062
|
+
const effects = yield* bounded(concurrencyN - 2);
|
|
1063
|
+
yield* addFinalizer(forkedScope, shutdown(effects));
|
|
1064
|
+
yield* take$1(effects).pipe(flatten$1, flatMap$2((value) => offer(queue, value)), forever({ disableYield: true }), catchCause((cause) => failCause$3(queue, cause)), forkIn(forkedScope));
|
|
1065
|
+
let errorCause;
|
|
1066
|
+
const onExit = (exit) => {
|
|
1067
|
+
if (exit._tag === "Success") return;
|
|
1068
|
+
errorCause = exit.cause;
|
|
1069
|
+
failCauseUnsafe(queue, exit.cause);
|
|
1070
|
+
};
|
|
1071
|
+
yield* pull.pipe(flatMap$2((value) => {
|
|
1072
|
+
if (errorCause) return failCause$1(errorCause);
|
|
1073
|
+
const index = i++;
|
|
1074
|
+
const fiber = runFork(suspend$2(() => f(value, index)));
|
|
1075
|
+
trackFiber(fiber);
|
|
1076
|
+
fiber.addObserver(onExit);
|
|
1077
|
+
return offer(effects, join(fiber));
|
|
1078
|
+
}), forever({ disableYield: true }), catchCause((cause) => offer(effects, failCause$2(cause)).pipe(andThen(failCause$3(effects, cause)))), forkIn(forkedScope));
|
|
1079
|
+
}
|
|
1080
|
+
return take$1(queue);
|
|
1081
|
+
}));
|
|
1082
|
+
/**
|
|
1083
|
+
* Maps each output element to a channel and flattens the child channel
|
|
1084
|
+
* outputs.
|
|
1085
|
+
*
|
|
1086
|
+
* **Details**
|
|
1087
|
+
*
|
|
1088
|
+
* The source channel's done value is preserved. Child channel done values are
|
|
1089
|
+
* used only for child-channel completion. By default child channels are run
|
|
1090
|
+
* sequentially. Use `options.concurrency` and `options.bufferSize` to run child
|
|
1091
|
+
* channels concurrently.
|
|
1092
|
+
*
|
|
1093
|
+
* **Example** (Flat mapping channel output)
|
|
1094
|
+
*
|
|
1095
|
+
* ```ts import.meta.vitest
|
|
1096
|
+
* import { Channel, Data, Effect } from "effect"
|
|
1097
|
+
*
|
|
1098
|
+
* class ProcessError extends Data.TaggedError("ProcessError")<{
|
|
1099
|
+
* readonly cause: string
|
|
1100
|
+
* }> {}
|
|
1101
|
+
*
|
|
1102
|
+
* // Create a channel that outputs numbers
|
|
1103
|
+
* const numberChannel = Channel.fromIterable([1, 2, 3])
|
|
1104
|
+
*
|
|
1105
|
+
* // FlatMap each number to create new channels
|
|
1106
|
+
* const flatMappedChannel = Channel.flatMap(
|
|
1107
|
+
* numberChannel,
|
|
1108
|
+
* (n) =>
|
|
1109
|
+
* Channel.fromIterable(Array.from({ length: n }, (_, i) => `item-${n}-${i}`))
|
|
1110
|
+
* )
|
|
1111
|
+
*
|
|
1112
|
+
* Effect.runSync(Channel.runCollect(flatMappedChannel)) // => ["item-1-0", "item-2-0", "item-2-1", "item-3-0", "item-3-1", "item-3-2"]
|
|
1113
|
+
* ```
|
|
1114
|
+
*
|
|
1115
|
+
* @stability stable
|
|
1116
|
+
* @category sequencing
|
|
1117
|
+
* @since 2.0.0
|
|
1118
|
+
*/
|
|
1119
|
+
const flatMap$1 = /*#__PURE__*/ dual((args) => isChannel(args[0]), (self, f, options) => concurrencyIsSequential(options?.concurrency) ? flatMapSequential(self, f) : flatMapConcurrent(self, f, options));
|
|
1120
|
+
const flatMapSequential = (self, f) => fromTransform((upstream, scope) => map$3(toTransform(self)(upstream, scope), (pull) => {
|
|
1121
|
+
let childPull;
|
|
1122
|
+
let childScope;
|
|
1123
|
+
const makePull = flatMap$2(pull, (value) => {
|
|
1124
|
+
childScope ??= forkUnsafe(scope);
|
|
1125
|
+
return flatMapEager(toTransform(f(value))(upstream, childScope), (pull) => {
|
|
1126
|
+
childPull = catchHalt(pull);
|
|
1127
|
+
return childPull;
|
|
1128
|
+
});
|
|
1129
|
+
});
|
|
1130
|
+
const catchHalt = catchDone((_) => {
|
|
1131
|
+
childPull = void 0;
|
|
1132
|
+
if (childScope.state._tag === "Empty") return makePull;
|
|
1133
|
+
const close$2 = close(childScope, void_$1);
|
|
1134
|
+
childScope = void 0;
|
|
1135
|
+
return flatMap$2(close$2, () => makePull);
|
|
1136
|
+
});
|
|
1137
|
+
return suspend$2(() => childPull ?? makePull);
|
|
1138
|
+
}));
|
|
1139
|
+
const flatMapConcurrent = (self, f, options) => self.pipe(map$1(f), mergeAll(options));
|
|
1140
|
+
/**
|
|
1141
|
+
* Flattens a channel of channels.
|
|
1142
|
+
*
|
|
1143
|
+
* **Example** (Flattening nested channels)
|
|
1144
|
+
*
|
|
1145
|
+
* ```ts import.meta.vitest
|
|
1146
|
+
* import { Channel, Data, Effect } from "effect"
|
|
1147
|
+
*
|
|
1148
|
+
* class FlattenError extends Data.TaggedError("FlattenError")<{
|
|
1149
|
+
* readonly cause: string
|
|
1150
|
+
* }> {}
|
|
1151
|
+
*
|
|
1152
|
+
* // Create a channel that outputs channels
|
|
1153
|
+
* const nestedChannels = Channel.fromIterable([
|
|
1154
|
+
* Channel.fromIterable([1, 2]),
|
|
1155
|
+
* Channel.fromIterable([3, 4]),
|
|
1156
|
+
* Channel.fromIterable([5, 6])
|
|
1157
|
+
* ])
|
|
1158
|
+
*
|
|
1159
|
+
* // Flatten the nested channels
|
|
1160
|
+
* const flattenedChannel = Channel.flatten(nestedChannels)
|
|
1161
|
+
*
|
|
1162
|
+
* Effect.runSync(Channel.runCollect(flattenedChannel)) // => [1, 2, 3, 4, 5, 6]
|
|
1163
|
+
* ```
|
|
1164
|
+
*
|
|
1165
|
+
* @stability stable
|
|
1166
|
+
* @category constructors
|
|
1167
|
+
* @since 2.0.0
|
|
1168
|
+
*/
|
|
1169
|
+
const flatten = (channels) => flatMap$1(channels, identity);
|
|
1170
|
+
/**
|
|
1171
|
+
* Flattens a channel that outputs arrays into a channel that outputs individual elements.
|
|
1172
|
+
*
|
|
1173
|
+
* **Example** (Flattening arrays of channel output)
|
|
1174
|
+
*
|
|
1175
|
+
* ```ts import.meta.vitest
|
|
1176
|
+
* import { Channel, Data, Effect } from "effect"
|
|
1177
|
+
*
|
|
1178
|
+
* class FlattenError extends Data.TaggedError("FlattenError")<{
|
|
1179
|
+
* readonly message: string
|
|
1180
|
+
* }> {}
|
|
1181
|
+
*
|
|
1182
|
+
* // Create a channel that outputs arrays
|
|
1183
|
+
* const arrayChannel = Channel.fromIterable([
|
|
1184
|
+
* [1, 2, 3],
|
|
1185
|
+
* [4, 5],
|
|
1186
|
+
* [6, 7, 8, 9]
|
|
1187
|
+
* ])
|
|
1188
|
+
*
|
|
1189
|
+
* // Flatten the arrays into individual elements
|
|
1190
|
+
* const flattenedChannel = Channel.flattenArray(arrayChannel)
|
|
1191
|
+
*
|
|
1192
|
+
* Effect.runSync(Channel.runCollect(flattenedChannel)) // => [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
1193
|
+
* ```
|
|
1194
|
+
*
|
|
1195
|
+
* @stability stable
|
|
1196
|
+
* @category transforming
|
|
1197
|
+
* @since 4.0.0
|
|
1198
|
+
*/
|
|
1199
|
+
const flattenArray = (self) => transformPull$1(self, (pull) => {
|
|
1200
|
+
let array;
|
|
1201
|
+
let index = 0;
|
|
1202
|
+
const pump = suspend$2(function loop() {
|
|
1203
|
+
if (array === void 0) return flatMap$2(pull, (array_) => {
|
|
1204
|
+
switch (array_.length) {
|
|
1205
|
+
case 0: return loop();
|
|
1206
|
+
case 1: return succeed$2(array_[0]);
|
|
1207
|
+
default:
|
|
1208
|
+
array = array_;
|
|
1209
|
+
return succeed$2(array_[index++]);
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
const next = array[index++];
|
|
1213
|
+
if (index >= array.length) {
|
|
1214
|
+
array = void 0;
|
|
1215
|
+
index = 0;
|
|
1216
|
+
}
|
|
1217
|
+
return succeed$2(next);
|
|
1218
|
+
});
|
|
1219
|
+
return succeed$2(pump);
|
|
1220
|
+
});
|
|
1221
|
+
/**
|
|
1222
|
+
* Creates a new channel that consumes all output from the source channel
|
|
1223
|
+
* but emits nothing, preserving only the completion value.
|
|
1224
|
+
*
|
|
1225
|
+
* **Example** (Draining channel output)
|
|
1226
|
+
*
|
|
1227
|
+
* ```ts import.meta.vitest
|
|
1228
|
+
* import { Channel, Effect } from "effect"
|
|
1229
|
+
*
|
|
1230
|
+
* // Create a channel that outputs values
|
|
1231
|
+
* const sourceChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
|
1232
|
+
*
|
|
1233
|
+
* // Drain all output, keeping only the completion
|
|
1234
|
+
* const drainedChannel = Channel.drain(sourceChannel)
|
|
1235
|
+
*
|
|
1236
|
+
* Effect.runSync(Channel.runCollect(drainedChannel)) // => []
|
|
1237
|
+
* ```
|
|
1238
|
+
*
|
|
1239
|
+
* @stability stable
|
|
1240
|
+
* @category constructors
|
|
1241
|
+
* @since 2.0.0
|
|
1242
|
+
*/
|
|
1243
|
+
const drain$1 = (self) => transformPull$1(self, (pull) => succeed$2(forever(pull, { disableYield: true })));
|
|
1244
|
+
const repeatLoop = (self, onExit) => fromTransform((upstream, scope) => sync(() => {
|
|
1245
|
+
let currentPull;
|
|
1246
|
+
const makePull = () => {
|
|
1247
|
+
const runScope = forkUnsafe(scope);
|
|
1248
|
+
return flatMap$2(toTransform(self)(upstream, runScope), (pull) => {
|
|
1249
|
+
currentPull = catchCause(pull, (cause) => {
|
|
1250
|
+
const exit = doneExitFromCause(cause);
|
|
1251
|
+
return close(runScope, exit).pipe(flatMap$2(() => onExit(exit, scope)), flatMap$2(makePull));
|
|
1252
|
+
});
|
|
1253
|
+
return currentPull;
|
|
1254
|
+
});
|
|
1255
|
+
};
|
|
1256
|
+
return suspend$2(() => currentPull ?? makePull());
|
|
1257
|
+
}));
|
|
1258
|
+
/**
|
|
1259
|
+
* Filters arrays of elements emitted by a channel, applying the filter
|
|
1260
|
+
* to each element within the arrays and only emitting non-empty filtered arrays.
|
|
1261
|
+
*
|
|
1262
|
+
* **Example** (Filtering array output)
|
|
1263
|
+
*
|
|
1264
|
+
* ```ts import.meta.vitest
|
|
1265
|
+
* import { Array, Channel, Effect } from "effect"
|
|
1266
|
+
*
|
|
1267
|
+
* const nonEmptyArrayPredicate = Array.isReadonlyArrayNonEmpty
|
|
1268
|
+
*
|
|
1269
|
+
* // Create a channel that outputs arrays of mixed data
|
|
1270
|
+
* const arrayChannel = Channel.fromIterable([
|
|
1271
|
+
* Array.make(1, 2, 3, 4, 5),
|
|
1272
|
+
* Array.make(6, 7, 8, 9, 10),
|
|
1273
|
+
* Array.make(11, 12, 13, 14, 15)
|
|
1274
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
1275
|
+
*
|
|
1276
|
+
* // Filter arrays to keep only even numbers
|
|
1277
|
+
* const evenArraysChannel = Channel.filterArray(arrayChannel, (n) => n % 2 === 0)
|
|
1278
|
+
* Effect.runSync(Channel.runCollect(evenArraysChannel)) // => [[2, 4], [6, 8, 10], [12, 14]]
|
|
1279
|
+
* // Note: Only non-empty filtered arrays are emitted
|
|
1280
|
+
*
|
|
1281
|
+
* // Arrays that would become empty after filtering are discarded entirely
|
|
1282
|
+
* const oddChannel = Channel.fromIterable([
|
|
1283
|
+
* Array.make(1, 3, 5),
|
|
1284
|
+
* Array.make(2, 4),
|
|
1285
|
+
* Array.make(7, 9)
|
|
1286
|
+
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
|
1287
|
+
* const filteredOddChannel = Channel.filterArray(oddChannel, (n) => n % 2 === 0)
|
|
1288
|
+
* Effect.runSync(Channel.runCollect(filteredOddChannel)) // => [[2, 4]]
|
|
1289
|
+
* ```
|
|
1290
|
+
*
|
|
1291
|
+
* @stability stable
|
|
1292
|
+
* @category filtering
|
|
1293
|
+
* @since 4.0.0
|
|
1294
|
+
*/
|
|
1295
|
+
const filterArray = /*#__PURE__*/ dual(2, (self, predicate) => transformPull$1(self, (pull) => succeed$2(flatMap$2(pull, function loop(arr) {
|
|
1296
|
+
const passes = [];
|
|
1297
|
+
for (let i = 0; i < arr.length; i++) if (predicate(arr[i])) passes.push(arr[i]);
|
|
1298
|
+
return isReadonlyArrayNonEmpty(passes) ? succeed$2(passes) : flatMap$2(pull, loop);
|
|
1299
|
+
}))));
|
|
1300
|
+
/**
|
|
1301
|
+
* Maps over a channel statefully with an accumulator, where each element can produce multiple output values.
|
|
1302
|
+
*
|
|
1303
|
+
* **Example** (Mapping with accumulated state)
|
|
1304
|
+
*
|
|
1305
|
+
* ```ts import.meta.vitest
|
|
1306
|
+
* import { Channel, Effect } from "effect"
|
|
1307
|
+
*
|
|
1308
|
+
* // Create a channel with numbers
|
|
1309
|
+
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4])
|
|
1310
|
+
*
|
|
1311
|
+
* // Use mapAccum to create running sums and emit both current and sum
|
|
1312
|
+
* const runningSum = Channel.mapAccum(
|
|
1313
|
+
* numbersChannel,
|
|
1314
|
+
* () => 0, // initial accumulator state
|
|
1315
|
+
* (sum, current) => {
|
|
1316
|
+
* const newSum = sum + current
|
|
1317
|
+
* // Return [newState, outputValues]
|
|
1318
|
+
* return [newSum, [current, newSum]] as const
|
|
1319
|
+
* }
|
|
1320
|
+
* )
|
|
1321
|
+
* // Using with Effect for async processing
|
|
1322
|
+
* const asyncMapAccum = Channel.mapAccum(
|
|
1323
|
+
* numbersChannel,
|
|
1324
|
+
* () => "",
|
|
1325
|
+
* (acc, value) =>
|
|
1326
|
+
* Effect.gen(function*() {
|
|
1327
|
+
* const newAcc = acc + value.toString()
|
|
1328
|
+
* return [newAcc, [`${value}-processed`, newAcc]] as const
|
|
1329
|
+
* })
|
|
1330
|
+
* )
|
|
1331
|
+
* Effect.runSync(Channel.runCollect(runningSum)) // => [1, 1, 2, 3, 3, 6, 4, 10]
|
|
1332
|
+
* Effect.runSync(Channel.runCollect(asyncMapAccum)) // => ["1-processed", "1", "2-processed", "12", "3-processed", "123", "4-processed", "1234"]
|
|
1333
|
+
* ```
|
|
1334
|
+
*
|
|
1335
|
+
* @stability stable
|
|
1336
|
+
* @category sequencing
|
|
1337
|
+
* @since 4.0.0
|
|
1338
|
+
*/
|
|
1339
|
+
const mapAccum$1 = /*#__PURE__*/ dual((args) => isChannel(args[0]), (self, initial, f, options) => fromTransform((upstream, scope) => map$3(toTransform(self)(upstream, scope), (pull) => {
|
|
1340
|
+
let state = initial();
|
|
1341
|
+
let current;
|
|
1342
|
+
let index = 0;
|
|
1343
|
+
let cause;
|
|
1344
|
+
const pullNext = matchCauseEffect(pull, {
|
|
1345
|
+
onFailure(cause_) {
|
|
1346
|
+
cause = cause_;
|
|
1347
|
+
const b = options?.onHalt && options.onHalt(state);
|
|
1348
|
+
return b && b.length > 0 ? succeed$2([state, b]) : failCause$1(cause_);
|
|
1349
|
+
},
|
|
1350
|
+
onSuccess(a) {
|
|
1351
|
+
const b = f(state, a);
|
|
1352
|
+
return isArray(b) ? succeed$2(b) : b;
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
return suspend$2(function loop() {
|
|
1356
|
+
if (current === void 0) {
|
|
1357
|
+
if (cause) return failCause$1(cause);
|
|
1358
|
+
return flatMap$2(pullNext, ([newState, values]) => {
|
|
1359
|
+
state = newState;
|
|
1360
|
+
if (values.length === 0) return loop();
|
|
1361
|
+
else if (values.length === 1) return succeed$2(values[0]);
|
|
1362
|
+
current = values;
|
|
1363
|
+
return loop();
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
const next = current[index++];
|
|
1367
|
+
if (index >= current.length) {
|
|
1368
|
+
current = void 0;
|
|
1369
|
+
index = 0;
|
|
1370
|
+
}
|
|
1371
|
+
return succeed$2(next);
|
|
1372
|
+
});
|
|
1373
|
+
})));
|
|
1374
|
+
/**
|
|
1375
|
+
* Runs an effect when the channel fails with a typed error, then preserves the
|
|
1376
|
+
* original channel failure.
|
|
1377
|
+
*
|
|
1378
|
+
* **Details**
|
|
1379
|
+
*
|
|
1380
|
+
* The effect is not run for normal channel completion. If the observer effect
|
|
1381
|
+
* fails, that failure can fail the returned channel.
|
|
1382
|
+
*
|
|
1383
|
+
* @stability stable
|
|
1384
|
+
* @category error handling
|
|
1385
|
+
* @since 4.0.0
|
|
1386
|
+
*/
|
|
1387
|
+
const tapError$1 = /*#__PURE__*/ dual(2, (self, f) => transformPull$1(self, (pull) => succeed$2(tapError$2(pull, (err) => isDone(err) ? void_ : asVoid(f(err))))));
|
|
1388
|
+
/**
|
|
1389
|
+
* Transforms the typed errors of a channel using the specified function.
|
|
1390
|
+
*
|
|
1391
|
+
* **Details**
|
|
1392
|
+
*
|
|
1393
|
+
* Maps every typed error in the cause, preserving defects, interrupts and
|
|
1394
|
+
* reason order. Unlike typed recovery, mapping does not convert errors to defects.
|
|
1395
|
+
*
|
|
1396
|
+
* @stability stable
|
|
1397
|
+
* @category error handling
|
|
1398
|
+
* @since 2.0.0
|
|
1399
|
+
*/
|
|
1400
|
+
const mapError$1 = /*#__PURE__*/ dual(2, (self, f) => transformPull$1(self, (pull) => succeed$2(mapError$2(pull, (err) => isDone(err) ? err : f(err)))));
|
|
1401
|
+
/**
|
|
1402
|
+
* Returns a new channel that retries this channel according to the specified
|
|
1403
|
+
* schedule whenever it fails.
|
|
1404
|
+
*
|
|
1405
|
+
* **Gotchas**
|
|
1406
|
+
*
|
|
1407
|
+
* Defects take precedence over typed errors. A failure whose cause contains a
|
|
1408
|
+
* defect is not retried and is propagated with every typed error converted to a
|
|
1409
|
+
* defect. A typed error alongside an interruption is retried.
|
|
1410
|
+
*
|
|
1411
|
+
* @stability stable
|
|
1412
|
+
* @category error handling
|
|
1413
|
+
* @since 4.0.0
|
|
1414
|
+
*/
|
|
1415
|
+
const retry$1 = /*#__PURE__*/ dual(2, (self, schedule) => suspend$1(() => {
|
|
1416
|
+
let step = void 0;
|
|
1417
|
+
let meta = CurrentMetadata.defaultValue();
|
|
1418
|
+
const selfWithMeta = provideServiceEffect(self, CurrentMetadata, sync(() => meta));
|
|
1419
|
+
const withReset = onFirst(selfWithMeta, () => {
|
|
1420
|
+
step = void 0;
|
|
1421
|
+
return void_;
|
|
1422
|
+
});
|
|
1423
|
+
const resolvedSchedule = typeof schedule === "function" ? schedule(identity) : schedule;
|
|
1424
|
+
return repeatLoop(withReset, (exit, scope) => {
|
|
1425
|
+
if (isSuccess$1(exit)) return done(exit.value);
|
|
1426
|
+
const error = findRecoverableError(exit.cause);
|
|
1427
|
+
if (isFailure(error)) return failCause$1(error.failure);
|
|
1428
|
+
return gen(function* () {
|
|
1429
|
+
if (!step) step = yield* toStepWithMetadata(resolvedSchedule);
|
|
1430
|
+
meta = yield* step(error.success);
|
|
1431
|
+
}).pipe(catchDone(() => fail$2(error.success)), provide(scope));
|
|
1432
|
+
});
|
|
1433
|
+
}));
|
|
1434
|
+
/**
|
|
1435
|
+
* Merges multiple channels with specified concurrency and buffering options.
|
|
1436
|
+
*
|
|
1437
|
+
* **When to use**
|
|
1438
|
+
*
|
|
1439
|
+
* Use when channel outputs are themselves channels and multiple inner channels
|
|
1440
|
+
* should run with configured concurrency and buffering.
|
|
1441
|
+
*
|
|
1442
|
+
* **Example** (Merging nested channels)
|
|
1443
|
+
*
|
|
1444
|
+
* ```ts import.meta.vitest
|
|
1445
|
+
* import { Channel, Data, Effect } from "effect"
|
|
1446
|
+
*
|
|
1447
|
+
* class MergeAllError extends Data.TaggedError("MergeAllError")<{
|
|
1448
|
+
* readonly reason: string
|
|
1449
|
+
* }> {}
|
|
1450
|
+
*
|
|
1451
|
+
* // Create channels that output other channels
|
|
1452
|
+
* const nestedChannels = Channel.fromIterable([
|
|
1453
|
+
* Channel.fromIterable([1, 2]),
|
|
1454
|
+
* Channel.fromIterable([3, 4]),
|
|
1455
|
+
* Channel.fromIterable([5, 6])
|
|
1456
|
+
* ])
|
|
1457
|
+
*
|
|
1458
|
+
* // Merge all channels with bounded concurrency
|
|
1459
|
+
* const mergedChannel = Channel.mergeAll({
|
|
1460
|
+
* concurrency: 1,
|
|
1461
|
+
* bufferSize: 16
|
|
1462
|
+
* })(nestedChannels)
|
|
1463
|
+
*
|
|
1464
|
+
* await Effect.runPromise(Channel.runCollect(mergedChannel)) // => [1, 2, 3, 4, 5, 6]
|
|
1465
|
+
* ```
|
|
1466
|
+
*
|
|
1467
|
+
* @stability stable
|
|
1468
|
+
* @category combining
|
|
1469
|
+
* @since 2.0.0
|
|
1470
|
+
*/
|
|
1471
|
+
const mergeAll = /*#__PURE__*/ dual(2, (channels, { bufferSize = 16, concurrency, switch: switch_ = false }) => fromTransformBracket(fnUntraced(function* (upstream, scope, forkedScope) {
|
|
1472
|
+
const concurrencyN = concurrency === "unbounded" ? Number.MAX_SAFE_INTEGER : Math.max(1, concurrency);
|
|
1473
|
+
const semaphore = switch_ ? void 0 : makeUnsafe$2(concurrencyN);
|
|
1474
|
+
const doneLatch = yield* make$1(true);
|
|
1475
|
+
const fibers = /* @__PURE__ */ new Map();
|
|
1476
|
+
const queue = yield* bounded(bufferSize);
|
|
1477
|
+
yield* addFinalizer(forkedScope, shutdown(queue));
|
|
1478
|
+
const pull = yield* toTransform(channels)(upstream, scope);
|
|
1479
|
+
yield* gen(function* () {
|
|
1480
|
+
while (true) {
|
|
1481
|
+
let pullFiber;
|
|
1482
|
+
if (semaphore) {
|
|
1483
|
+
if (fibers.size < concurrencyN) yield* semaphore.take(1);
|
|
1484
|
+
else {
|
|
1485
|
+
pullFiber = yield* forkChild(pull);
|
|
1486
|
+
yield* raceFirst(semaphore.take(1), andThen(join(pullFiber), never));
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
const channel = pullFiber === void 0 ? yield* pull : yield* join(pullFiber);
|
|
1490
|
+
const childScope = forkUnsafe(forkedScope);
|
|
1491
|
+
const childPull = yield* toTransform(channel)(upstream, childScope);
|
|
1492
|
+
while (fibers.size >= concurrencyN) {
|
|
1493
|
+
const [fiber, fiberScope] = headUnsafe(fibers);
|
|
1494
|
+
fibers.delete(fiber);
|
|
1495
|
+
if (fibers.size === 0) yield* doneLatch.open;
|
|
1496
|
+
yield* interrupt(fiber);
|
|
1497
|
+
yield* ignoreCause(close(fiberScope, interrupt$1()));
|
|
1498
|
+
}
|
|
1499
|
+
const fiber = yield* childPull.pipe(tap$1(() => yieldNow), flatMap$2((value) => offer(queue, value)), forever({ disableYield: true }), onError(fnUntraced(function* (cause) {
|
|
1500
|
+
const exit$1 = doneExitFromCause(cause);
|
|
1501
|
+
const closeExit = yield* exit(close(childScope, exit$1));
|
|
1502
|
+
if (!fibers.has(fiber)) return;
|
|
1503
|
+
const failure = asVoidAll([exit$1, closeExit]);
|
|
1504
|
+
if (isFailure$1(failure)) yield* failCause$3(queue, failure.cause);
|
|
1505
|
+
fibers.delete(fiber);
|
|
1506
|
+
if (semaphore) yield* semaphore.release(1);
|
|
1507
|
+
if (fibers.size === 0) yield* doneLatch.open;
|
|
1508
|
+
})), forkChild);
|
|
1509
|
+
doneLatch.closeUnsafe();
|
|
1510
|
+
fibers.set(fiber, childScope);
|
|
1511
|
+
}
|
|
1512
|
+
}).pipe(catchCause((cause) => {
|
|
1513
|
+
const halt = filterDone(cause);
|
|
1514
|
+
if (isSuccess(halt)) return doneLatch.whenOpen(failCause$3(queue, cause));
|
|
1515
|
+
return failCause$3(queue, cause);
|
|
1516
|
+
}), forkIn(forkedScope));
|
|
1517
|
+
return take$1(queue);
|
|
1518
|
+
})));
|
|
1519
|
+
/**
|
|
1520
|
+
* Returns a new channel, which is the merge of this channel and the specified
|
|
1521
|
+
* channel.
|
|
1522
|
+
*
|
|
1523
|
+
* **Example** (Merging channels)
|
|
1524
|
+
*
|
|
1525
|
+
* ```ts import.meta.vitest
|
|
1526
|
+
* import { Channel, Effect } from "effect"
|
|
1527
|
+
*
|
|
1528
|
+
* // Create two channels
|
|
1529
|
+
* const leftChannel = Channel.fromIterable([1, 2, 3])
|
|
1530
|
+
* const rightChannel = Channel.fromIterable(["a", "b", "c"])
|
|
1531
|
+
*
|
|
1532
|
+
* // The default "both" strategy waits for both channels to complete
|
|
1533
|
+
* const mergedChannel = Channel.merge(leftChannel, rightChannel)
|
|
1534
|
+
*
|
|
1535
|
+
* const values = await Effect.runPromise(Channel.runCollect(mergedChannel))
|
|
1536
|
+
* values.map(String).sort() // => ["1", "2", "3", "a", "b", "c"]
|
|
1537
|
+
* ```
|
|
1538
|
+
*
|
|
1539
|
+
* @stability stable
|
|
1540
|
+
* @category combining
|
|
1541
|
+
* @since 4.0.0
|
|
1542
|
+
*/
|
|
1543
|
+
const merge$1 = /*#__PURE__*/ dual((args) => isChannel(args[0]) && isChannel(args[1]), (left, right, options) => fromTransformBracket(fnUntraced(function* (upstream, _scope, forkedScope) {
|
|
1544
|
+
const strategy = options?.haltStrategy ?? "both";
|
|
1545
|
+
const queue = yield* bounded(0);
|
|
1546
|
+
yield* addFinalizer(forkedScope, shutdown(queue));
|
|
1547
|
+
let done = 0;
|
|
1548
|
+
function onExit(side, cause) {
|
|
1549
|
+
done++;
|
|
1550
|
+
if (!isDoneCause(cause)) return failCause$3(queue, cause);
|
|
1551
|
+
switch (strategy) {
|
|
1552
|
+
case "both": return done === 2 ? failCause$3(queue, cause) : void_;
|
|
1553
|
+
case "left":
|
|
1554
|
+
case "right": return side === strategy ? failCause$3(queue, cause) : void_;
|
|
1555
|
+
case "either": return failCause$3(queue, cause);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
const runSide = (side, channel, scope) => toTransform(channel)(upstream, scope).pipe(flatMap$2((pull) => pull.pipe(flatMap$2((value) => offer(queue, value)), forever)), onError((cause) => onExitPrimitive(doneExitFromCause(cause), (exit) => close(scope, exit)).pipe(matchCauseEffect({
|
|
1559
|
+
onFailure: (cause) => onExit(side, cause),
|
|
1560
|
+
onSuccess: () => onExit(side, cause)
|
|
1561
|
+
}))), forkIn(forkedScope));
|
|
1562
|
+
yield* runSide("left", left, forkUnsafe(forkedScope));
|
|
1563
|
+
yield* runSide("right", right, forkUnsafe(forkedScope));
|
|
1564
|
+
return take$1(queue);
|
|
1565
|
+
})));
|
|
1566
|
+
/**
|
|
1567
|
+
* Runs an effect concurrently with a channel while emitting only the channel's
|
|
1568
|
+
* output elements.
|
|
1569
|
+
*
|
|
1570
|
+
* **When to use**
|
|
1571
|
+
*
|
|
1572
|
+
* Use when a side effect should run for the lifetime of a channel and only the
|
|
1573
|
+
* channel's output elements should be emitted.
|
|
1574
|
+
*
|
|
1575
|
+
* **Details**
|
|
1576
|
+
*
|
|
1577
|
+
* The effect's successful value is ignored. If the effect fails while the
|
|
1578
|
+
* channel is running, the returned channel fails with that error.
|
|
1579
|
+
*
|
|
1580
|
+
* @stability stable
|
|
1581
|
+
* @category combining
|
|
1582
|
+
* @since 4.0.0
|
|
1583
|
+
*/
|
|
1584
|
+
const mergeEffect$1 = /*#__PURE__*/ dual(2, (self, effect) => merge$1(self, fromEffectDrain(effect), { haltStrategy: "left" }));
|
|
1585
|
+
/**
|
|
1586
|
+
* Splits upstream string chunks into lines, recognizing `\n`, `\r\n`, and
|
|
1587
|
+
* standalone `\r` as line terminators. The behavior matches
|
|
1588
|
+
* `String.linesIterator` regardless of how the input is chunked.
|
|
1589
|
+
*
|
|
1590
|
+
* **Details**
|
|
1591
|
+
*
|
|
1592
|
+
* A line terminator at the very end of the stream does **not** produce a
|
|
1593
|
+
* trailing empty line (consistent with `String.linesIterator`). Conversely,
|
|
1594
|
+
* if the stream ends without a terminator the final partial line is still
|
|
1595
|
+
* emitted.
|
|
1596
|
+
*
|
|
1597
|
+
* **Example** (Splitting string chunks into lines)
|
|
1598
|
+
*
|
|
1599
|
+
* ```ts import.meta.vitest
|
|
1600
|
+
* import { Effect, Stream } from "effect"
|
|
1601
|
+
*
|
|
1602
|
+
* const result = await Effect.runPromise(Stream.runCollect(
|
|
1603
|
+
* Stream.splitLines(Stream.make("hel", "lo\r\nwor", "ld\n"))
|
|
1604
|
+
* ))
|
|
1605
|
+
* result // => ["hello", "world"]
|
|
1606
|
+
* ```
|
|
1607
|
+
*
|
|
1608
|
+
* @stability stable
|
|
1609
|
+
* @category splitting
|
|
1610
|
+
* @since 2.0.0
|
|
1611
|
+
*/
|
|
1612
|
+
const splitLines$1 = () => fromTransform((upstream, _scope) => sync(() => {
|
|
1613
|
+
let stringBuilder = "";
|
|
1614
|
+
let midCRLF = false;
|
|
1615
|
+
let done$10 = none();
|
|
1616
|
+
function splitLinesArray(chunk) {
|
|
1617
|
+
const chunkBuilder = [];
|
|
1618
|
+
function pushLine(segment) {
|
|
1619
|
+
if (stringBuilder.length === 0) chunkBuilder.push(segment);
|
|
1620
|
+
else {
|
|
1621
|
+
chunkBuilder.push(stringBuilder + segment);
|
|
1622
|
+
stringBuilder = "";
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
1626
|
+
const str = chunk[i];
|
|
1627
|
+
if (str.length !== 0) {
|
|
1628
|
+
let from = 0;
|
|
1629
|
+
let indexOfCR = str.indexOf("\r");
|
|
1630
|
+
let indexOfLF = str.indexOf("\n");
|
|
1631
|
+
if (midCRLF) {
|
|
1632
|
+
if (indexOfLF === 0) {
|
|
1633
|
+
from = 1;
|
|
1634
|
+
indexOfLF = str.indexOf("\n", from);
|
|
1635
|
+
}
|
|
1636
|
+
midCRLF = false;
|
|
1637
|
+
}
|
|
1638
|
+
while (indexOfCR !== -1 || indexOfLF !== -1) if (indexOfCR === -1 || indexOfLF !== -1 && indexOfLF < indexOfCR) {
|
|
1639
|
+
pushLine(str.substring(from, indexOfLF));
|
|
1640
|
+
from = indexOfLF + 1;
|
|
1641
|
+
indexOfLF = str.indexOf("\n", from);
|
|
1642
|
+
} else {
|
|
1643
|
+
pushLine(str.substring(from, indexOfCR));
|
|
1644
|
+
if (str.length === indexOfCR + 1) {
|
|
1645
|
+
midCRLF = true;
|
|
1646
|
+
from = str.length;
|
|
1647
|
+
indexOfCR = -1;
|
|
1648
|
+
} else {
|
|
1649
|
+
from = indexOfCR + (indexOfLF === indexOfCR + 1 ? 2 : 1);
|
|
1650
|
+
indexOfCR = str.indexOf("\r", from);
|
|
1651
|
+
indexOfLF = str.indexOf("\n", from);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
stringBuilder = stringBuilder + str.substring(from);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
return isReadonlyArrayNonEmpty(chunkBuilder) ? chunkBuilder : null;
|
|
1658
|
+
}
|
|
1659
|
+
const pullOrFlush = suspend$2(() => {
|
|
1660
|
+
if (done$10._tag === "Some") return done(done$10.value);
|
|
1661
|
+
return matchEffect(upstream, {
|
|
1662
|
+
onSuccess: loop,
|
|
1663
|
+
onFailure: failCause$1,
|
|
1664
|
+
onDone: (leftover) => {
|
|
1665
|
+
done$10 = some(leftover);
|
|
1666
|
+
if (stringBuilder.length > 0) {
|
|
1667
|
+
const last = stringBuilder;
|
|
1668
|
+
stringBuilder = "";
|
|
1669
|
+
midCRLF = false;
|
|
1670
|
+
return succeed$2([last]);
|
|
1671
|
+
}
|
|
1672
|
+
return done(leftover);
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
});
|
|
1676
|
+
function loop(chunk) {
|
|
1677
|
+
const lines = splitLinesArray(chunk);
|
|
1678
|
+
return lines !== null ? succeed$2(lines) : pullOrFlush;
|
|
1679
|
+
}
|
|
1680
|
+
return pullOrFlush;
|
|
1681
|
+
}));
|
|
1682
|
+
/**
|
|
1683
|
+
* Decodes incoming `Uint8Array` chunks into strings using `TextDecoder`.
|
|
1684
|
+
*
|
|
1685
|
+
* **Details**
|
|
1686
|
+
*
|
|
1687
|
+
* Input chunks are decoded with streaming enabled so multi-byte characters may
|
|
1688
|
+
* span `Uint8Array` boundaries. On normal completion, incomplete trailing
|
|
1689
|
+
* characters become U+FFFD or, with `fatal: true`, a defect. The optional
|
|
1690
|
+
* `encoding` and `options` are passed to `TextDecoder`.
|
|
1691
|
+
*
|
|
1692
|
+
* @stability stable
|
|
1693
|
+
* @category decoding
|
|
1694
|
+
* @since 4.0.0
|
|
1695
|
+
*/
|
|
1696
|
+
const decodeText$1 = (encoding, options) => fromTransform((upstream, _scope) => sync(() => {
|
|
1697
|
+
const decoder = new TextDecoder(encoding, options);
|
|
1698
|
+
const streamOptions = { stream: true };
|
|
1699
|
+
let pull = upstream.pipe(map$3(map$2((bytes) => decoder.decode(bytes, streamOptions))), catchDone((leftover) => {
|
|
1700
|
+
pull = done(leftover);
|
|
1701
|
+
const last = decoder.decode();
|
|
1702
|
+
return last.length > 0 ? succeed$2(of(last)) : pull;
|
|
1703
|
+
}));
|
|
1704
|
+
return suspend$2(() => pull);
|
|
1705
|
+
}));
|
|
1706
|
+
/**
|
|
1707
|
+
* Returns a new channel that pipes the output of this channel into the
|
|
1708
|
+
* specified channel. The returned channel has the input type of this channel,
|
|
1709
|
+
* and the output type of the specified channel, terminating with the value of
|
|
1710
|
+
* the specified channel.
|
|
1711
|
+
*
|
|
1712
|
+
* **Example** (Piping one channel into another)
|
|
1713
|
+
*
|
|
1714
|
+
* ```ts import.meta.vitest
|
|
1715
|
+
* import { Channel, Data, Effect } from "effect"
|
|
1716
|
+
*
|
|
1717
|
+
* class PipeError extends Data.TaggedError("PipeError")<{
|
|
1718
|
+
* readonly stage: string
|
|
1719
|
+
* }> {}
|
|
1720
|
+
*
|
|
1721
|
+
* // Create source and transform channels
|
|
1722
|
+
* const sourceChannel = Channel.fromIterable([1, 2, 3])
|
|
1723
|
+
* const transformChannel = Channel.map(sourceChannel, (n: number) => n * 2)
|
|
1724
|
+
*
|
|
1725
|
+
* // Pipe the source into the transform
|
|
1726
|
+
* const pipedChannel = Channel.pipeTo(sourceChannel, transformChannel)
|
|
1727
|
+
*
|
|
1728
|
+
* Effect.runSync(Channel.runCollect(pipedChannel)) // => [2, 4, 6]
|
|
1729
|
+
* ```
|
|
1730
|
+
*
|
|
1731
|
+
* @stability stable
|
|
1732
|
+
* @category sequencing
|
|
1733
|
+
* @since 2.0.0
|
|
1734
|
+
*/
|
|
1735
|
+
const pipeTo = /*#__PURE__*/ dual(2, (self, that) => fromTransform((upstream, scope) => flatMap$2(toTransform(self)(upstream, scope), (upstream) => toTransform(that)(upstream, scope))));
|
|
1736
|
+
const isUpstreamFailure = (reason) => reason._tag === "Die" && isDone(reason.defect) && isCause(reason.defect.value);
|
|
1737
|
+
/** @internal */
|
|
1738
|
+
const hasUpstreamFailure = (cause) => cause.reasons.some(isUpstreamFailure);
|
|
1739
|
+
/** @internal */
|
|
1740
|
+
const upstreamFailureToDefect = (pull) => catchCause(pull, (cause) => isDoneCause(cause) ? failCause$1(cause) : die$3(Done(cause)));
|
|
1741
|
+
/** @internal */
|
|
1742
|
+
const unwrapUpstreamFailure = (cause) => hasUpstreamFailure(cause) ? fromReasons(cause.reasons.flatMap((reason) => isUpstreamFailure(reason) ? reason.defect.value.reasons : [reason])) : cause;
|
|
1743
|
+
/**
|
|
1744
|
+
* The type parameters are explicit because the restored error type includes
|
|
1745
|
+
* the upstream error, which is not part of the input effect's type.
|
|
1746
|
+
*
|
|
1747
|
+
* @internal
|
|
1748
|
+
*/
|
|
1749
|
+
const restoreUpstreamFailure = (effect) => catchCause(effect, (cause) => failCause$1(unwrapUpstreamFailure(cause)));
|
|
1750
|
+
/**
|
|
1751
|
+
* Constructs a `Channel` from a scoped effect that will result in a
|
|
1752
|
+
* `Channel` if successful.
|
|
1753
|
+
*
|
|
1754
|
+
* **Example** (Unwrapping channel effects)
|
|
1755
|
+
*
|
|
1756
|
+
* ```ts import.meta.vitest
|
|
1757
|
+
* import { Channel, Data, Effect } from "effect"
|
|
1758
|
+
*
|
|
1759
|
+
* class UnwrapError extends Data.TaggedError("UnwrapError")<{
|
|
1760
|
+
* readonly reason: string
|
|
1761
|
+
* }> {}
|
|
1762
|
+
*
|
|
1763
|
+
* // Create an effect that produces a channel
|
|
1764
|
+
* const channelEffect = Effect.succeed(
|
|
1765
|
+
* Channel.fromIterable([1, 2, 3])
|
|
1766
|
+
* )
|
|
1767
|
+
*
|
|
1768
|
+
* // Unwrap the effect to get the channel
|
|
1769
|
+
* const unwrappedChannel = Channel.unwrap(channelEffect)
|
|
1770
|
+
*
|
|
1771
|
+
* Effect.runSync(Channel.runCollect(unwrappedChannel)) // => [1, 2, 3]
|
|
1772
|
+
* ```
|
|
1773
|
+
*
|
|
1774
|
+
* @stability stable
|
|
1775
|
+
* @category constructors
|
|
1776
|
+
* @since 2.0.0
|
|
1777
|
+
*/
|
|
1778
|
+
const unwrap$1 = (channel) => fromTransform((upstream, scope) => {
|
|
1779
|
+
let pull;
|
|
1780
|
+
return succeed$2(suspend$2(() => {
|
|
1781
|
+
if (pull) return pull;
|
|
1782
|
+
return channel.pipe(provide(scope), flatMap$2((channel) => toTransform(channel)(upstream, scope)), flatMap$2((pull_) => pull = pull_));
|
|
1783
|
+
}));
|
|
1784
|
+
});
|
|
1785
|
+
/**
|
|
1786
|
+
* Buffers array output elements in a queue with the configured `capacity` so a
|
|
1787
|
+
* faster producer can progress independently of a slower consumer.
|
|
1788
|
+
*
|
|
1789
|
+
* **When to use**
|
|
1790
|
+
*
|
|
1791
|
+
* Use when emitted arrays are batches of elements and it is acceptable for
|
|
1792
|
+
* buffering to flatten and rebuild those batches.
|
|
1793
|
+
*
|
|
1794
|
+
* **Details**
|
|
1795
|
+
*
|
|
1796
|
+
* Finite queues use the `strategy` option. The default `"suspend"` strategy
|
|
1797
|
+
* applies backpressure, while `"dropping"` and `"sliding"` can discard output
|
|
1798
|
+
* elements when the queue is full. `"unbounded"` capacity does not use a finite
|
|
1799
|
+
* capacity strategy.
|
|
1800
|
+
*
|
|
1801
|
+
* **Gotchas**
|
|
1802
|
+
*
|
|
1803
|
+
* Input arrays are offered to the queue element-by-element and outputs are
|
|
1804
|
+
* rebuilt from the currently available queued elements, so upstream array
|
|
1805
|
+
* boundaries are not preserved.
|
|
1806
|
+
*
|
|
1807
|
+
* @see {@link buffer} for buffering output elements without flattening arrays
|
|
1808
|
+
*
|
|
1809
|
+
* @stability stable
|
|
1810
|
+
* @category buffering
|
|
1811
|
+
* @since 4.0.0
|
|
1812
|
+
*/
|
|
1813
|
+
const bufferArray = /*#__PURE__*/ dual(2, (self, options) => fromTransform(fnUntraced(function* (upstream, scope) {
|
|
1814
|
+
const pull = yield* toTransform(self)(upstream, scope);
|
|
1815
|
+
const queue = yield* make$3({
|
|
1816
|
+
capacity: options.capacity === "unbounded" ? void 0 : options.capacity,
|
|
1817
|
+
strategy: options.capacity === "unbounded" ? void 0 : options.strategy
|
|
1818
|
+
});
|
|
1819
|
+
yield* addFinalizer(scope, shutdown(queue));
|
|
1820
|
+
yield* pull.pipe(flatMap$2((value) => offerAll(queue, value)), forever({ disableYield: true }), onError((cause) => failCause$3(queue, cause)), forkIn(scope));
|
|
1821
|
+
return takeAll(queue);
|
|
1822
|
+
})));
|
|
1823
|
+
/**
|
|
1824
|
+
* Interrupts a channel when another effect completes.
|
|
1825
|
+
*
|
|
1826
|
+
* **When to use**
|
|
1827
|
+
*
|
|
1828
|
+
* Use to race channel execution against an external effect whose success can
|
|
1829
|
+
* become the channel's done value.
|
|
1830
|
+
*
|
|
1831
|
+
* **Details**
|
|
1832
|
+
*
|
|
1833
|
+
* If the effect completes first, its success value becomes the returned
|
|
1834
|
+
* channel's done value. If the channel completes first, the original channel's
|
|
1835
|
+
* done value is preserved.
|
|
1836
|
+
*
|
|
1837
|
+
* @stability stable
|
|
1838
|
+
* @category interruption
|
|
1839
|
+
* @since 2.0.0
|
|
1840
|
+
*/
|
|
1841
|
+
const interruptWhen$1 = /*#__PURE__*/ dual(2, (self, effect) => merge$1(self, fromPull$1(succeed$2(flatMap$2(effect, done))), { haltStrategy: "either" }));
|
|
1842
|
+
/**
|
|
1843
|
+
* Returns a channel with an exit-aware finalizer that is guaranteed to run once
|
|
1844
|
+
* the channel begins execution, whether it succeeds or fails.
|
|
1845
|
+
*
|
|
1846
|
+
* **Example** (Running exit finalizers)
|
|
1847
|
+
*
|
|
1848
|
+
* ```ts import.meta.vitest
|
|
1849
|
+
* import { Channel, Data, Effect, Exit } from "effect"
|
|
1850
|
+
*
|
|
1851
|
+
* class ExitError extends Data.TaggedError("ExitError")<{
|
|
1852
|
+
* readonly stage: string
|
|
1853
|
+
* }> {}
|
|
1854
|
+
*
|
|
1855
|
+
* // Create a channel
|
|
1856
|
+
* const dataChannel = Channel.fromIterable([1, 2, 3])
|
|
1857
|
+
*
|
|
1858
|
+
* // Attach exit handler
|
|
1859
|
+
* const exits: Array<Exit.Exit<void, ExitError>> = []
|
|
1860
|
+
* const channelWithExit = Channel.onExit(dataChannel, (exit) => {
|
|
1861
|
+
* exits.push(exit)
|
|
1862
|
+
* return Effect.void
|
|
1863
|
+
* })
|
|
1864
|
+
* const observed = [await Effect.runPromise(Channel.runCollect(channelWithExit)), exits] // => [[1, 2, 3], [Exit.void]]
|
|
1865
|
+
* ```
|
|
1866
|
+
*
|
|
1867
|
+
* @stability stable
|
|
1868
|
+
* @category resource management
|
|
1869
|
+
* @since 4.0.0
|
|
1870
|
+
*/
|
|
1871
|
+
const onExit = /*#__PURE__*/ dual(2, (self, finalizer) => fromTransformBracket((upstream, scope, forkedScope) => addFinalizerExit(forkedScope, finalizer).pipe(andThen(toTransform(self)(upstream, scope)))));
|
|
1872
|
+
/**
|
|
1873
|
+
* Runs an effect the first time the channel emits an output element.
|
|
1874
|
+
*
|
|
1875
|
+
* **When to use**
|
|
1876
|
+
*
|
|
1877
|
+
* Use when initialization depends on the first output element rather than only
|
|
1878
|
+
* on channel startup.
|
|
1879
|
+
*
|
|
1880
|
+
* **Details**
|
|
1881
|
+
*
|
|
1882
|
+
* The effect receives the first emitted element. The first element is still
|
|
1883
|
+
* emitted unchanged. The effect is not run if the channel completes without
|
|
1884
|
+
* emitting an element.
|
|
1885
|
+
*
|
|
1886
|
+
* @stability stable
|
|
1887
|
+
* @category hooks
|
|
1888
|
+
* @since 4.0.0
|
|
1889
|
+
*/
|
|
1890
|
+
const onFirst = /*#__PURE__*/ dual(2, (self, onFirst) => transformPull$1(self, (pull) => sync(() => {
|
|
1891
|
+
let isFirst = true;
|
|
1892
|
+
const pullFirst = tap$1(pull, (element) => {
|
|
1893
|
+
isFirst = false;
|
|
1894
|
+
return onFirst(element);
|
|
1895
|
+
});
|
|
1896
|
+
return suspend$2(() => isFirst ? pullFirst : pull);
|
|
1897
|
+
})));
|
|
1898
|
+
/**
|
|
1899
|
+
* Returns a channel with a finalizer effect that is guaranteed to run once the
|
|
1900
|
+
* channel begins execution, whether it succeeds or fails.
|
|
1901
|
+
*
|
|
1902
|
+
* **Example** (Ensuring cleanup runs)
|
|
1903
|
+
*
|
|
1904
|
+
* ```ts import.meta.vitest
|
|
1905
|
+
* import { Channel, Data, Effect } from "effect"
|
|
1906
|
+
*
|
|
1907
|
+
* class EnsureError extends Data.TaggedError("EnsureError")<{
|
|
1908
|
+
* readonly operation: string
|
|
1909
|
+
* }> {}
|
|
1910
|
+
*
|
|
1911
|
+
* // Create a channel
|
|
1912
|
+
* const dataChannel = Channel.fromIterable([1, 2, 3])
|
|
1913
|
+
*
|
|
1914
|
+
* // Ensure cleanup always runs
|
|
1915
|
+
* const events: Array<string> = []
|
|
1916
|
+
* const channelWithCleanup = Channel.ensuring(
|
|
1917
|
+
* dataChannel,
|
|
1918
|
+
* Effect.sync(() => events.push("cleanup"))
|
|
1919
|
+
* )
|
|
1920
|
+
* const observed = [await Effect.runPromise(Channel.runCollect(channelWithCleanup)), events] // => [[1, 2, 3], ["cleanup"]]
|
|
1921
|
+
* ```
|
|
1922
|
+
*
|
|
1923
|
+
* @stability stable
|
|
1924
|
+
* @category resource management
|
|
1925
|
+
* @since 2.0.0
|
|
1926
|
+
*/
|
|
1927
|
+
const ensuring$1 = /*#__PURE__*/ dual(2, (self, finalizer) => onExit(self, (_) => finalizer));
|
|
1928
|
+
const runWith = (self, f, onHalt) => suspend$2(() => {
|
|
1929
|
+
const scope = makeUnsafe$1();
|
|
1930
|
+
const makePull = toTransform(self)(done(), scope);
|
|
1931
|
+
return catchDone(flatMap$2(makePull, f), onHalt ? onHalt : succeed$2).pipe(onExit$1((exit) => close(scope, exit)));
|
|
1932
|
+
});
|
|
1933
|
+
/**
|
|
1934
|
+
* Provides a `Context` to the channel, removing the corresponding service
|
|
1935
|
+
* requirements from the returned channel.
|
|
1936
|
+
*
|
|
1937
|
+
* @stability stable
|
|
1938
|
+
* @category providing services
|
|
1939
|
+
* @since 2.0.0
|
|
1940
|
+
*/
|
|
1941
|
+
const provideContext$1 = /*#__PURE__*/ dual(2, (self, context) => fromTransform((upstream, scope) => map$3(provideContext$2(toTransform(self)(upstream, scope), context), provideContext$2(context))));
|
|
1942
|
+
/**
|
|
1943
|
+
* Provides a concrete service for a context key, removing that service
|
|
1944
|
+
* requirement from the returned channel.
|
|
1945
|
+
*
|
|
1946
|
+
* @stability stable
|
|
1947
|
+
* @category providing services
|
|
1948
|
+
* @since 2.0.0
|
|
1949
|
+
*/
|
|
1950
|
+
const provideService$1 = /*#__PURE__*/ dual(3, (self, key, service) => fromTransform((upstream, scope) => map$3(provideService$2(toTransform(self)(upstream, scope), key, service), provideService$2(key, service))));
|
|
1951
|
+
/**
|
|
1952
|
+
* Provides a service to the channel after obtaining it from an effect.
|
|
1953
|
+
*
|
|
1954
|
+
* **When to use**
|
|
1955
|
+
*
|
|
1956
|
+
* Use to supply a channel dependency when constructing the service itself is
|
|
1957
|
+
* effectful or can fail.
|
|
1958
|
+
*
|
|
1959
|
+
* **Details**
|
|
1960
|
+
*
|
|
1961
|
+
* If the service effect fails, the returned channel fails. The provided service
|
|
1962
|
+
* removes the corresponding service requirement from the returned channel.
|
|
1963
|
+
*
|
|
1964
|
+
* @stability stable
|
|
1965
|
+
* @category providing services
|
|
1966
|
+
* @since 4.0.0
|
|
1967
|
+
*/
|
|
1968
|
+
const provideServiceEffect = /*#__PURE__*/ dual(3, (self, key, service) => fromTransform((upstream, scope) => flatMap$2(service, (s) => toTransform(provideService$1(self, key, s))(upstream, scope))));
|
|
1969
|
+
/**
|
|
1970
|
+
* Runs a channel and discards all output elements, returning only the final result.
|
|
1971
|
+
*
|
|
1972
|
+
* **Example** (Draining channel output at runtime)
|
|
1973
|
+
*
|
|
1974
|
+
* ```ts import.meta.vitest
|
|
1975
|
+
* import { Channel, Data, Effect } from "effect"
|
|
1976
|
+
*
|
|
1977
|
+
* class DrainError extends Data.TaggedError("DrainError")<{
|
|
1978
|
+
* readonly stage: string
|
|
1979
|
+
* }> {}
|
|
1980
|
+
*
|
|
1981
|
+
* // Create a channel that outputs elements and completes with a result
|
|
1982
|
+
* const resultChannel = Channel.fromIterable([1, 2, 3])
|
|
1983
|
+
* const completedChannel = Channel.concat(resultChannel, Channel.end("completed"))
|
|
1984
|
+
*
|
|
1985
|
+
* // Drain all elements and get only the final result
|
|
1986
|
+
* const drainEffect = Channel.runDrain(completedChannel)
|
|
1987
|
+
*
|
|
1988
|
+
* Effect.runSync(drainEffect) // => "completed"
|
|
1989
|
+
* ```
|
|
1990
|
+
*
|
|
1991
|
+
* @stability stable
|
|
1992
|
+
* @category running
|
|
1993
|
+
* @since 2.0.0
|
|
1994
|
+
*/
|
|
1995
|
+
const runDrain$1 = (self) => runWith(self, (pull) => forever(pull, { disableYield: true }));
|
|
1996
|
+
/**
|
|
1997
|
+
* Runs a channel and applies an effect to each output element.
|
|
1998
|
+
*
|
|
1999
|
+
* **Example** (Running effects for each output)
|
|
2000
|
+
*
|
|
2001
|
+
* ```ts import.meta.vitest
|
|
2002
|
+
* import { Channel, Data, Effect } from "effect"
|
|
2003
|
+
*
|
|
2004
|
+
* class ForEachError extends Data.TaggedError("ForEachError")<{
|
|
2005
|
+
* readonly element: unknown
|
|
2006
|
+
* }> {}
|
|
2007
|
+
*
|
|
2008
|
+
* // Create a channel with numbers
|
|
2009
|
+
* const numbersChannel = Channel.fromIterable([1, 2, 3])
|
|
2010
|
+
*
|
|
2011
|
+
* // Run forEach to process each element
|
|
2012
|
+
* const processed: Array<number> = []
|
|
2013
|
+
* const forEachEffect = Channel.runForEach(
|
|
2014
|
+
* numbersChannel,
|
|
2015
|
+
* (n) => Effect.sync(() => processed.push(n))
|
|
2016
|
+
* )
|
|
2017
|
+
*
|
|
2018
|
+
* await Effect.runPromise(forEachEffect)
|
|
2019
|
+
* processed // => [1, 2, 3]
|
|
2020
|
+
* ```
|
|
2021
|
+
*
|
|
2022
|
+
* @stability stable
|
|
2023
|
+
* @category running
|
|
2024
|
+
* @since 4.0.0
|
|
2025
|
+
*/
|
|
2026
|
+
const runForEach$1 = /*#__PURE__*/ dual(2, (self, f) => runWith(self, (pull) => forever(flatMap$2(pull, f), { disableYield: true })));
|
|
2027
|
+
/**
|
|
2028
|
+
* Concatenates a channel's `Uint8Array` chunks into a single `Uint8Array`.
|
|
2029
|
+
*
|
|
2030
|
+
* **Gotchas**
|
|
2031
|
+
*
|
|
2032
|
+
* This materializes the full content in memory. The source channel must not
|
|
2033
|
+
* reuse or mutate emitted buffers, which are retained until collection completes.
|
|
2034
|
+
*
|
|
2035
|
+
* **Example** (Joining channel byte chunks)
|
|
2036
|
+
*
|
|
2037
|
+
* ```ts import.meta.vitest
|
|
2038
|
+
* import { Channel, Effect } from "effect"
|
|
2039
|
+
*
|
|
2040
|
+
* const channel = Channel.fromArray([
|
|
2041
|
+
* [new Uint8Array([1, 2])],
|
|
2042
|
+
* [new Uint8Array([3, 4])]
|
|
2043
|
+
* ] as const)
|
|
2044
|
+
*
|
|
2045
|
+
* const bytes = Effect.runSync(Channel.mkUint8Array(channel))
|
|
2046
|
+
* Array.from(bytes) // => [1, 2, 3, 4]
|
|
2047
|
+
* ```
|
|
2048
|
+
*
|
|
2049
|
+
* @stability stable
|
|
2050
|
+
* @category running
|
|
2051
|
+
* @since 4.0.0
|
|
2052
|
+
*/
|
|
2053
|
+
const mkUint8Array$1 = (self) => map$3(runFold$1(self, () => ({
|
|
2054
|
+
bytes: 0,
|
|
2055
|
+
arrays: []
|
|
2056
|
+
}), (acc, chunk) => {
|
|
2057
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
2058
|
+
acc.bytes += chunk[i].length;
|
|
2059
|
+
acc.arrays.push(chunk[i]);
|
|
2060
|
+
}
|
|
2061
|
+
return acc;
|
|
2062
|
+
}), ({ arrays, bytes }) => {
|
|
2063
|
+
const result = new Uint8Array(bytes);
|
|
2064
|
+
let offset = 0;
|
|
2065
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
2066
|
+
const array = arrays[i];
|
|
2067
|
+
result.set(array, offset);
|
|
2068
|
+
offset += array.length;
|
|
2069
|
+
}
|
|
2070
|
+
return result;
|
|
2071
|
+
});
|
|
2072
|
+
/**
|
|
2073
|
+
* Runs a channel and folds over all output elements with an accumulator.
|
|
2074
|
+
*
|
|
2075
|
+
* **Example** (Folding channel output)
|
|
2076
|
+
*
|
|
2077
|
+
* ```ts import.meta.vitest
|
|
2078
|
+
* import { Channel, Data, Effect } from "effect"
|
|
2079
|
+
*
|
|
2080
|
+
* class FoldError extends Data.TaggedError("FoldError")<{
|
|
2081
|
+
* readonly operation: string
|
|
2082
|
+
* }> {}
|
|
2083
|
+
*
|
|
2084
|
+
* // Create a channel with numbers
|
|
2085
|
+
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
|
2086
|
+
*
|
|
2087
|
+
* // Fold to calculate sum
|
|
2088
|
+
* const sumEffect = Channel.runFold(numbersChannel, () => 0, (acc, n) => acc + n)
|
|
2089
|
+
*
|
|
2090
|
+
* Effect.runSync(sumEffect) // => 15
|
|
2091
|
+
* ```
|
|
2092
|
+
*
|
|
2093
|
+
* @stability stable
|
|
2094
|
+
* @category running
|
|
2095
|
+
* @since 4.0.0
|
|
2096
|
+
*/
|
|
2097
|
+
const runFold$1 = /*#__PURE__*/ dual(3, (self, initial, f) => suspend$2(() => {
|
|
2098
|
+
let state = initial();
|
|
2099
|
+
return runWith(self, (pull) => whileLoop({
|
|
2100
|
+
while: constTrue,
|
|
2101
|
+
body: () => pull,
|
|
2102
|
+
step: (value) => {
|
|
2103
|
+
state = f(state, value);
|
|
2104
|
+
}
|
|
2105
|
+
}), () => succeed$2(state));
|
|
2106
|
+
}));
|
|
2107
|
+
/**
|
|
2108
|
+
* Runs a channel and effectfully folds all output elements with an accumulator.
|
|
2109
|
+
*
|
|
2110
|
+
* **When to use**
|
|
2111
|
+
*
|
|
2112
|
+
* Use when folding channel output needs effects, services, or an additional
|
|
2113
|
+
* failure channel during accumulation.
|
|
2114
|
+
*
|
|
2115
|
+
* **Details**
|
|
2116
|
+
*
|
|
2117
|
+
* The initial accumulator is evaluated lazily. Each output element is passed to
|
|
2118
|
+
* the effectful accumulator function. The returned effect succeeds with the
|
|
2119
|
+
* final accumulator value.
|
|
2120
|
+
*
|
|
2121
|
+
* @stability stable
|
|
2122
|
+
* @category running
|
|
2123
|
+
* @since 4.0.0
|
|
2124
|
+
*/
|
|
2125
|
+
const runFoldEffect$1 = /*#__PURE__*/ dual(3, (self, initial, f) => suspend$2(() => {
|
|
2126
|
+
let state = initial();
|
|
2127
|
+
return runWith(self, (pull) => whileLoop({
|
|
2128
|
+
while: constTrue,
|
|
2129
|
+
body: constant(pull.pipe(flatMap$2((o) => f(state, o)), map$3((s) => {
|
|
2130
|
+
state = s;
|
|
2131
|
+
}))),
|
|
2132
|
+
step: constVoid
|
|
2133
|
+
}), () => succeed$2(state));
|
|
2134
|
+
}));
|
|
2135
|
+
/**
|
|
2136
|
+
* Converts a channel to a Pull within an existing scope.
|
|
2137
|
+
*
|
|
2138
|
+
* **Example** (Converting channels to scoped pulls)
|
|
2139
|
+
*
|
|
2140
|
+
* ```ts import.meta.vitest
|
|
2141
|
+
* import { Channel, Data, Effect, Scope } from "effect"
|
|
2142
|
+
*
|
|
2143
|
+
* class ScopedPullError extends Data.TaggedError("ScopedPullError")<{
|
|
2144
|
+
* readonly reason: string
|
|
2145
|
+
* }> {}
|
|
2146
|
+
*
|
|
2147
|
+
* // Create a channel
|
|
2148
|
+
* const numbersChannel = Channel.fromIterable([1, 2, 3])
|
|
2149
|
+
*
|
|
2150
|
+
* // Convert to Pull with explicit scope
|
|
2151
|
+
* const scopedPullEffect = Effect.gen(function*() {
|
|
2152
|
+
* const scope = yield* Effect.scope
|
|
2153
|
+
* const pull = yield* Channel.toPullScoped(numbersChannel, scope)
|
|
2154
|
+
* return [yield* pull, yield* pull, yield* pull]
|
|
2155
|
+
* })
|
|
2156
|
+
* await Effect.runPromise(Effect.scoped(scopedPullEffect)) // => [1, 2, 3]
|
|
2157
|
+
* ```
|
|
2158
|
+
*
|
|
2159
|
+
* @stability stable
|
|
2160
|
+
* @category destructors
|
|
2161
|
+
* @since 4.0.0
|
|
2162
|
+
*/
|
|
2163
|
+
const toPullScoped = (self, scope) => toTransform(self)(done(), scope);
|
|
2164
|
+
// ../../node_modules/.pnpm/effect@4.1.2_patch_hash=0e61176203bb1256381ce1c5b488d7951983ce38ff698647d3f4617383abbf07/node_modules/effect/dist/internal/stream.js
|
|
2165
|
+
const TypeId$1 = "~effect/Stream";
|
|
2166
|
+
const streamVariance = {
|
|
2167
|
+
_R: identity,
|
|
2168
|
+
_E: identity,
|
|
2169
|
+
_A: identity
|
|
2170
|
+
};
|
|
2171
|
+
const Stream = function(channel) {
|
|
2172
|
+
this.channel = channel;
|
|
2173
|
+
};
|
|
2174
|
+
Stream.prototype = {
|
|
2175
|
+
[TypeId$1]: streamVariance,
|
|
2176
|
+
pipe() {
|
|
2177
|
+
return pipeArguments(this, arguments);
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
/** @internal */
|
|
2181
|
+
const fromChannel$1 = (channel) => new Stream(channel);
|
|
2182
|
+
// ../../node_modules/.pnpm/effect@4.1.2_patch_hash=0e61176203bb1256381ce1c5b488d7951983ce38ff698647d3f4617383abbf07/node_modules/effect/dist/Stream.js
|
|
2183
|
+
/**
|
|
2184
|
+
* Describes effectful sources that emit values over time.
|
|
2185
|
+
*
|
|
2186
|
+
* A `Stream<A, E, R>` can emit many `A` values, fail with `E`, and require
|
|
2187
|
+
* services `R` while it is being consumed. Streams are useful for data that is
|
|
2188
|
+
* pulled in steps, such as values from collections, queues, pubsubs, schedules,
|
|
2189
|
+
* callbacks, async iterables, or platform streams. The APIs here cover the full
|
|
2190
|
+
* stream lifecycle: create a stream, transform or combine it, control buffering
|
|
2191
|
+
* and timing, handle failures, and finally consume it.
|
|
2192
|
+
*
|
|
2193
|
+
* @stability stable
|
|
2194
|
+
* @since 2.0.0
|
|
2195
|
+
*/
|
|
2196
|
+
/**
|
|
2197
|
+
* Runtime identifier stored on `Stream` values and used by `isStream` to
|
|
2198
|
+
* recognize them.
|
|
2199
|
+
*
|
|
2200
|
+
* **Details**
|
|
2201
|
+
*
|
|
2202
|
+
* This marker is part of the runtime representation of `Stream` values. Prefer
|
|
2203
|
+
* `isStream` when narrowing unknown values.
|
|
2204
|
+
*
|
|
2205
|
+
* @see {@link isStream} for the public guard that checks this identifier
|
|
2206
|
+
*
|
|
2207
|
+
* @stability stable
|
|
2208
|
+
* @category type IDs
|
|
2209
|
+
* @since 4.0.0
|
|
2210
|
+
*/
|
|
2211
|
+
const TypeId = "~effect/Stream";
|
|
2212
|
+
/**
|
|
2213
|
+
* Checks whether a value is a Stream.
|
|
2214
|
+
*
|
|
2215
|
+
* **Example** (Checking whether a value is a Stream)
|
|
2216
|
+
*
|
|
2217
|
+
* ```ts import.meta.vitest
|
|
2218
|
+
* import { Stream } from "effect"
|
|
2219
|
+
*
|
|
2220
|
+
* Stream.isStream(Stream.make(1, 2, 3)) // => true
|
|
2221
|
+
* Stream.isStream({ data: [1, 2, 3] }) // => false
|
|
2222
|
+
* ```
|
|
2223
|
+
*
|
|
2224
|
+
* @stability stable
|
|
2225
|
+
* @category guards
|
|
2226
|
+
* @since 4.0.0
|
|
2227
|
+
*/
|
|
2228
|
+
const isStream = (u) => hasProperty(u, TypeId);
|
|
2229
|
+
/**
|
|
2230
|
+
* Creates a stream from a array-emitting `Channel`.
|
|
2231
|
+
*
|
|
2232
|
+
* **Example** (Creating a stream from an array-emitting channel)
|
|
2233
|
+
*
|
|
2234
|
+
* ```ts import.meta.vitest
|
|
2235
|
+
* import { Channel, Effect, Stream } from "effect"
|
|
2236
|
+
*
|
|
2237
|
+
* const channel = Channel.succeed([1, 2, 3] as const)
|
|
2238
|
+
* const stream = Stream.fromChannel(channel)
|
|
2239
|
+
* await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3]
|
|
2240
|
+
* ```
|
|
2241
|
+
*
|
|
2242
|
+
* @stability stable
|
|
2243
|
+
* @category constructors
|
|
2244
|
+
* @since 2.0.0
|
|
2245
|
+
*/
|
|
2246
|
+
const fromChannel = fromChannel$1;
|
|
2247
|
+
/**
|
|
2248
|
+
* Creates a stream from an effect.
|
|
2249
|
+
*
|
|
2250
|
+
* **Example** (Creating a stream from an effect)
|
|
2251
|
+
*
|
|
2252
|
+
* ```ts import.meta.vitest
|
|
2253
|
+
* import { Effect, Stream } from "effect"
|
|
2254
|
+
*
|
|
2255
|
+
* const stream = Stream.fromEffect(Effect.succeed(42))
|
|
2256
|
+
* await Effect.runPromise(Stream.runCollect(stream)) // => [42]
|
|
2257
|
+
* ```
|
|
2258
|
+
*
|
|
2259
|
+
* @stability stable
|
|
2260
|
+
* @category constructors
|
|
2261
|
+
* @since 2.0.0
|
|
2262
|
+
*/
|
|
2263
|
+
const fromEffect = (effect) => fromChannel(fromEffect$1(map$3(effect, of)));
|
|
2264
|
+
/**
|
|
2265
|
+
* Creates a stream that emits `void` immediately once, then emits another
|
|
2266
|
+
* `void` after each specified interval.
|
|
2267
|
+
*
|
|
2268
|
+
* **Example** (Emitting ticks on an interval)
|
|
2269
|
+
*
|
|
2270
|
+
* ```ts import.meta.vitest
|
|
2271
|
+
* import { Effect, Stream } from "effect"
|
|
2272
|
+
*
|
|
2273
|
+
* await Effect.runPromise(Stream.tick(0).pipe(Stream.take(3), Stream.runCollect)) // => [undefined, undefined, undefined]
|
|
2274
|
+
* ```
|
|
2275
|
+
*
|
|
2276
|
+
* @stability stable
|
|
2277
|
+
* @category constructors
|
|
2278
|
+
* @since 2.0.0
|
|
2279
|
+
*/
|
|
2280
|
+
const tick = (interval) => fromPull(sync(() => {
|
|
2281
|
+
let first = true;
|
|
2282
|
+
const effect = succeed$2(of(void 0));
|
|
2283
|
+
const delayed = delay(effect, interval);
|
|
2284
|
+
return suspend$2(() => {
|
|
2285
|
+
if (first) {
|
|
2286
|
+
first = false;
|
|
2287
|
+
return effect;
|
|
2288
|
+
}
|
|
2289
|
+
return delayed;
|
|
2290
|
+
});
|
|
2291
|
+
}));
|
|
2292
|
+
/**
|
|
2293
|
+
* Creates a stream from a pull effect, such as one produced by `Stream.toPull`.
|
|
2294
|
+
*
|
|
2295
|
+
* **Details**
|
|
2296
|
+
*
|
|
2297
|
+
* A pull effect yields chunks on demand and completes when the upstream stream ends.
|
|
2298
|
+
* See `Stream.toPull` for a matching producer.
|
|
2299
|
+
*
|
|
2300
|
+
* **Example** (Creating a stream from a pull effect)
|
|
2301
|
+
*
|
|
2302
|
+
* ```ts import.meta.vitest
|
|
2303
|
+
* import { Effect, Stream } from "effect"
|
|
2304
|
+
*
|
|
2305
|
+
* const program = Effect.scoped(
|
|
2306
|
+
* Effect.gen(function*() {
|
|
2307
|
+
* const source = Stream.make(1, 2, 3)
|
|
2308
|
+
* const pull = yield* Stream.toPull(source)
|
|
2309
|
+
* const stream = Stream.fromPull(Effect.succeed(pull))
|
|
2310
|
+
* return yield* Stream.runCollect(stream)
|
|
2311
|
+
* })
|
|
2312
|
+
* )
|
|
2313
|
+
*
|
|
2314
|
+
* await Effect.runPromise(program) // => [1, 2, 3]
|
|
2315
|
+
* ```
|
|
2316
|
+
*
|
|
2317
|
+
* @stability stable
|
|
2318
|
+
* @category constructors
|
|
2319
|
+
* @since 2.0.0
|
|
2320
|
+
*/
|
|
2321
|
+
const fromPull = (pull) => fromChannel(fromPull$1(pull));
|
|
2322
|
+
/**
|
|
2323
|
+
* Derives a stream by transforming its pull effect.
|
|
2324
|
+
*
|
|
2325
|
+
* **Example** (Transforming a pull effect)
|
|
2326
|
+
*
|
|
2327
|
+
* ```ts import.meta.vitest
|
|
2328
|
+
* import { Effect, Stream } from "effect"
|
|
2329
|
+
*
|
|
2330
|
+
* const stream = Stream.make(1, 2, 3)
|
|
2331
|
+
*
|
|
2332
|
+
* const transformed = Stream.transformPull(stream, (pull) => Effect.succeed(pull))
|
|
2333
|
+
*
|
|
2334
|
+
* await Effect.runPromise(Stream.runCollect(transformed)) // => [1, 2, 3]
|
|
2335
|
+
* ```
|
|
2336
|
+
*
|
|
2337
|
+
* @stability stable
|
|
2338
|
+
* @category constructors
|
|
2339
|
+
* @since 4.0.0
|
|
2340
|
+
*/
|
|
2341
|
+
const transformPull = (self, f) => fromChannel(fromTransform((_, scope) => flatMap$2(toPullScoped(self.channel, scope), (pull) => f(pull, scope))));
|
|
2342
|
+
/**
|
|
2343
|
+
* Creates a channel from a stream.
|
|
2344
|
+
*
|
|
2345
|
+
* **Example** (Converting a stream to a channel)
|
|
2346
|
+
*
|
|
2347
|
+
* ```ts import.meta.vitest
|
|
2348
|
+
* import { Channel, Effect, Stream } from "effect"
|
|
2349
|
+
*
|
|
2350
|
+
* const channel = Stream.toChannel(Stream.make(1, 2, 3))
|
|
2351
|
+
* const values = await Effect.runPromise(Channel.runCollect(channel))
|
|
2352
|
+
* values.flat() // => [1, 2, 3]
|
|
2353
|
+
* ```
|
|
2354
|
+
*
|
|
2355
|
+
* @stability stable
|
|
2356
|
+
* @category destructors
|
|
2357
|
+
* @since 2.0.0
|
|
2358
|
+
*/
|
|
2359
|
+
const toChannel = (stream) => stream.channel;
|
|
2360
|
+
/**
|
|
2361
|
+
* Creates a stream from a callback that can emit values into a queue.
|
|
2362
|
+
*
|
|
2363
|
+
* **When to use**
|
|
2364
|
+
*
|
|
2365
|
+
* Use when you need callback-based code to emit stream values by offering to a
|
|
2366
|
+
* `Queue`, or signal stream completion through the `Queue` module APIs.
|
|
2367
|
+
*
|
|
2368
|
+
* By default it uses an "unbounded" buffer size.
|
|
2369
|
+
* You can customize the buffer size and strategy by passing an object as the
|
|
2370
|
+
* second argument with the `bufferSize` and `strategy` fields.
|
|
2371
|
+
*
|
|
2372
|
+
* **Example** (Creating a stream from a callback that can emit values into a queue)
|
|
2373
|
+
*
|
|
2374
|
+
* ```ts import.meta.vitest
|
|
2375
|
+
* import { Effect, Queue, Stream } from "effect"
|
|
2376
|
+
*
|
|
2377
|
+
* const stream = Stream.callback<number>((queue) =>
|
|
2378
|
+
* Effect.sync(() => {
|
|
2379
|
+
* // Emit values to the stream
|
|
2380
|
+
* Queue.offerUnsafe(queue, 1)
|
|
2381
|
+
* Queue.offerUnsafe(queue, 2)
|
|
2382
|
+
* Queue.offerUnsafe(queue, 3)
|
|
2383
|
+
* // Signal completion
|
|
2384
|
+
* Queue.endUnsafe(queue)
|
|
2385
|
+
* })
|
|
2386
|
+
* )
|
|
2387
|
+
*
|
|
2388
|
+
* await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3]
|
|
2389
|
+
* ```
|
|
2390
|
+
*
|
|
2391
|
+
* @stability stable
|
|
2392
|
+
* @category constructors
|
|
2393
|
+
* @since 4.0.0
|
|
2394
|
+
*/
|
|
2395
|
+
const callback = (f, options) => fromChannel(callbackArray(f, options));
|
|
2396
|
+
/**
|
|
2397
|
+
* Creates an empty stream.
|
|
2398
|
+
*
|
|
2399
|
+
* **Example** (Creating an empty stream)
|
|
2400
|
+
*
|
|
2401
|
+
* ```ts import.meta.vitest
|
|
2402
|
+
* import { Effect, Stream } from "effect"
|
|
2403
|
+
*
|
|
2404
|
+
* await Effect.runPromise(Stream.runCollect(Stream.empty)) // => []
|
|
2405
|
+
* ```
|
|
2406
|
+
*
|
|
2407
|
+
* @stability stable
|
|
2408
|
+
* @category constructors
|
|
2409
|
+
* @since 2.0.0
|
|
2410
|
+
*/
|
|
2411
|
+
const empty = /*#__PURE__*/ fromChannel(empty$1);
|
|
2412
|
+
/**
|
|
2413
|
+
* Creates a single-valued pure stream.
|
|
2414
|
+
*
|
|
2415
|
+
* **Example** (Creating a single-valued pure stream)
|
|
2416
|
+
*
|
|
2417
|
+
* ```ts import.meta.vitest
|
|
2418
|
+
* import { Effect, Stream } from "effect"
|
|
2419
|
+
*
|
|
2420
|
+
* await Effect.runPromise(Stream.runCollect(Stream.succeed(3))) // => [3]
|
|
2421
|
+
* ```
|
|
2422
|
+
*
|
|
2423
|
+
* @stability stable
|
|
2424
|
+
* @category constructors
|
|
2425
|
+
* @since 2.0.0
|
|
2426
|
+
*/
|
|
2427
|
+
const succeed = (value) => fromChannel(succeed$1(of(value)));
|
|
2428
|
+
/**
|
|
2429
|
+
* Creates a stream from a sequence of values.
|
|
2430
|
+
*
|
|
2431
|
+
* **Example** (Creating a stream from a sequence of values)
|
|
2432
|
+
*
|
|
2433
|
+
* ```ts import.meta.vitest
|
|
2434
|
+
* import { Effect, Stream } from "effect"
|
|
2435
|
+
*
|
|
2436
|
+
* const stream = Stream.make(1, 2, 3)
|
|
2437
|
+
*
|
|
2438
|
+
* await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3]
|
|
2439
|
+
* ```
|
|
2440
|
+
*
|
|
2441
|
+
* @stability stable
|
|
2442
|
+
* @category constructors
|
|
2443
|
+
* @since 2.0.0
|
|
2444
|
+
*/
|
|
2445
|
+
const make = (...values) => fromArray(values);
|
|
2446
|
+
/**
|
|
2447
|
+
* Creates a lazily constructed stream.
|
|
2448
|
+
*
|
|
2449
|
+
* **Details**
|
|
2450
|
+
*
|
|
2451
|
+
* The stream factory is evaluated each time the stream is run.
|
|
2452
|
+
*
|
|
2453
|
+
* **Example** (Creating a lazily constructed stream)
|
|
2454
|
+
*
|
|
2455
|
+
* ```ts import.meta.vitest
|
|
2456
|
+
* import { Effect, Stream } from "effect"
|
|
2457
|
+
*
|
|
2458
|
+
* await Effect.runPromise(Stream.suspend(() => Stream.make(1, 2, 3)).pipe(Stream.runCollect)) // => [1, 2, 3]
|
|
2459
|
+
* ```
|
|
2460
|
+
*
|
|
2461
|
+
* @stability stable
|
|
2462
|
+
* @category constructors
|
|
2463
|
+
* @since 2.0.0
|
|
2464
|
+
*/
|
|
2465
|
+
const suspend = (stream) => {
|
|
2466
|
+
const suspended = fromChannel(suspend$1(() => stream().channel));
|
|
2467
|
+
suspendedStreams.set(suspended, stream);
|
|
2468
|
+
return suspended;
|
|
2469
|
+
};
|
|
2470
|
+
const suspendedStreams = /*#__PURE__*/ new WeakMap();
|
|
2471
|
+
/**
|
|
2472
|
+
* Terminates with the specified error.
|
|
2473
|
+
*
|
|
2474
|
+
* **Example** (Failing a stream)
|
|
2475
|
+
*
|
|
2476
|
+
* ```ts import.meta.vitest
|
|
2477
|
+
* import { Effect, Exit, Stream } from "effect"
|
|
2478
|
+
*
|
|
2479
|
+
* await Effect.runPromise(Effect.exit(Stream.runCollect(Stream.fail("Uh oh!")))) // => Exit.fail("Uh oh!")
|
|
2480
|
+
* ```
|
|
2481
|
+
*
|
|
2482
|
+
* @stability stable
|
|
2483
|
+
* @category constructors
|
|
2484
|
+
* @since 2.0.0
|
|
2485
|
+
*/
|
|
2486
|
+
const fail = (error) => fromChannel(fail$1(error));
|
|
2487
|
+
/**
|
|
2488
|
+
* The stream that dies with the specified defect.
|
|
2489
|
+
*
|
|
2490
|
+
* **Example** (Dying with a defect)
|
|
2491
|
+
*
|
|
2492
|
+
* ```ts import.meta.vitest
|
|
2493
|
+
* import { Cause, Effect, Exit, Stream } from "effect"
|
|
2494
|
+
*
|
|
2495
|
+
* const defect = new Error("Boom")
|
|
2496
|
+
* const stream = Stream.die(defect)
|
|
2497
|
+
*
|
|
2498
|
+
* await Effect.runPromise(Effect.exit(Stream.runCollect(stream))) // => Exit.failCause(Cause.die(defect))
|
|
2499
|
+
* ```
|
|
2500
|
+
*
|
|
2501
|
+
* @stability stable
|
|
2502
|
+
* @category constructors
|
|
2503
|
+
* @since 2.0.0
|
|
2504
|
+
*/
|
|
2505
|
+
const die = (defect) => fromChannel(die$1(defect));
|
|
2506
|
+
/**
|
|
2507
|
+
* Creates a new `Stream` from an iterable collection of values.
|
|
2508
|
+
*
|
|
2509
|
+
* **Details**
|
|
2510
|
+
*
|
|
2511
|
+
* - `chunkSize`: Maximum number of values emitted per chunk. Finite fractional
|
|
2512
|
+
* values are rounded down, while `NaN` and non-positive values are treated as
|
|
2513
|
+
* `1`.
|
|
2514
|
+
*
|
|
2515
|
+
* **Example** (Creating a stream from an iterable)
|
|
2516
|
+
*
|
|
2517
|
+
* ```ts import.meta.vitest
|
|
2518
|
+
* import { Effect, Stream } from "effect"
|
|
2519
|
+
*
|
|
2520
|
+
* const numbers = [1, 2, 3]
|
|
2521
|
+
*
|
|
2522
|
+
* const program = Effect.gen(function*() {
|
|
2523
|
+
* const stream = Stream.fromIterable(numbers)
|
|
2524
|
+
* const values = yield* Stream.runCollect(stream)
|
|
2525
|
+
* values // => [1, 2, 3]
|
|
2526
|
+
* })
|
|
2527
|
+
*
|
|
2528
|
+
* await Effect.runPromise(program)
|
|
2529
|
+
* ```
|
|
2530
|
+
*
|
|
2531
|
+
* @stability stable
|
|
2532
|
+
* @category constructors
|
|
2533
|
+
* @since 2.0.0
|
|
2534
|
+
*/
|
|
2535
|
+
const fromIterable = (iterable, options) => Array.isArray(iterable) && options?.chunkSize === void 0 ? fromArray(iterable) : fromChannel(fromIterableArray(iterable, options?.chunkSize));
|
|
2536
|
+
/**
|
|
2537
|
+
* Creates a stream from an array of values.
|
|
2538
|
+
*
|
|
2539
|
+
* **Example** (Creating a stream from an array of values)
|
|
2540
|
+
*
|
|
2541
|
+
* ```ts import.meta.vitest
|
|
2542
|
+
* import { Effect, Stream } from "effect"
|
|
2543
|
+
*
|
|
2544
|
+
* const program = Effect.gen(function*() {
|
|
2545
|
+
* const stream = Stream.fromArray([1, 2, 3])
|
|
2546
|
+
* const values = yield* Stream.runCollect(stream)
|
|
2547
|
+
* values // => [1, 2, 3]
|
|
2548
|
+
* })
|
|
2549
|
+
*
|
|
2550
|
+
* await Effect.runPromise(program)
|
|
2551
|
+
* ```
|
|
2552
|
+
*
|
|
2553
|
+
* @stability stable
|
|
2554
|
+
* @category constructors
|
|
2555
|
+
* @since 4.0.0
|
|
2556
|
+
*/
|
|
2557
|
+
const fromArray = (array) => isReadonlyArrayNonEmpty(array) ? fromChannel(succeed$1(array)) : empty;
|
|
2558
|
+
/**
|
|
2559
|
+
* Creates a stream that pulls values from a `Queue.Dequeue`.
|
|
2560
|
+
*
|
|
2561
|
+
* **Details**
|
|
2562
|
+
*
|
|
2563
|
+
* The stream emits non-empty batches of queued values and ends when the queue
|
|
2564
|
+
* fails with `Cause.Done`; other queue failures are propagated.
|
|
2565
|
+
*
|
|
2566
|
+
* **Example** (Creating a stream from a queue of values)
|
|
2567
|
+
*
|
|
2568
|
+
* ```ts import.meta.vitest
|
|
2569
|
+
* import { Cause, Effect, Queue, Stream } from "effect"
|
|
2570
|
+
*
|
|
2571
|
+
* const program = Effect.gen(function*() {
|
|
2572
|
+
* const queue = yield* Queue.unbounded<number, Cause.Done>()
|
|
2573
|
+
* yield* Queue.offer(queue, 1)
|
|
2574
|
+
* yield* Queue.offer(queue, 2)
|
|
2575
|
+
* yield* Queue.offer(queue, 3)
|
|
2576
|
+
* yield* Queue.end(queue)
|
|
2577
|
+
*
|
|
2578
|
+
* const stream = Stream.fromQueue(queue)
|
|
2579
|
+
* const values = yield* Stream.runCollect(stream)
|
|
2580
|
+
* values // => [1, 2, 3]
|
|
2581
|
+
* })
|
|
2582
|
+
*
|
|
2583
|
+
* await Effect.runPromise(program)
|
|
2584
|
+
* ```
|
|
2585
|
+
*
|
|
2586
|
+
* @stability stable
|
|
2587
|
+
* @category constructors
|
|
2588
|
+
* @since 2.0.0
|
|
2589
|
+
*/
|
|
2590
|
+
const fromQueue = (queue) => fromChannel(fromQueueArray(queue));
|
|
2591
|
+
/**
|
|
2592
|
+
* Creates a stream from a lazily supplied Web `ReadableStream`.
|
|
2593
|
+
*
|
|
2594
|
+
* **Details**
|
|
2595
|
+
*
|
|
2596
|
+
* The stream reads from a `ReadableStreamDefaultReader` and maps read failures
|
|
2597
|
+
* with `onError`. Finalization cancels the reader and releases its lock, or
|
|
2598
|
+
* only releases the lock when `releaseLockOnEnd` is set. A rejected cancel
|
|
2599
|
+
* hook becomes a defect.
|
|
2600
|
+
*
|
|
2601
|
+
* **Example** (Creating a stream from a ReadableStream)
|
|
2602
|
+
*
|
|
2603
|
+
* ```ts import.meta.vitest
|
|
2604
|
+
* import { Data, Effect, Stream } from "effect"
|
|
2605
|
+
*
|
|
2606
|
+
* class StreamError extends Data.TaggedError("StreamError")<{ readonly cause: unknown }> {}
|
|
2607
|
+
*
|
|
2608
|
+
* const readableStream = new ReadableStream({
|
|
2609
|
+
* start(controller) {
|
|
2610
|
+
* controller.enqueue(1)
|
|
2611
|
+
* controller.enqueue(2)
|
|
2612
|
+
* controller.enqueue(3)
|
|
2613
|
+
* controller.close()
|
|
2614
|
+
* }
|
|
2615
|
+
* })
|
|
2616
|
+
*
|
|
2617
|
+
* const program = Effect.gen(function*() {
|
|
2618
|
+
* const stream = Stream.fromReadableStream({
|
|
2619
|
+
* evaluate: () => readableStream,
|
|
2620
|
+
* onError: (cause) => new StreamError({ cause })
|
|
2621
|
+
* })
|
|
2622
|
+
* const values = yield* Stream.runCollect(stream)
|
|
2623
|
+
* values // => [1, 2, 3]
|
|
2624
|
+
* })
|
|
2625
|
+
*
|
|
2626
|
+
* await Effect.runPromise(program)
|
|
2627
|
+
* ```
|
|
2628
|
+
*
|
|
2629
|
+
* @stability stable
|
|
2630
|
+
* @category constructors
|
|
2631
|
+
* @since 2.0.0
|
|
2632
|
+
*/
|
|
2633
|
+
const fromReadableStream = (options) => fromChannel(fromReadableStream$1(options));
|
|
2634
|
+
/**
|
|
2635
|
+
* Creates a stream from an AsyncIterable.
|
|
2636
|
+
*
|
|
2637
|
+
* **Example** (Creating a stream from an AsyncIterable)
|
|
2638
|
+
*
|
|
2639
|
+
* ```ts import.meta.vitest
|
|
2640
|
+
* import { Data, Effect, Stream } from "effect"
|
|
2641
|
+
*
|
|
2642
|
+
* class StreamError extends Data.TaggedError("StreamError")<{ readonly cause: unknown }> {}
|
|
2643
|
+
*
|
|
2644
|
+
* const iterable = (async function*() {
|
|
2645
|
+
* yield 1
|
|
2646
|
+
* yield 2
|
|
2647
|
+
* yield 3
|
|
2648
|
+
* })()
|
|
2649
|
+
*
|
|
2650
|
+
* await Effect.runPromise(Effect.gen(function*() {
|
|
2651
|
+
* const stream = Stream.fromAsyncIterable(iterable, (cause) => new StreamError({ cause }))
|
|
2652
|
+
* const values = yield* Stream.runCollect(stream)
|
|
2653
|
+
* values // => [1, 2, 3]
|
|
2654
|
+
* }))
|
|
2655
|
+
*
|
|
2656
|
+
* ```
|
|
2657
|
+
*
|
|
2658
|
+
* @stability stable
|
|
2659
|
+
* @category constructors
|
|
2660
|
+
* @since 2.0.0
|
|
2661
|
+
*/
|
|
2662
|
+
const fromAsyncIterable = (iterable, onError) => fromChannel(fromAsyncIterableArray(iterable, onError));
|
|
2663
|
+
/**
|
|
2664
|
+
* Creates a stream by repeatedly evaluating an effectful page function.
|
|
2665
|
+
*
|
|
2666
|
+
* **When to use**
|
|
2667
|
+
*
|
|
2668
|
+
* Use to consume paginated APIs where each step returns a batch of values
|
|
2669
|
+
* together with an optional next state.
|
|
2670
|
+
*
|
|
2671
|
+
* **Details**
|
|
2672
|
+
*
|
|
2673
|
+
* This is similar to {@link unfold}, but each step can emit zero or more values
|
|
2674
|
+
* and independently decide whether another state should be requested.
|
|
2675
|
+
*
|
|
2676
|
+
* **Example** (Paginating stream state)
|
|
2677
|
+
*
|
|
2678
|
+
* ```ts import.meta.vitest
|
|
2679
|
+
* import { Effect, Option, Stream } from "effect"
|
|
2680
|
+
*
|
|
2681
|
+
* const stream = Stream.paginate(0, (n: number) =>
|
|
2682
|
+
* Effect.succeed(
|
|
2683
|
+
* [
|
|
2684
|
+
* [n],
|
|
2685
|
+
* n < 3 ? Option.some(n + 1) : Option.none<number>()
|
|
2686
|
+
* ] as const
|
|
2687
|
+
* ))
|
|
2688
|
+
*
|
|
2689
|
+
* await Effect.runPromise(Stream.runCollect(stream)) // => [0, 1, 2, 3]
|
|
2690
|
+
* ```
|
|
2691
|
+
*
|
|
2692
|
+
* @stability stable
|
|
2693
|
+
* @category constructors
|
|
2694
|
+
* @since 2.0.0
|
|
2695
|
+
*/
|
|
2696
|
+
const paginate = (s, f) => fromPull(sync(() => {
|
|
2697
|
+
let state = s;
|
|
2698
|
+
let done$1 = false;
|
|
2699
|
+
return suspend$2(function loop() {
|
|
2700
|
+
if (done$1) return done();
|
|
2701
|
+
return flatMap$2(f(state), ([a, s]) => {
|
|
2702
|
+
if (isNone(s)) done$1 = true;
|
|
2703
|
+
else state = s.value;
|
|
2704
|
+
if (!isReadonlyArrayNonEmpty(a)) return loop();
|
|
2705
|
+
return succeed$2(a);
|
|
2706
|
+
});
|
|
2707
|
+
});
|
|
2708
|
+
}));
|
|
2709
|
+
/**
|
|
2710
|
+
* Creates a stream produced from an `Effect`.
|
|
2711
|
+
*
|
|
2712
|
+
* **Example** (Unwrapping a stream effect)
|
|
2713
|
+
*
|
|
2714
|
+
* ```ts import.meta.vitest
|
|
2715
|
+
* import { Effect, Stream } from "effect"
|
|
2716
|
+
*
|
|
2717
|
+
* const effect = Effect.succeed(Stream.make(1, 2, 3))
|
|
2718
|
+
*
|
|
2719
|
+
* const stream = Stream.unwrap(effect)
|
|
2720
|
+
*
|
|
2721
|
+
* const program = Effect.gen(function*() {
|
|
2722
|
+
* const chunk = yield* Stream.runCollect(stream)
|
|
2723
|
+
* chunk // => [ 1, 2, 3 ]
|
|
2724
|
+
* })
|
|
2725
|
+
* await Effect.runPromise(program)
|
|
2726
|
+
* ```
|
|
2727
|
+
*
|
|
2728
|
+
* @stability stable
|
|
2729
|
+
* @category constructors
|
|
2730
|
+
* @since 2.0.0
|
|
2731
|
+
*/
|
|
2732
|
+
const unwrap = (effect) => fromChannel(unwrap$1(map$3(effect, toChannel)));
|
|
2733
|
+
/**
|
|
2734
|
+
* Transforms the elements of this stream using the supplied function.
|
|
2735
|
+
*
|
|
2736
|
+
* **Example** (Mapping stream values)
|
|
2737
|
+
*
|
|
2738
|
+
* ```ts import.meta.vitest
|
|
2739
|
+
* import { Effect, Option, Stream } from "effect"
|
|
2740
|
+
*
|
|
2741
|
+
* const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.map((n, i) => n + i))
|
|
2742
|
+
* await Effect.runPromise(Stream.runCollect(stream)) // => [1, 3, 5]
|
|
2743
|
+
* ```
|
|
2744
|
+
*
|
|
2745
|
+
* @stability stable
|
|
2746
|
+
* @category mapping
|
|
2747
|
+
* @since 2.0.0
|
|
2748
|
+
*/
|
|
2749
|
+
const map = /*#__PURE__*/ dual(2, (self, f) => suspend(() => {
|
|
2750
|
+
let i = 0;
|
|
2751
|
+
return fromChannel(map$1(self.channel, map$2((o) => f(o, i++))));
|
|
2752
|
+
}));
|
|
2753
|
+
/**
|
|
2754
|
+
* Maps over elements of the stream with the specified effectful function.
|
|
2755
|
+
*
|
|
2756
|
+
* **When to use**
|
|
2757
|
+
*
|
|
2758
|
+
* Use when each stream element transformation needs an Effect, service
|
|
2759
|
+
* dependency, failure channel, or configured concurrency.
|
|
2760
|
+
*
|
|
2761
|
+
* **Example** (Effectfully mapping stream values)
|
|
2762
|
+
*
|
|
2763
|
+
* ```ts import.meta.vitest
|
|
2764
|
+
* import { Effect, Stream } from "effect"
|
|
2765
|
+
*
|
|
2766
|
+
* const events: Array<string> = []
|
|
2767
|
+
* const stream = Stream.make(1, 2, 3)
|
|
2768
|
+
*
|
|
2769
|
+
* const mappedStream = stream.pipe(
|
|
2770
|
+
* Stream.mapEffect((n) =>
|
|
2771
|
+
* Effect.sync(() => {
|
|
2772
|
+
* events.push(`Processing: ${n}`)
|
|
2773
|
+
* return n * 2
|
|
2774
|
+
* })
|
|
2775
|
+
* )
|
|
2776
|
+
* )
|
|
2777
|
+
*
|
|
2778
|
+
* const program = Effect.gen(function*() {
|
|
2779
|
+
* const result = yield* Stream.runCollect(mappedStream)
|
|
2780
|
+
* result // => [2, 4, 6]
|
|
2781
|
+
* })
|
|
2782
|
+
*
|
|
2783
|
+
* await Effect.runPromise(program)
|
|
2784
|
+
* events // => ["Processing: 1", "Processing: 2", "Processing: 3"]
|
|
2785
|
+
* ```
|
|
2786
|
+
*
|
|
2787
|
+
* @stability stable
|
|
2788
|
+
* @category mapping
|
|
2789
|
+
* @since 2.0.0
|
|
2790
|
+
*/
|
|
2791
|
+
const mapEffect = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, f, options) => self.channel.pipe(flattenArray, mapEffect$1(f, options), map$1(of), fromChannel));
|
|
2792
|
+
/**
|
|
2793
|
+
* Runs the provided effect for each element while preserving the elements.
|
|
2794
|
+
*
|
|
2795
|
+
* **Example** (Tapping stream values)
|
|
2796
|
+
*
|
|
2797
|
+
* ```ts import.meta.vitest
|
|
2798
|
+
* import { Effect, Stream } from "effect"
|
|
2799
|
+
*
|
|
2800
|
+
* const events: Array<string> = []
|
|
2801
|
+
* const program = Effect.gen(function*() {
|
|
2802
|
+
* const result = yield* Stream.fromArray([1, 2, 3]).pipe(
|
|
2803
|
+
* Stream.tap((n) => Effect.sync(() => events.push(`before mapping: ${n}`))),
|
|
2804
|
+
* Stream.map((n) => n * 2),
|
|
2805
|
+
* Stream.tap((n) => Effect.sync(() => events.push(`after mapping: ${n}`))),
|
|
2806
|
+
* Stream.runCollect
|
|
2807
|
+
* )
|
|
2808
|
+
*
|
|
2809
|
+
* result // => [2, 4, 6]
|
|
2810
|
+
* })
|
|
2811
|
+
*
|
|
2812
|
+
* await Effect.runPromise(program)
|
|
2813
|
+
* events // => ["before mapping: 1", "after mapping: 2", "before mapping: 2", "after mapping: 4", "before mapping: 3", "after mapping: 6"]
|
|
2814
|
+
* ```
|
|
2815
|
+
*
|
|
2816
|
+
* @stability stable
|
|
2817
|
+
* @category sequencing
|
|
2818
|
+
* @since 2.0.0
|
|
2819
|
+
*/
|
|
2820
|
+
const tap = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, f, options) => mapEffect(self, (a) => as(f(a), a), options));
|
|
2821
|
+
/**
|
|
2822
|
+
* Maps each element to a stream and flattens the resulting streams.
|
|
2823
|
+
*
|
|
2824
|
+
* **Details**
|
|
2825
|
+
*
|
|
2826
|
+
* With the default sequential concurrency, inner streams are concatenated in
|
|
2827
|
+
* input order. When `concurrency` is greater than `1` or `"unbounded"`,
|
|
2828
|
+
* multiple inner streams may run at the same time and their outputs are merged
|
|
2829
|
+
* as they arrive.
|
|
2830
|
+
*
|
|
2831
|
+
* **Example** (Flat mapping stream values)
|
|
2832
|
+
*
|
|
2833
|
+
* ```ts import.meta.vitest
|
|
2834
|
+
* import { Effect, Stream } from "effect"
|
|
2835
|
+
*
|
|
2836
|
+
* const program = Effect.gen(function*() {
|
|
2837
|
+
* const values = yield* Stream.make(1, 2, 3).pipe(
|
|
2838
|
+
* Stream.flatMap((n) => Stream.make(n, n * 2)),
|
|
2839
|
+
* Stream.runCollect
|
|
2840
|
+
* )
|
|
2841
|
+
* values // => [ 1, 2, 2, 4, 3, 6 ]
|
|
2842
|
+
* })
|
|
2843
|
+
*
|
|
2844
|
+
* await Effect.runPromise(program)
|
|
2845
|
+
* ```
|
|
2846
|
+
*
|
|
2847
|
+
* @stability stable
|
|
2848
|
+
* @category sequencing
|
|
2849
|
+
* @since 2.0.0
|
|
2850
|
+
*/
|
|
2851
|
+
const flatMap = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, f, options) => self.channel.pipe(flattenArray, flatMap$1((a) => f(a).channel, options), fromChannel));
|
|
2852
|
+
/**
|
|
2853
|
+
* Converts this stream to one that runs its effects but emits no elements.
|
|
2854
|
+
*
|
|
2855
|
+
* **Example** (Draining stream values)
|
|
2856
|
+
*
|
|
2857
|
+
* ```ts import.meta.vitest
|
|
2858
|
+
* import { Effect, Stream } from "effect"
|
|
2859
|
+
*
|
|
2860
|
+
* const program = Effect.gen(function*() {
|
|
2861
|
+
* const result = yield* Stream.range(1, 6).pipe(Stream.drain, Stream.runCollect)
|
|
2862
|
+
* result // => []
|
|
2863
|
+
* })
|
|
2864
|
+
*
|
|
2865
|
+
* await Effect.runPromise(program)
|
|
2866
|
+
* ```
|
|
2867
|
+
*
|
|
2868
|
+
* @stability stable
|
|
2869
|
+
* @category sequencing
|
|
2870
|
+
* @since 2.0.0
|
|
2871
|
+
*/
|
|
2872
|
+
const drain = (self) => fromChannel(drain$1(self.channel));
|
|
2873
|
+
/**
|
|
2874
|
+
* Runs the provided stream in the background while this stream runs, interrupting it
|
|
2875
|
+
* when this stream completes and failing if the background stream fails or defects.
|
|
2876
|
+
*
|
|
2877
|
+
* **Example** (Draining a stream in the background)
|
|
2878
|
+
*
|
|
2879
|
+
* ```ts import.meta.vitest
|
|
2880
|
+
* import { Effect, Stream } from "effect"
|
|
2881
|
+
*
|
|
2882
|
+
* const events: Array<string> = []
|
|
2883
|
+
* const foreground = Stream.make(1, 2)
|
|
2884
|
+
* const background = Stream.fromEffect(Effect.sync(() => events.push("background task")))
|
|
2885
|
+
*
|
|
2886
|
+
* const program = Effect.gen(function*() {
|
|
2887
|
+
* const values = yield* foreground.pipe(
|
|
2888
|
+
* Stream.drainFork(background),
|
|
2889
|
+
* Stream.runCollect
|
|
2890
|
+
* )
|
|
2891
|
+
* values // => [1, 2]
|
|
2892
|
+
* })
|
|
2893
|
+
*
|
|
2894
|
+
* await Effect.runPromise(program)
|
|
2895
|
+
* events // => ["background task"]
|
|
2896
|
+
* ```
|
|
2897
|
+
*
|
|
2898
|
+
* @stability stable
|
|
2899
|
+
* @category sequencing
|
|
2900
|
+
* @since 2.0.0
|
|
2901
|
+
*/
|
|
2902
|
+
const drainFork = /*#__PURE__*/ dual(2, (self, that) => mergeEffect(self, runDrain(that)));
|
|
2903
|
+
/**
|
|
2904
|
+
* Concatenates two streams, emitting all elements from the first stream
|
|
2905
|
+
* followed by all elements from the second stream.
|
|
2906
|
+
*
|
|
2907
|
+
* **Example** (Concatenating streams)
|
|
2908
|
+
*
|
|
2909
|
+
* ```ts import.meta.vitest
|
|
2910
|
+
* import { Effect, Stream } from "effect"
|
|
2911
|
+
*
|
|
2912
|
+
* const stream = Stream.concat(Stream.make(1, 2, 3), Stream.make(4, 5, 6))
|
|
2913
|
+
*
|
|
2914
|
+
* await Effect.runPromise(Effect.gen(function*() {
|
|
2915
|
+
* const values = yield* Stream.runCollect(stream)
|
|
2916
|
+
* values // => [ 1, 2, 3, 4, 5, 6 ]
|
|
2917
|
+
* }))
|
|
2918
|
+
* ```
|
|
2919
|
+
*
|
|
2920
|
+
* @stability stable
|
|
2921
|
+
* @category sequencing
|
|
2922
|
+
* @since 2.0.0
|
|
2923
|
+
*/
|
|
2924
|
+
const concat = /*#__PURE__*/ dual(2, (self, that) => {
|
|
2925
|
+
const stream = fromChannel(flatten(fromIterator(() => concatChannels(self, that))));
|
|
2926
|
+
concatParts.set(stream, [self, that]);
|
|
2927
|
+
return stream;
|
|
2928
|
+
});
|
|
2929
|
+
const concatParts = /*#__PURE__*/ new WeakMap();
|
|
2930
|
+
function* concatChannels(self, that) {
|
|
2931
|
+
const stack = [that, self];
|
|
2932
|
+
while (stack.length > 0) {
|
|
2933
|
+
const stream = stack.pop();
|
|
2934
|
+
const parts = concatParts.get(stream);
|
|
2935
|
+
if (parts !== void 0) {
|
|
2936
|
+
stack.push(parts[1], parts[0]);
|
|
2937
|
+
continue;
|
|
2938
|
+
}
|
|
2939
|
+
const evaluate = suspendedStreams.get(stream);
|
|
2940
|
+
if (evaluate === void 0) {
|
|
2941
|
+
yield stream.channel;
|
|
2942
|
+
continue;
|
|
2943
|
+
}
|
|
2944
|
+
try {
|
|
2945
|
+
stack.push(evaluate());
|
|
2946
|
+
} catch (defect) {
|
|
2947
|
+
stack.push(die(defect));
|
|
2948
|
+
}
|
|
2949
|
+
yield empty$1;
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
/**
|
|
2953
|
+
* Merges two streams, emitting elements from both as they arrive.
|
|
2954
|
+
*
|
|
2955
|
+
* **Details**
|
|
2956
|
+
*
|
|
2957
|
+
* By default, the merged stream ends when both streams end. Use
|
|
2958
|
+
* `haltStrategy` to change the termination behavior.
|
|
2959
|
+
*
|
|
2960
|
+
* **Example** (Merging stream values)
|
|
2961
|
+
*
|
|
2962
|
+
* ```ts import.meta.vitest
|
|
2963
|
+
* import { Effect, Stream } from "effect"
|
|
2964
|
+
*
|
|
2965
|
+
* const fast = Stream.make(1, 2, 3)
|
|
2966
|
+
* const slow = Stream.fromEffect(Effect.delay(Effect.succeed(4), "50 millis"))
|
|
2967
|
+
*
|
|
2968
|
+
* const program = Effect.gen(function*() {
|
|
2969
|
+
* const result = yield* Stream.runCollect(Stream.merge(fast, slow))
|
|
2970
|
+
* result // => [ 1, 2, 3, 4 ]
|
|
2971
|
+
* })
|
|
2972
|
+
*
|
|
2973
|
+
* await Effect.runPromise(program)
|
|
2974
|
+
* ```
|
|
2975
|
+
*
|
|
2976
|
+
* @stability stable
|
|
2977
|
+
* @category merging
|
|
2978
|
+
* @since 2.0.0
|
|
2979
|
+
*/
|
|
2980
|
+
const merge = /*#__PURE__*/ dual((args) => isStream(args[0]) && isStream(args[1]), (self, that, options) => fromChannel(merge$1(toChannel(self), toChannel(that), options)));
|
|
2981
|
+
/**
|
|
2982
|
+
* Merges this stream with a background effect, keeping the stream's elements.
|
|
2983
|
+
*
|
|
2984
|
+
* **When to use**
|
|
2985
|
+
*
|
|
2986
|
+
* Use when an effect should run concurrently for the lifetime of a stream while
|
|
2987
|
+
* only the stream's elements remain in the output.
|
|
2988
|
+
*
|
|
2989
|
+
* **Details**
|
|
2990
|
+
*
|
|
2991
|
+
* The effect runs concurrently, fails the stream if it fails, and is interrupted
|
|
2992
|
+
* when the stream completes.
|
|
2993
|
+
*
|
|
2994
|
+
* **Example** (Merging with a background effect)
|
|
2995
|
+
*
|
|
2996
|
+
* ```ts import.meta.vitest
|
|
2997
|
+
* import { Effect, Stream } from "effect"
|
|
2998
|
+
*
|
|
2999
|
+
* const events: Array<string> = []
|
|
3000
|
+
* const program = Effect.gen(function*() {
|
|
3001
|
+
* const values = yield* Stream.make(1, 2, 3).pipe(
|
|
3002
|
+
* Stream.mergeEffect(Effect.sync(() => events.push("side task"))),
|
|
3003
|
+
* Stream.runCollect
|
|
3004
|
+
* )
|
|
3005
|
+
*
|
|
3006
|
+
* values // => [1, 2, 3]
|
|
3007
|
+
* })
|
|
3008
|
+
*
|
|
3009
|
+
* await Effect.runPromise(program)
|
|
3010
|
+
* events // => ["side task"]
|
|
3011
|
+
* ```
|
|
3012
|
+
*
|
|
3013
|
+
* @stability stable
|
|
3014
|
+
* @category merging
|
|
3015
|
+
* @since 4.0.0
|
|
3016
|
+
*/
|
|
3017
|
+
const mergeEffect = /*#__PURE__*/ dual(2, (self, effect) => self.channel.pipe(mergeEffect$1(effect), fromChannel));
|
|
3018
|
+
/**
|
|
3019
|
+
* Filters a stream to the elements that satisfy a predicate.
|
|
3020
|
+
*
|
|
3021
|
+
* **Example** (Filtering stream values)
|
|
3022
|
+
*
|
|
3023
|
+
* ```ts import.meta.vitest
|
|
3024
|
+
* import { Effect, Stream } from "effect"
|
|
3025
|
+
*
|
|
3026
|
+
* const program = Effect.gen(function*() {
|
|
3027
|
+
* const stream = Stream.make(1, 2, 3, 4).pipe(
|
|
3028
|
+
* Stream.filter((n) => n % 2 === 0)
|
|
3029
|
+
* )
|
|
3030
|
+
* const values = yield* Stream.runCollect(stream)
|
|
3031
|
+
* values // => [ 2, 4 ]
|
|
3032
|
+
* })
|
|
3033
|
+
*
|
|
3034
|
+
* await Effect.runPromise(program)
|
|
3035
|
+
* ```
|
|
3036
|
+
*
|
|
3037
|
+
* @stability stable
|
|
3038
|
+
* @category filtering
|
|
3039
|
+
* @since 2.0.0
|
|
3040
|
+
*/
|
|
3041
|
+
const filter = /*#__PURE__*/ dual(2, (self, predicate) => fromChannel(filterArray(toChannel(self), predicate)));
|
|
3042
|
+
/**
|
|
3043
|
+
* Buffers up to `capacity` elements so a faster producer can progress
|
|
3044
|
+
* independently of a slower consumer.
|
|
3045
|
+
*
|
|
3046
|
+
* **Details**
|
|
3047
|
+
*
|
|
3048
|
+
* Finite buffers use the configured queue strategy: `"suspend"` applies
|
|
3049
|
+
* backpressure, while `"dropping"` and `"sliding"` may discard elements when
|
|
3050
|
+
* the buffer is full. This combinator destroys chunking; use `Stream.rechunk`
|
|
3051
|
+
* afterward if you need fixed chunk sizes.
|
|
3052
|
+
*
|
|
3053
|
+
* **Example** (Buffering stream elements)
|
|
3054
|
+
*
|
|
3055
|
+
* ```ts import.meta.vitest
|
|
3056
|
+
* import { Effect, Stream } from "effect"
|
|
3057
|
+
*
|
|
3058
|
+
* const program = Effect.gen(function*() {
|
|
3059
|
+
* const values = yield* Stream.make(1, 2, 3).pipe(
|
|
3060
|
+
* Stream.buffer({ capacity: 1 }),
|
|
3061
|
+
* Stream.runCollect
|
|
3062
|
+
* )
|
|
3063
|
+
* values // => [ 1, 2, 3 ]
|
|
3064
|
+
* })
|
|
3065
|
+
*
|
|
3066
|
+
* await Effect.runPromise(program)
|
|
3067
|
+
* ```
|
|
3068
|
+
*
|
|
3069
|
+
* @stability stable
|
|
3070
|
+
* @category buffering
|
|
3071
|
+
* @since 2.0.0
|
|
3072
|
+
*/
|
|
3073
|
+
const buffer = /*#__PURE__*/ dual(2, (self, options) => fromChannel(bufferArray(self.channel, options)));
|
|
3074
|
+
/**
|
|
3075
|
+
* Peeks at errors effectfully without changing the stream unless the tap fails.
|
|
3076
|
+
*
|
|
3077
|
+
* **Example** (Effectfully peeking at errors)
|
|
3078
|
+
*
|
|
3079
|
+
* ```ts import.meta.vitest
|
|
3080
|
+
* import { Effect, Stream } from "effect"
|
|
3081
|
+
*
|
|
3082
|
+
* const errors: Array<string> = []
|
|
3083
|
+
* const stream = Stream.make(1, 2).pipe(
|
|
3084
|
+
* Stream.concat(Stream.fail("boom")),
|
|
3085
|
+
* Stream.tapError((error) => Effect.sync(() => errors.push(error))),
|
|
3086
|
+
* Stream.catch(() => Stream.make(999))
|
|
3087
|
+
* )
|
|
3088
|
+
*
|
|
3089
|
+
* const program = Effect.gen(function*() {
|
|
3090
|
+
* const values = yield* Stream.runCollect(stream)
|
|
3091
|
+
* values // => [1, 2, 999]
|
|
3092
|
+
* })
|
|
3093
|
+
*
|
|
3094
|
+
* await Effect.runPromise(program)
|
|
3095
|
+
* errors // => ["boom"]
|
|
3096
|
+
* ```
|
|
3097
|
+
*
|
|
3098
|
+
* @stability stable
|
|
3099
|
+
* @category error handling
|
|
3100
|
+
* @since 2.0.0
|
|
3101
|
+
*/
|
|
3102
|
+
const tapError = /*#__PURE__*/ dual(2, (self, f) => self.channel.pipe(tapError$1(f), fromChannel));
|
|
3103
|
+
/**
|
|
3104
|
+
* Transforms the errors emitted by this stream using `f`.
|
|
3105
|
+
*
|
|
3106
|
+
* **Details**
|
|
3107
|
+
*
|
|
3108
|
+
* Maps every typed error in the cause, preserving defects, interrupts and
|
|
3109
|
+
* reason order. Unlike typed recovery, mapping does not convert errors to defects.
|
|
3110
|
+
*
|
|
3111
|
+
* **Example** (Mapping stream errors)
|
|
3112
|
+
*
|
|
3113
|
+
* ```ts import.meta.vitest
|
|
3114
|
+
* import { Effect, Stream } from "effect"
|
|
3115
|
+
*
|
|
3116
|
+
* const program = Effect.gen(function*() {
|
|
3117
|
+
* const result = yield* Stream.fail("bad").pipe(
|
|
3118
|
+
* Stream.mapError((error) => `mapped: ${error}`),
|
|
3119
|
+
* Stream.catch((error) => Stream.make(`recovered from ${error}`)),
|
|
3120
|
+
* Stream.runCollect
|
|
3121
|
+
* )
|
|
3122
|
+
* result // => [ 'recovered from mapped: bad' ]
|
|
3123
|
+
* })
|
|
3124
|
+
*
|
|
3125
|
+
* await Effect.runPromise(program)
|
|
3126
|
+
* ```
|
|
3127
|
+
*
|
|
3128
|
+
* @stability stable
|
|
3129
|
+
* @category error handling
|
|
3130
|
+
* @since 2.0.0
|
|
3131
|
+
*/
|
|
3132
|
+
const mapError = /*#__PURE__*/ dual(2, (self, f) => fromChannel(mapError$1(self.channel, f)));
|
|
3133
|
+
/**
|
|
3134
|
+
* Retries the stream according to the given schedule when it fails.
|
|
3135
|
+
*
|
|
3136
|
+
* **Details**
|
|
3137
|
+
*
|
|
3138
|
+
* This retries the entire stream, so will re-execute all of the stream's
|
|
3139
|
+
* acquire operations.
|
|
3140
|
+
*
|
|
3141
|
+
* The schedule is reset as soon as the first element passes through the
|
|
3142
|
+
* stream again.
|
|
3143
|
+
*
|
|
3144
|
+
* **Gotchas**
|
|
3145
|
+
*
|
|
3146
|
+
* Defects take precedence over typed errors. A failure whose cause contains a
|
|
3147
|
+
* defect is not retried and is propagated with every typed error converted to a
|
|
3148
|
+
* defect. A typed error alongside an interruption is retried.
|
|
3149
|
+
*
|
|
3150
|
+
* **Example** (Retrying stream failures)
|
|
3151
|
+
*
|
|
3152
|
+
* ```ts import.meta.vitest
|
|
3153
|
+
* import { Effect, Schedule, Stream } from "effect"
|
|
3154
|
+
*
|
|
3155
|
+
* const program = Effect.gen(function*() {
|
|
3156
|
+
* const values = yield* Stream.make(1).pipe(
|
|
3157
|
+
* Stream.concat(Stream.fail("boom")),
|
|
3158
|
+
* Stream.retry(Schedule.recurs(1)),
|
|
3159
|
+
* Stream.take(2),
|
|
3160
|
+
* Stream.runCollect
|
|
3161
|
+
* )
|
|
3162
|
+
*
|
|
3163
|
+
* values // => [ 1, 1 ]
|
|
3164
|
+
* })
|
|
3165
|
+
*
|
|
3166
|
+
* await Effect.runPromise(program)
|
|
3167
|
+
* ```
|
|
3168
|
+
*
|
|
3169
|
+
* @stability stable
|
|
3170
|
+
* @category error handling
|
|
3171
|
+
* @since 2.0.0
|
|
3172
|
+
*/
|
|
3173
|
+
const retry = /*#__PURE__*/ dual(2, (self, policy) => fromChannel(retry$1(self.channel, policy)));
|
|
3174
|
+
/**
|
|
3175
|
+
* Takes the first `n` elements from this stream.
|
|
3176
|
+
*
|
|
3177
|
+
* **Details**
|
|
3178
|
+
*
|
|
3179
|
+
* Finite fractional values of `n` are rounded down. `NaN` and non-positive
|
|
3180
|
+
* values return `Stream.empty` without evaluating the source stream.
|
|
3181
|
+
*
|
|
3182
|
+
* **Example** (Taking values from the left)
|
|
3183
|
+
*
|
|
3184
|
+
* ```ts import.meta.vitest
|
|
3185
|
+
* import { Effect, Stream } from "effect"
|
|
3186
|
+
*
|
|
3187
|
+
* const program = Effect.gen(function*() {
|
|
3188
|
+
* const values = yield* Stream.make(1, 2, 3, 4, 5).pipe(
|
|
3189
|
+
* Stream.take(3),
|
|
3190
|
+
* Stream.runCollect
|
|
3191
|
+
* )
|
|
3192
|
+
* values // => [ 1, 2, 3 ]
|
|
3193
|
+
* })
|
|
3194
|
+
*
|
|
3195
|
+
* await Effect.runPromise(program)
|
|
3196
|
+
* ```
|
|
3197
|
+
*
|
|
3198
|
+
* @stability stable
|
|
3199
|
+
* @category filtering
|
|
3200
|
+
* @since 2.0.0
|
|
3201
|
+
*/
|
|
3202
|
+
const take = /*#__PURE__*/ dual(2, (self, n) => {
|
|
3203
|
+
const count = normalize(n);
|
|
3204
|
+
return count === 0 ? empty : takeUntil(self, (_, i) => i === count - 1);
|
|
3205
|
+
});
|
|
3206
|
+
/**
|
|
3207
|
+
* Takes elements until the predicate matches.
|
|
3208
|
+
*
|
|
3209
|
+
* **Details**
|
|
3210
|
+
*
|
|
3211
|
+
* When `excludeLast` is `true`, the matching element is dropped.
|
|
3212
|
+
*
|
|
3213
|
+
* **Example** (Taking until a predicate matches)
|
|
3214
|
+
*
|
|
3215
|
+
* ```ts import.meta.vitest
|
|
3216
|
+
* import { Effect, Stream } from "effect"
|
|
3217
|
+
*
|
|
3218
|
+
* const stream = Stream.range(1, 5)
|
|
3219
|
+
*
|
|
3220
|
+
* const program = Effect.gen(function*() {
|
|
3221
|
+
* const inclusive = yield* stream.pipe(
|
|
3222
|
+
* Stream.takeUntil((n) => n % 3 === 0),
|
|
3223
|
+
* Stream.runCollect
|
|
3224
|
+
* )
|
|
3225
|
+
* inclusive // => [ 1, 2, 3 ]
|
|
3226
|
+
*
|
|
3227
|
+
* const exclusive = yield* stream.pipe(
|
|
3228
|
+
* Stream.takeUntil((n) => n % 3 === 0, { excludeLast: true }),
|
|
3229
|
+
* Stream.runCollect
|
|
3230
|
+
* )
|
|
3231
|
+
* exclusive // => [ 1, 2 ]
|
|
3232
|
+
* })
|
|
3233
|
+
* await Effect.runPromise(program)
|
|
3234
|
+
* ```
|
|
3235
|
+
*
|
|
3236
|
+
* @stability stable
|
|
3237
|
+
* @category filtering
|
|
3238
|
+
* @since 2.0.0
|
|
3239
|
+
*/
|
|
3240
|
+
const takeUntil = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, predicate, options) => transformPull(self, (pull, _scope) => sync(() => {
|
|
3241
|
+
let i = 0;
|
|
3242
|
+
let done$6 = false;
|
|
3243
|
+
return flatMap$2(suspend$2(() => done$6 ? done() : pull), (chunk) => {
|
|
3244
|
+
const index = chunk.findIndex((a) => predicate(a, i++));
|
|
3245
|
+
if (index >= 0) {
|
|
3246
|
+
done$6 = true;
|
|
3247
|
+
const arr = chunk.slice(0, options?.excludeLast ? index : index + 1);
|
|
3248
|
+
return isReadonlyArrayNonEmpty(arr) ? succeed$2(arr) : done();
|
|
3249
|
+
}
|
|
3250
|
+
return succeed$2(chunk);
|
|
3251
|
+
});
|
|
3252
|
+
})));
|
|
3253
|
+
/**
|
|
3254
|
+
* Drops the first `n` elements from this stream.
|
|
3255
|
+
*
|
|
3256
|
+
* **Details**
|
|
3257
|
+
*
|
|
3258
|
+
* Finite fractional values of `n` are rounded down. `NaN` and non-positive
|
|
3259
|
+
* values return the source stream unchanged.
|
|
3260
|
+
*
|
|
3261
|
+
* **Example** (Dropping values from the left)
|
|
3262
|
+
*
|
|
3263
|
+
* ```ts import.meta.vitest
|
|
3264
|
+
* import { Effect, Stream } from "effect"
|
|
3265
|
+
*
|
|
3266
|
+
* const stream = Stream.make(1, 2, 3, 4, 5)
|
|
3267
|
+
* const result = Stream.drop(stream, 2)
|
|
3268
|
+
*
|
|
3269
|
+
* const program = Effect.gen(function*() {
|
|
3270
|
+
* const items = yield* Stream.runCollect(result)
|
|
3271
|
+
* items // => [ 3, 4, 5 ]
|
|
3272
|
+
* })
|
|
3273
|
+
*
|
|
3274
|
+
* await Effect.runPromise(program)
|
|
3275
|
+
* ```
|
|
3276
|
+
*
|
|
3277
|
+
* @stability stable
|
|
3278
|
+
* @category filtering
|
|
3279
|
+
* @since 2.0.0
|
|
3280
|
+
*/
|
|
3281
|
+
const drop = /*#__PURE__*/ dual(2, (self, n) => {
|
|
3282
|
+
const count = normalize(n);
|
|
3283
|
+
if (count === 0) return self;
|
|
3284
|
+
return transformPull(self, (pull, _scope) => sync(() => {
|
|
3285
|
+
let dropped = 0;
|
|
3286
|
+
const pump = pull.pipe(flatMap$2((chunk) => {
|
|
3287
|
+
if (dropped >= count) return succeed$2(chunk);
|
|
3288
|
+
dropped += chunk.length;
|
|
3289
|
+
if (dropped <= count) return pump;
|
|
3290
|
+
return succeed$2(chunk.slice(count - dropped));
|
|
3291
|
+
}));
|
|
3292
|
+
return pump;
|
|
3293
|
+
}));
|
|
3294
|
+
});
|
|
3295
|
+
/**
|
|
3296
|
+
* Maps elements statefully, emitting zero or more outputs per input.
|
|
3297
|
+
*
|
|
3298
|
+
* **Example** (Statefully mapping stream values)
|
|
3299
|
+
*
|
|
3300
|
+
* ```ts import.meta.vitest
|
|
3301
|
+
* import { Console, Effect, Stream } from "effect"
|
|
3302
|
+
*
|
|
3303
|
+
* const program = Effect.gen(function*() {
|
|
3304
|
+
* const totals = yield* Stream.make(0, 1, 2, 3, 4, 5, 6).pipe(
|
|
3305
|
+
* Stream.mapAccum(() => 0, (total, n) => {
|
|
3306
|
+
* const next = total + n
|
|
3307
|
+
* return [next, [next]] as const
|
|
3308
|
+
* }),
|
|
3309
|
+
* Stream.runCollect
|
|
3310
|
+
* )
|
|
3311
|
+
*
|
|
3312
|
+
* totals // => [0, 1, 3, 6, 10, 15, 21]
|
|
3313
|
+
* })
|
|
3314
|
+
*
|
|
3315
|
+
* await Effect.runPromise(program)
|
|
3316
|
+
* ```
|
|
3317
|
+
*
|
|
3318
|
+
* @stability stable
|
|
3319
|
+
* @category mapping
|
|
3320
|
+
* @since 2.0.0
|
|
3321
|
+
*/
|
|
3322
|
+
const mapAccum = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, initial, f, options) => fromChannel(mapAccum$1(self.channel, initial, (state, arr) => {
|
|
3323
|
+
const acc = empty$2();
|
|
3324
|
+
for (let index = 0; index < arr.length; index++) {
|
|
3325
|
+
const [newState, values] = f(state, arr[index]);
|
|
3326
|
+
state = newState;
|
|
3327
|
+
for (let i = 0; i < values.length; i++) acc.push(values[i]);
|
|
3328
|
+
}
|
|
3329
|
+
return [state, isArrayNonEmpty(acc) ? of(acc) : emptyArr];
|
|
3330
|
+
}, options?.onHalt ? { onHalt(state) {
|
|
3331
|
+
const arr = options.onHalt(state);
|
|
3332
|
+
return isReadonlyArrayNonEmpty(arr) ? of(arr) : emptyArr;
|
|
3333
|
+
} } : void 0)));
|
|
3334
|
+
const emptyArr = /*#__PURE__*/ empty$2();
|
|
3335
|
+
/**
|
|
3336
|
+
* Applies a sink transducer to the stream and emits each sink result.
|
|
3337
|
+
*
|
|
3338
|
+
* **Example** (Transducing with a sink)
|
|
3339
|
+
*
|
|
3340
|
+
* ```ts import.meta.vitest
|
|
3341
|
+
* import { Effect, Sink, Stream } from "effect"
|
|
3342
|
+
*
|
|
3343
|
+
* const program = Effect.gen(function* () {
|
|
3344
|
+
* const result = yield* Stream.make(1, 2, 3, 4).pipe(
|
|
3345
|
+
* Stream.transduce(Sink.take(2)),
|
|
3346
|
+
* Stream.runCollect
|
|
3347
|
+
* )
|
|
3348
|
+
*
|
|
3349
|
+
* result // => [ [ 1, 2 ], [ 3, 4 ], [] ]
|
|
3350
|
+
* })
|
|
3351
|
+
* await Effect.runPromise(program)
|
|
3352
|
+
* ```
|
|
3353
|
+
*
|
|
3354
|
+
* @stability stable
|
|
3355
|
+
* @category aggregation
|
|
3356
|
+
* @since 2.0.0
|
|
3357
|
+
*/
|
|
3358
|
+
const transduce = /*#__PURE__*/ dual(2, (self, sink) => transformPull(self, (upstream, scope) => sync(() => {
|
|
3359
|
+
let done$3;
|
|
3360
|
+
let leftover;
|
|
3361
|
+
const upstreamWithLeftover = suspend$2(() => {
|
|
3362
|
+
if (leftover !== void 0) {
|
|
3363
|
+
const chunk = leftover;
|
|
3364
|
+
leftover = void 0;
|
|
3365
|
+
return succeed$2(chunk);
|
|
3366
|
+
}
|
|
3367
|
+
return upstream;
|
|
3368
|
+
}).pipe(catch_((error) => {
|
|
3369
|
+
done$3 = fail$3(error);
|
|
3370
|
+
return done();
|
|
3371
|
+
}));
|
|
3372
|
+
const pull = map$3(suspend$2(() => sink.transform(upstreamWithLeftover, scope)), ([value, leftover_]) => {
|
|
3373
|
+
leftover = leftover_;
|
|
3374
|
+
return of(value);
|
|
3375
|
+
});
|
|
3376
|
+
return suspend$2(() => done$3 ? done$3 : pull);
|
|
3377
|
+
})));
|
|
3378
|
+
/**
|
|
3379
|
+
* Pipes this stream through a channel that consumes and emits chunked elements.
|
|
3380
|
+
*
|
|
3381
|
+
* **Details**
|
|
3382
|
+
*
|
|
3383
|
+
* The channel receives `NonEmptyReadonlyArray` chunks and can transform both the
|
|
3384
|
+
* output elements and error type.
|
|
3385
|
+
*
|
|
3386
|
+
* **Example** (Piping through a channel)
|
|
3387
|
+
*
|
|
3388
|
+
* ```ts import.meta.vitest
|
|
3389
|
+
* import { Array, Channel, Effect, Stream } from "effect"
|
|
3390
|
+
*
|
|
3391
|
+
* type NumberChunk = readonly [number, ...Array<number>]
|
|
3392
|
+
*
|
|
3393
|
+
* const doubleChunks = Channel.identity<NumberChunk, never, unknown>().pipe(
|
|
3394
|
+
* Channel.map((chunk) => Array.map(chunk, (n) => n * 2))
|
|
3395
|
+
* )
|
|
3396
|
+
*
|
|
3397
|
+
* const program = Effect.gen(function*() {
|
|
3398
|
+
* const result = yield* Stream.fromArray([1, 2, 3]).pipe(
|
|
3399
|
+
* Stream.rechunk(2),
|
|
3400
|
+
* Stream.pipeThroughChannel(doubleChunks),
|
|
3401
|
+
* Stream.runCollect
|
|
3402
|
+
* )
|
|
3403
|
+
* result // => [ 2, 4, 6 ]
|
|
3404
|
+
* })
|
|
3405
|
+
*
|
|
3406
|
+
* await Effect.runPromise(program)
|
|
3407
|
+
* ```
|
|
3408
|
+
*
|
|
3409
|
+
* @stability stable
|
|
3410
|
+
* @category sequencing
|
|
3411
|
+
* @since 2.0.0
|
|
3412
|
+
*/
|
|
3413
|
+
const pipeThroughChannel = /*#__PURE__*/ dual(2, (self, channel) => fromChannel(pipeTo(self.channel, channel)));
|
|
3414
|
+
/**
|
|
3415
|
+
* Decodes Uint8Array chunks into strings using TextDecoder with an optional encoding.
|
|
3416
|
+
*
|
|
3417
|
+
* **Example** (Decoding Uint8Array chunks into strings using TextDecoder with an optional encoding)
|
|
3418
|
+
*
|
|
3419
|
+
* ```ts import.meta.vitest
|
|
3420
|
+
* import { Effect, Stream } from "effect"
|
|
3421
|
+
*
|
|
3422
|
+
* const encoder = new TextEncoder()
|
|
3423
|
+
* const stream = Stream.make(
|
|
3424
|
+
* encoder.encode("Hello"),
|
|
3425
|
+
* encoder.encode(" World")
|
|
3426
|
+
* )
|
|
3427
|
+
*
|
|
3428
|
+
* const program = Effect.gen(function*() {
|
|
3429
|
+
* const decoded = yield* stream.pipe(
|
|
3430
|
+
* Stream.decodeText,
|
|
3431
|
+
* Stream.runCollect
|
|
3432
|
+
* )
|
|
3433
|
+
* decoded // => [ 'Hello', ' World' ]
|
|
3434
|
+
* })
|
|
3435
|
+
*
|
|
3436
|
+
* await Effect.runPromise(program)
|
|
3437
|
+
* ```
|
|
3438
|
+
*
|
|
3439
|
+
* @stability stable
|
|
3440
|
+
* @category text
|
|
3441
|
+
* @since 2.0.0
|
|
3442
|
+
*/
|
|
3443
|
+
const decodeText = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, options) => self.channel.pipe(pipeTo(decodeText$1(options?.encoding)), fromChannel));
|
|
3444
|
+
/**
|
|
3445
|
+
* Encodes a stream of strings into UTF-8 `Uint8Array` chunks.
|
|
3446
|
+
*
|
|
3447
|
+
* **Example** (Encoding a stream of strings into UTF-8 Uint8Array chunks)
|
|
3448
|
+
*
|
|
3449
|
+
* ```ts import.meta.vitest
|
|
3450
|
+
* import { Effect, Stream } from "effect"
|
|
3451
|
+
*
|
|
3452
|
+
* const stream = Stream.make("Hello", " ", "World")
|
|
3453
|
+
* const program = Effect.gen(function*() {
|
|
3454
|
+
* const encoded = Stream.encodeText(stream)
|
|
3455
|
+
* const chunks = yield* Stream.runCollect(encoded)
|
|
3456
|
+
* const bytes = chunks.map((chunk) => [...chunk])
|
|
3457
|
+
* bytes // => [ [ 72, 101, 108, 108, 111 ], [ 32 ], [ 87, 111, 114, 108, 100 ] ]
|
|
3458
|
+
* })
|
|
3459
|
+
*
|
|
3460
|
+
* await Effect.runPromise(program)
|
|
3461
|
+
* ```
|
|
3462
|
+
*
|
|
3463
|
+
* @stability stable
|
|
3464
|
+
* @category text
|
|
3465
|
+
* @since 2.0.0
|
|
3466
|
+
*/
|
|
3467
|
+
const encodeText = (self) => suspend(() => {
|
|
3468
|
+
const encoder = new TextEncoder();
|
|
3469
|
+
return map(self, (chunk) => encoder.encode(chunk));
|
|
3470
|
+
});
|
|
3471
|
+
/**
|
|
3472
|
+
* Splits a stream of strings into lines, handling `\n`, `\r`, and `\r\n` delimiters across chunks.
|
|
3473
|
+
*
|
|
3474
|
+
* **Example** (Splitting streamed text into lines)
|
|
3475
|
+
*
|
|
3476
|
+
* ```ts import.meta.vitest
|
|
3477
|
+
* import { Effect, Stream } from "effect"
|
|
3478
|
+
*
|
|
3479
|
+
* await Effect.runPromise(Effect.gen(function* () {
|
|
3480
|
+
* const lines = yield* Stream.runCollect(
|
|
3481
|
+
* Stream.make("a\nb\r\n", "c\n").pipe(Stream.splitLines)
|
|
3482
|
+
* )
|
|
3483
|
+
* lines // => [ 'a', 'b', 'c' ]
|
|
3484
|
+
* }))
|
|
3485
|
+
* ```
|
|
3486
|
+
*
|
|
3487
|
+
* @stability stable
|
|
3488
|
+
* @category text
|
|
3489
|
+
* @since 2.0.0
|
|
3490
|
+
*/
|
|
3491
|
+
const splitLines = (self) => self.channel.pipe(pipeTo(splitLines$1()), fromChannel);
|
|
3492
|
+
/**
|
|
3493
|
+
* Interrupts the evaluation of this stream when the provided effect
|
|
3494
|
+
* completes. The given effect will be forked as part of this stream, and its
|
|
3495
|
+
* success will be discarded. This combinator will also interrupt any
|
|
3496
|
+
* in-progress element being pulled from upstream.
|
|
3497
|
+
*
|
|
3498
|
+
* **Details**
|
|
3499
|
+
*
|
|
3500
|
+
* If the effect completes with a failure before the stream completes, the
|
|
3501
|
+
* returned stream will emit that failure.
|
|
3502
|
+
*
|
|
3503
|
+
* **Example** (Interrupting when an effect completes)
|
|
3504
|
+
*
|
|
3505
|
+
* ```ts import.meta.vitest
|
|
3506
|
+
* import { Deferred, Effect, Stream } from "effect"
|
|
3507
|
+
*
|
|
3508
|
+
* const program = Effect.gen(function*() {
|
|
3509
|
+
* const interrupt = yield* Deferred.make<void>()
|
|
3510
|
+
* const stream = Stream.make(1, 2, 3).pipe(
|
|
3511
|
+
* Stream.tap((value) =>
|
|
3512
|
+
* value === 2
|
|
3513
|
+
* ? Deferred.succeed(interrupt, void 0)
|
|
3514
|
+
* : Effect.void
|
|
3515
|
+
* ),
|
|
3516
|
+
* Stream.interruptWhen(Deferred.await(interrupt))
|
|
3517
|
+
* )
|
|
3518
|
+
*
|
|
3519
|
+
* const result = yield* Stream.runCollect(stream)
|
|
3520
|
+
* result // => [ 1 ]
|
|
3521
|
+
* })
|
|
3522
|
+
*
|
|
3523
|
+
* await Effect.runPromise(program)
|
|
3524
|
+
* ```
|
|
3525
|
+
*
|
|
3526
|
+
* @stability stable
|
|
3527
|
+
* @category interruption
|
|
3528
|
+
* @since 2.0.0
|
|
3529
|
+
*/
|
|
3530
|
+
const interruptWhen = /*#__PURE__*/ dual(2, (self, effect) => fromChannel(interruptWhen$1(self.channel, effect)));
|
|
3531
|
+
/**
|
|
3532
|
+
* Executes the provided finalizer after this stream's finalizers run.
|
|
3533
|
+
*
|
|
3534
|
+
* **Example** (Ensuring finalization)
|
|
3535
|
+
*
|
|
3536
|
+
* ```ts import.meta.vitest
|
|
3537
|
+
* import { Effect, Stream } from "effect"
|
|
3538
|
+
*
|
|
3539
|
+
* const events: Array<string> = []
|
|
3540
|
+
* const stream = Stream.fromArray([1, 2]).pipe(
|
|
3541
|
+
* Stream.ensuring(Effect.sync(() => events.push("cleanup")))
|
|
3542
|
+
* )
|
|
3543
|
+
*
|
|
3544
|
+
* const program = Effect.gen(function*() {
|
|
3545
|
+
* const collected = yield* Stream.runCollect(stream)
|
|
3546
|
+
* collected // => [1, 2]
|
|
3547
|
+
* })
|
|
3548
|
+
*
|
|
3549
|
+
* await Effect.runPromise(program)
|
|
3550
|
+
* events // => ["cleanup"]
|
|
3551
|
+
* ```
|
|
3552
|
+
*
|
|
3553
|
+
* @stability stable
|
|
3554
|
+
* @category resource management
|
|
3555
|
+
* @since 2.0.0
|
|
3556
|
+
*/
|
|
3557
|
+
const ensuring = /*#__PURE__*/ dual(2, (self, finalizer) => fromChannel(ensuring$1(self.channel, finalizer)));
|
|
3558
|
+
/**
|
|
3559
|
+
* Provides multiple services to the stream using a context.
|
|
3560
|
+
*
|
|
3561
|
+
* **Example** (Providing multiple services to the stream using a context)
|
|
3562
|
+
*
|
|
3563
|
+
* ```ts import.meta.vitest
|
|
3564
|
+
* import { Context, Effect, Stream } from "effect"
|
|
3565
|
+
*
|
|
3566
|
+
* class Config extends Context.Service<Config, { readonly prefix: string }>()("Config") {}
|
|
3567
|
+
* class Greeter extends Context.Service<Greeter, { greet: (name: string) => string }>()("Greeter") {}
|
|
3568
|
+
*
|
|
3569
|
+
* const context = Context.make(Config, { prefix: "Hello" }).pipe(
|
|
3570
|
+
* Context.add(Greeter, { greet: (name: string) => `${name}!` })
|
|
3571
|
+
* )
|
|
3572
|
+
*
|
|
3573
|
+
* const stream = Stream.fromEffect(
|
|
3574
|
+
* Effect.gen(function*() {
|
|
3575
|
+
* const config = yield* Effect.service(Config)
|
|
3576
|
+
* const greeter = yield* Effect.service(Greeter)
|
|
3577
|
+
* return greeter.greet(config.prefix)
|
|
3578
|
+
* })
|
|
3579
|
+
* )
|
|
3580
|
+
*
|
|
3581
|
+
* const program = Effect.gen(function*() {
|
|
3582
|
+
* const result = yield* Stream.runCollect(Stream.provideContext(stream, context))
|
|
3583
|
+
* result // => [ 'Hello!' ]
|
|
3584
|
+
* })
|
|
3585
|
+
*
|
|
3586
|
+
* await Effect.runPromise(program)
|
|
3587
|
+
* ```
|
|
3588
|
+
*
|
|
3589
|
+
* @stability stable
|
|
3590
|
+
* @category providing services
|
|
3591
|
+
* @since 2.0.0
|
|
3592
|
+
*/
|
|
3593
|
+
const provideContext = /*#__PURE__*/ dual(2, (self, context) => fromChannel(provideContext$1(self.channel, context)));
|
|
3594
|
+
/**
|
|
3595
|
+
* Provides the stream with a single required service, eliminating that
|
|
3596
|
+
* requirement from its environment.
|
|
3597
|
+
*
|
|
3598
|
+
* **Example** (Providing a stream service)
|
|
3599
|
+
*
|
|
3600
|
+
* ```ts import.meta.vitest
|
|
3601
|
+
* import { Context, Effect, Stream } from "effect"
|
|
3602
|
+
*
|
|
3603
|
+
* class Greeter extends Context.Service<Greeter, {
|
|
3604
|
+
* greet: (name: string) => string
|
|
3605
|
+
* }>()("Greeter") {}
|
|
3606
|
+
*
|
|
3607
|
+
* const stream = Stream.fromEffect(
|
|
3608
|
+
* Effect.service(Greeter).pipe(
|
|
3609
|
+
* Effect.map((greeter) => greeter.greet("Ada"))
|
|
3610
|
+
* )
|
|
3611
|
+
* )
|
|
3612
|
+
*
|
|
3613
|
+
* const program = Effect.gen(function*() {
|
|
3614
|
+
* const collected = yield* Stream.runCollect(
|
|
3615
|
+
* stream.pipe(
|
|
3616
|
+
* Stream.provideService(Greeter, {
|
|
3617
|
+
* greet: (name) => `Hello, ${name}`
|
|
3618
|
+
* })
|
|
3619
|
+
* )
|
|
3620
|
+
* )
|
|
3621
|
+
* collected // => [ 'Hello, Ada' ]
|
|
3622
|
+
* })
|
|
3623
|
+
*
|
|
3624
|
+
* await Effect.runPromise(program)
|
|
3625
|
+
* ```
|
|
3626
|
+
*
|
|
3627
|
+
* @stability stable
|
|
3628
|
+
* @category providing services
|
|
3629
|
+
* @since 2.0.0
|
|
3630
|
+
*/
|
|
3631
|
+
const provideService = /*#__PURE__*/ dual(3, (self, key, service) => fromChannel(provideService$1(self.channel, key, service)));
|
|
3632
|
+
/**
|
|
3633
|
+
* Runs a stream with a sink and returns the sink result.
|
|
3634
|
+
*
|
|
3635
|
+
* **Example** (Running a stream with a sink)
|
|
3636
|
+
*
|
|
3637
|
+
* ```ts import.meta.vitest
|
|
3638
|
+
* import { Effect, Sink, Stream } from "effect"
|
|
3639
|
+
*
|
|
3640
|
+
* const program = Stream.run(Stream.make(1, 2, 3), Sink.sum)
|
|
3641
|
+
*
|
|
3642
|
+
* await Effect.runPromise(program) // => 6
|
|
3643
|
+
* ```
|
|
3644
|
+
*
|
|
3645
|
+
* @stability stable
|
|
3646
|
+
* @category destructors
|
|
3647
|
+
* @since 2.0.0
|
|
3648
|
+
*/
|
|
3649
|
+
const run = /*#__PURE__*/ dual(2, (self, sink) => scopedWith((scope) => toPullScoped(self.channel, scope).pipe(flatMap$2((upstream) => runSink(sink, upstream, scope)), map$3(([a]) => a))));
|
|
3650
|
+
/**
|
|
3651
|
+
* Runs `sink` against `upstream`. Upstream failures bypass the sink's error
|
|
3652
|
+
* handlers and are restored once the sink has exited.
|
|
3653
|
+
*/
|
|
3654
|
+
const runSink = (sink, upstream, scope) => restoreUpstreamFailure(sink.transform(upstreamFailureToDefect(upstream), scope));
|
|
3655
|
+
/**
|
|
3656
|
+
* Runs the stream and collects all elements into an array.
|
|
3657
|
+
*
|
|
3658
|
+
* **Example** (Collecting stream values)
|
|
3659
|
+
*
|
|
3660
|
+
* ```ts import.meta.vitest
|
|
3661
|
+
* import { Effect, Stream } from "effect"
|
|
3662
|
+
*
|
|
3663
|
+
* const stream = Stream.make(1, 2, 3, 4, 5)
|
|
3664
|
+
*
|
|
3665
|
+
* const program = Effect.gen(function*() {
|
|
3666
|
+
* const collected = yield* Stream.runCollect(stream)
|
|
3667
|
+
* collected // => [ 1, 2, 3, 4, 5 ]
|
|
3668
|
+
* })
|
|
3669
|
+
*
|
|
3670
|
+
* await Effect.runPromise(program)
|
|
3671
|
+
* ```
|
|
3672
|
+
*
|
|
3673
|
+
* @stability stable
|
|
3674
|
+
* @category destructors
|
|
3675
|
+
* @since 2.0.0
|
|
3676
|
+
*/
|
|
3677
|
+
const runCollect = (self) => runFold$1(self.channel, () => [], (acc, chunk) => {
|
|
3678
|
+
for (let i = 0; i < chunk.length; i++) acc.push(chunk[i]);
|
|
3679
|
+
return acc;
|
|
3680
|
+
});
|
|
3681
|
+
/**
|
|
3682
|
+
* Runs the stream and folds elements using a pure reducer.
|
|
3683
|
+
*
|
|
3684
|
+
* **Example** (Folding stream values)
|
|
3685
|
+
*
|
|
3686
|
+
* ```ts import.meta.vitest
|
|
3687
|
+
* import { Effect, Stream } from "effect"
|
|
3688
|
+
*
|
|
3689
|
+
* const program = Effect.gen(function*() {
|
|
3690
|
+
* const total = yield* Stream.runFold(
|
|
3691
|
+
* Stream.make(1, 2, 3),
|
|
3692
|
+
* () => 0,
|
|
3693
|
+
* (acc, n) => acc + n
|
|
3694
|
+
* )
|
|
3695
|
+
* total // => 6
|
|
3696
|
+
* })
|
|
3697
|
+
*
|
|
3698
|
+
* await Effect.runPromise(program)
|
|
3699
|
+
* ```
|
|
3700
|
+
*
|
|
3701
|
+
* @stability stable
|
|
3702
|
+
* @category destructors
|
|
3703
|
+
* @since 2.0.0
|
|
3704
|
+
*/
|
|
3705
|
+
const runFold = /*#__PURE__*/ dual(3, (self, initial, f) => runFold$1(self.channel, initial, (acc, arr) => {
|
|
3706
|
+
for (let i = 0; i < arr.length; i++) acc = f(acc, arr[i]);
|
|
3707
|
+
return acc;
|
|
3708
|
+
}));
|
|
3709
|
+
/**
|
|
3710
|
+
* Runs the stream and folds elements using an effectful reducer.
|
|
3711
|
+
*
|
|
3712
|
+
* **When to use**
|
|
3713
|
+
*
|
|
3714
|
+
* Use when reducing stream elements needs Effects, services, or failures in the
|
|
3715
|
+
* reducer.
|
|
3716
|
+
*
|
|
3717
|
+
* **Example** (Effectfully folding stream values)
|
|
3718
|
+
*
|
|
3719
|
+
* ```ts import.meta.vitest
|
|
3720
|
+
* import { Effect, Stream } from "effect"
|
|
3721
|
+
*
|
|
3722
|
+
* const program = Effect.gen(function*() {
|
|
3723
|
+
* const total = yield* Stream.runFoldEffect(
|
|
3724
|
+
* Stream.make(1, 2, 3),
|
|
3725
|
+
* () => 0,
|
|
3726
|
+
* (acc, n) => Effect.succeed(acc + n)
|
|
3727
|
+
* )
|
|
3728
|
+
* total // => 6
|
|
3729
|
+
* })
|
|
3730
|
+
*
|
|
3731
|
+
* await Effect.runPromise(program)
|
|
3732
|
+
* ```
|
|
3733
|
+
*
|
|
3734
|
+
* @stability stable
|
|
3735
|
+
* @category destructors
|
|
3736
|
+
* @since 2.0.0
|
|
3737
|
+
*/
|
|
3738
|
+
const runFoldEffect = /*#__PURE__*/ dual(3, (self, initial, f) => runFoldEffect$1(self.channel, initial, (acc, arr) => {
|
|
3739
|
+
let i = 0;
|
|
3740
|
+
let s = acc;
|
|
3741
|
+
return map$3(whileLoop({
|
|
3742
|
+
while: () => i < arr.length,
|
|
3743
|
+
body: () => f(s, arr[i]),
|
|
3744
|
+
step(z) {
|
|
3745
|
+
s = z;
|
|
3746
|
+
i++;
|
|
3747
|
+
}
|
|
3748
|
+
}), () => s);
|
|
3749
|
+
}));
|
|
3750
|
+
/**
|
|
3751
|
+
* Runs the provided effectful callback for each element of the stream.
|
|
3752
|
+
*
|
|
3753
|
+
* **Example** (Running an effect for each value)
|
|
3754
|
+
*
|
|
3755
|
+
* ```ts import.meta.vitest
|
|
3756
|
+
* import { Effect, Stream } from "effect"
|
|
3757
|
+
*
|
|
3758
|
+
* const stream = Stream.make(1, 2, 3)
|
|
3759
|
+
* const values: Array<string> = []
|
|
3760
|
+
*
|
|
3761
|
+
* const program = Effect.gen(function*() {
|
|
3762
|
+
* yield* Stream.runForEach(stream, (n) => Effect.sync(() => values.push(`Processing: ${n}`)))
|
|
3763
|
+
* })
|
|
3764
|
+
*
|
|
3765
|
+
* await Effect.runPromise(program)
|
|
3766
|
+
* values // => ["Processing: 1", "Processing: 2", "Processing: 3"]
|
|
3767
|
+
* ```
|
|
3768
|
+
*
|
|
3769
|
+
* @stability stable
|
|
3770
|
+
* @category destructors
|
|
3771
|
+
* @since 2.0.0
|
|
3772
|
+
*/
|
|
3773
|
+
const runForEach = /*#__PURE__*/ dual(2, (self, f) => runForEach$1(self.channel, (arr) => {
|
|
3774
|
+
let i = 0;
|
|
3775
|
+
return whileLoop({
|
|
3776
|
+
while: () => i < arr.length,
|
|
3777
|
+
body: () => f(arr[i++]),
|
|
3778
|
+
step: constVoid
|
|
3779
|
+
});
|
|
3780
|
+
}));
|
|
3781
|
+
/**
|
|
3782
|
+
* Consumes the stream in chunks, passing each non-empty array to the callback.
|
|
3783
|
+
*
|
|
3784
|
+
* **Example** (Consuming stream chunks)
|
|
3785
|
+
*
|
|
3786
|
+
* ```ts import.meta.vitest
|
|
3787
|
+
* import { Effect, Stream } from "effect"
|
|
3788
|
+
*
|
|
3789
|
+
* const stream = Stream.make(1, 2, 3, 4, 5)
|
|
3790
|
+
* const chunks: Array<string> = []
|
|
3791
|
+
* const program = Effect.gen(function*() {
|
|
3792
|
+
* yield* Stream.runForEachArray(
|
|
3793
|
+
* stream,
|
|
3794
|
+
* (chunk) => Effect.sync(() => chunks.push(chunk.join(", ")))
|
|
3795
|
+
* )
|
|
3796
|
+
* })
|
|
3797
|
+
*
|
|
3798
|
+
* await Effect.runPromise(program)
|
|
3799
|
+
* chunks // => ["1, 2, 3, 4, 5"]
|
|
3800
|
+
* ```
|
|
3801
|
+
*
|
|
3802
|
+
* @stability stable
|
|
3803
|
+
* @category destructors
|
|
3804
|
+
* @since 4.0.0
|
|
3805
|
+
*/
|
|
3806
|
+
const runForEachArray = /*#__PURE__*/ dual(2, (self, f) => runForEach$1(self.channel, f));
|
|
3807
|
+
/**
|
|
3808
|
+
* Runs the stream for its effects, discarding emitted elements.
|
|
3809
|
+
*
|
|
3810
|
+
* **Example** (Draining a stream run)
|
|
3811
|
+
*
|
|
3812
|
+
* ```ts import.meta.vitest
|
|
3813
|
+
* import { Effect, Stream } from "effect"
|
|
3814
|
+
*
|
|
3815
|
+
* const values: Array<number> = []
|
|
3816
|
+
* const program = Effect.gen(function*() {
|
|
3817
|
+
* const stream = Stream.make(1, 2, 3).pipe(
|
|
3818
|
+
* Stream.mapEffect((n) => Effect.sync(() => values.push(n)))
|
|
3819
|
+
* )
|
|
3820
|
+
*
|
|
3821
|
+
* yield* Stream.runDrain(stream)
|
|
3822
|
+
* })
|
|
3823
|
+
*
|
|
3824
|
+
* await Effect.runPromise(program)
|
|
3825
|
+
* values // => [1, 2, 3]
|
|
3826
|
+
* ```
|
|
3827
|
+
*
|
|
3828
|
+
* @stability stable
|
|
3829
|
+
* @category destructors
|
|
3830
|
+
* @since 2.0.0
|
|
3831
|
+
*/
|
|
3832
|
+
const runDrain = (self) => runDrain$1(self.channel);
|
|
3833
|
+
/**
|
|
3834
|
+
* Concatenates all emitted strings into a single string.
|
|
3835
|
+
*
|
|
3836
|
+
* **Example** (Joining strings from a stream)
|
|
3837
|
+
*
|
|
3838
|
+
* ```ts import.meta.vitest
|
|
3839
|
+
* import { Effect, Stream } from "effect"
|
|
3840
|
+
*
|
|
3841
|
+
* const stream = Stream.make("Hello", " ", "World", "!")
|
|
3842
|
+
* const program = Effect.gen(function*() {
|
|
3843
|
+
* const text = yield* Stream.mkString(stream)
|
|
3844
|
+
* text // => "Hello World!"
|
|
3845
|
+
* })
|
|
3846
|
+
*
|
|
3847
|
+
* await Effect.runPromise(program)
|
|
3848
|
+
* ```
|
|
3849
|
+
*
|
|
3850
|
+
* @stability stable
|
|
3851
|
+
* @category destructors
|
|
3852
|
+
* @since 2.0.0
|
|
3853
|
+
*/
|
|
3854
|
+
const mkString = (self) => runFold$1(self.channel, () => "", (acc, chunk) => acc + chunk.join(""));
|
|
3855
|
+
/**
|
|
3856
|
+
* Concatenates the stream's `Uint8Array` chunks into a single `ArrayBuffer`.
|
|
3857
|
+
*
|
|
3858
|
+
* **Gotchas**
|
|
3859
|
+
*
|
|
3860
|
+
* This materializes the full content in memory. The source stream must not
|
|
3861
|
+
* reuse or mutate emitted buffers, which are retained until collection completes.
|
|
3862
|
+
*
|
|
3863
|
+
* **Example** (Joining byte chunks into an ArrayBuffer)
|
|
3864
|
+
*
|
|
3865
|
+
* ```ts import.meta.vitest
|
|
3866
|
+
* import { Effect, Stream } from "effect"
|
|
3867
|
+
*
|
|
3868
|
+
* const program = Stream.make(
|
|
3869
|
+
* new Uint8Array([1, 2]),
|
|
3870
|
+
* new Uint8Array([3, 4])
|
|
3871
|
+
* ).pipe(
|
|
3872
|
+
* Stream.mkArrayBuffer,
|
|
3873
|
+
* Effect.map((buffer) => [...new Uint8Array(buffer)])
|
|
3874
|
+
* )
|
|
3875
|
+
*
|
|
3876
|
+
* await Effect.runPromise(program) // => [1, 2, 3, 4]
|
|
3877
|
+
* ```
|
|
3878
|
+
*
|
|
3879
|
+
* @stability stable
|
|
3880
|
+
* @category destructors
|
|
3881
|
+
* @since 4.0.0
|
|
3882
|
+
*/
|
|
3883
|
+
const mkArrayBuffer = (self) => map$3(mkUint8Array$1(self.channel), (bytes) => bytes.buffer);
|
|
3884
|
+
/**
|
|
3885
|
+
* Concatenates the stream's `Uint8Array` chunks into a single `Uint8Array`.
|
|
3886
|
+
*
|
|
3887
|
+
* **Gotchas**
|
|
3888
|
+
*
|
|
3889
|
+
* This materializes the full content in memory. The source stream must not
|
|
3890
|
+
* reuse or mutate emitted buffers, which are retained until collection completes.
|
|
3891
|
+
*
|
|
3892
|
+
* **Example** (Joining Uint8Array chunks)
|
|
3893
|
+
*
|
|
3894
|
+
* ```ts import.meta.vitest
|
|
3895
|
+
* import { Effect, Stream } from "effect"
|
|
3896
|
+
*
|
|
3897
|
+
* const stream = Stream.make(new Uint8Array([1, 2]), new Uint8Array([3, 4]))
|
|
3898
|
+
* const program = Effect.gen(function*() {
|
|
3899
|
+
* const bytes = yield* Stream.mkUint8Array(stream)
|
|
3900
|
+
* const values = Array.from(bytes) // => [1, 2, 3, 4]
|
|
3901
|
+
* })
|
|
3902
|
+
*
|
|
3903
|
+
* await Effect.runPromise(program)
|
|
3904
|
+
* ```
|
|
3905
|
+
*
|
|
3906
|
+
* @stability stable
|
|
3907
|
+
* @category destructors
|
|
3908
|
+
* @since 4.0.0
|
|
3909
|
+
*/
|
|
3910
|
+
const mkUint8Array = (self) => mkUint8Array$1(self.channel);
|
|
3911
|
+
/**
|
|
3912
|
+
* Converts the stream to a `ReadableStream` using the provided services.
|
|
3913
|
+
*
|
|
3914
|
+
* **When to use**
|
|
3915
|
+
*
|
|
3916
|
+
* Use when bridging to Web Streams and you already have the `Context` required
|
|
3917
|
+
* to run the stream outside an `Effect`.
|
|
3918
|
+
*
|
|
3919
|
+
* **Details**
|
|
3920
|
+
*
|
|
3921
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream.
|
|
3922
|
+
*
|
|
3923
|
+
* When `signal` aborts, the stream is interrupted even if the reader has
|
|
3924
|
+
* stopped pulling, and the `ReadableStream` closes.
|
|
3925
|
+
*
|
|
3926
|
+
* **Example** (Converting to a ReadableStream with services)
|
|
3927
|
+
*
|
|
3928
|
+
* ```ts import.meta.vitest
|
|
3929
|
+
* import { Context, Stream } from "effect"
|
|
3930
|
+
*
|
|
3931
|
+
* const stream = Stream.make(1, 2, 3, 4, 5)
|
|
3932
|
+
* const readableStream = Stream.toReadableStreamWith(stream, Context.empty())
|
|
3933
|
+
* const values = await globalThis.Array.fromAsync(readableStream)
|
|
3934
|
+
* values // => [ 1, 2, 3, 4, 5 ]
|
|
3935
|
+
* ```
|
|
3936
|
+
*
|
|
3937
|
+
* @stability stable
|
|
3938
|
+
* @category destructors
|
|
3939
|
+
* @since 4.0.0
|
|
3940
|
+
*/
|
|
3941
|
+
const toReadableStreamWith = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, context, options) => {
|
|
3942
|
+
let currentResolve = void 0;
|
|
3943
|
+
let fiber = void 0;
|
|
3944
|
+
let cancelled = false;
|
|
3945
|
+
const latch = makeUnsafe(false);
|
|
3946
|
+
const signal = options?.signal;
|
|
3947
|
+
const onAbort = () => fiber.interruptUnsafe();
|
|
3948
|
+
return new ReadableStream({
|
|
3949
|
+
start(controller) {
|
|
3950
|
+
fiber = runFork(provideContext$2(runForEach$1(transformPull$1(self.channel, (pull) => succeed$2(latch.whenOpen(pull))), (chunk) => sync(() => {
|
|
3951
|
+
latch.closeUnsafe();
|
|
3952
|
+
for (let i = 0; i < chunk.length; i++) controller.enqueue(chunk[i]);
|
|
3953
|
+
currentResolve();
|
|
3954
|
+
currentResolve = void 0;
|
|
3955
|
+
})), context));
|
|
3956
|
+
fiber.addObserver((exit) => {
|
|
3957
|
+
signal?.removeEventListener("abort", onAbort);
|
|
3958
|
+
if (cancelled) return;
|
|
3959
|
+
if (exit._tag === "Failure" && signal?.aborted !== true) controller.error(squash(exit.cause));
|
|
3960
|
+
else controller.close();
|
|
3961
|
+
});
|
|
3962
|
+
if (signal !== void 0 && fiber.pollUnsafe() === void 0) {
|
|
3963
|
+
if (signal.aborted) onAbort();
|
|
3964
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
3965
|
+
}
|
|
3966
|
+
},
|
|
3967
|
+
pull() {
|
|
3968
|
+
return new Promise((resolve) => {
|
|
3969
|
+
currentResolve = resolve;
|
|
3970
|
+
latch.openUnsafe();
|
|
3971
|
+
});
|
|
3972
|
+
},
|
|
3973
|
+
cancel() {
|
|
3974
|
+
if (!fiber) return;
|
|
3975
|
+
cancelled = true;
|
|
3976
|
+
return runPromise(asVoid(interrupt(fiber)));
|
|
3977
|
+
}
|
|
3978
|
+
}, options?.strategy);
|
|
3979
|
+
});
|
|
3980
|
+
/**
|
|
3981
|
+
* Creates an Effect that builds a ReadableStream from the stream.
|
|
3982
|
+
*
|
|
3983
|
+
* **When to use**
|
|
3984
|
+
*
|
|
3985
|
+
* Use when bridging to Web Streams from inside an `Effect` so the required
|
|
3986
|
+
* services can be captured from the current context.
|
|
3987
|
+
*
|
|
3988
|
+
* **Details**
|
|
3989
|
+
*
|
|
3990
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream.
|
|
3991
|
+
*
|
|
3992
|
+
* **Example** (Creating a ReadableStream effect)
|
|
3993
|
+
*
|
|
3994
|
+
* ```ts import.meta.vitest
|
|
3995
|
+
* import { Effect, Stream } from "effect"
|
|
3996
|
+
*
|
|
3997
|
+
* const stream = Stream.make(1, 2, 3, 4, 5)
|
|
3998
|
+
*
|
|
3999
|
+
* const effect = Effect.gen(function*() {
|
|
4000
|
+
* const readableStream = yield* Stream.toReadableStreamEffect(stream)
|
|
4001
|
+
* readableStream instanceof ReadableStream // => true
|
|
4002
|
+
* })
|
|
4003
|
+
*
|
|
4004
|
+
* await Effect.runPromise(effect)
|
|
4005
|
+
* ```
|
|
4006
|
+
*
|
|
4007
|
+
* @stability stable
|
|
4008
|
+
* @category destructors
|
|
4009
|
+
* @since 2.0.0
|
|
4010
|
+
*/
|
|
4011
|
+
const toReadableStreamEffect = /*#__PURE__*/ dual((args) => isStream(args[0]), (self, options) => map$3(context(), (context) => toReadableStreamWith(self, context, options)));
|
|
4012
|
+
export { toReadableStreamEffect as $, merge as A, runCollect as B, interruptWhen as C, mapAccum as D, map as E, pipeThroughChannel as F, runForEachArray as G, runFold as H, provideContext as I, suspend as J, splitLines as K, provideService as L, mkString as M, mkUint8Array as N, mapEffect as O, paginate as P, tick as Q, retry as R, fromReadableStream as S, make as T, runFoldEffect as U, runDrain as V, runForEach as W, tap as X, take as Y, tapError as Z, fromChannel as _, interruptAll as _t, die as a, mapDone as at, fromPull as b, runIn as bt, drop as c, toTransform as ct, ensuring as d, make$1 as dt, toReadableStreamWith as et, fail as f, makeUnsafe as ft, fromAsyncIterable as g, interrupt as gt, fromArray as h, getCurrent as ht, decodeText as i, fromTransform as it, mkArrayBuffer as j, mapError as k, empty as l, unwrap$1 as lt, flatMap as m, await_ as mt, callback as n, unwrap as nt, drain as o, mapError$1 as ot, filter as p, open as pt, succeed as q, concat as r, fromPull$1 as rt, drainFork as s, mkUint8Array$1 as st, buffer as t, transduce as tt, encodeText as u, isOpen as ut, fromEffect as v, interruptAs as vt, isStream as w, fromQueue as x, fromIterable as y, join as yt, run as z };
|