@typed/fx 2.0.0-beta.0 → 2.0.0-beta.2

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.
Files changed (139) hide show
  1. package/README.md +24 -1
  2. package/dist/Fx/combinators/additive.d.ts +94 -0
  3. package/dist/Fx/combinators/additive.d.ts.map +1 -0
  4. package/dist/Fx/combinators/additive.js +92 -0
  5. package/dist/Fx/combinators/catch.d.ts +61 -0
  6. package/dist/Fx/combinators/catch.d.ts.map +1 -1
  7. package/dist/Fx/combinators/catch.js +54 -0
  8. package/dist/Fx/combinators/changesWithEffect.d.ts +20 -0
  9. package/dist/Fx/combinators/changesWithEffect.d.ts.map +1 -0
  10. package/dist/Fx/combinators/changesWithEffect.js +28 -0
  11. package/dist/Fx/combinators/dropUntil.d.ts +29 -0
  12. package/dist/Fx/combinators/dropUntil.d.ts.map +1 -0
  13. package/dist/Fx/combinators/dropUntil.js +23 -0
  14. package/dist/Fx/combinators/flatMapConcurrently.d.ts.map +1 -1
  15. package/dist/Fx/combinators/flatMapConcurrently.js +3 -2
  16. package/dist/Fx/combinators/index.d.ts +10 -0
  17. package/dist/Fx/combinators/index.d.ts.map +1 -1
  18. package/dist/Fx/combinators/index.js +10 -0
  19. package/dist/Fx/combinators/keyed.d.ts +1 -1
  20. package/dist/Fx/combinators/keyed.d.ts.map +1 -1
  21. package/dist/Fx/combinators/mapBoth.d.ts +21 -0
  22. package/dist/Fx/combinators/mapBoth.d.ts.map +1 -0
  23. package/dist/Fx/combinators/mapBoth.js +14 -0
  24. package/dist/Fx/combinators/mapError.d.ts +17 -0
  25. package/dist/Fx/combinators/mapError.d.ts.map +1 -0
  26. package/dist/Fx/combinators/mapError.js +16 -0
  27. package/dist/Fx/combinators/provide.d.ts +34 -1
  28. package/dist/Fx/combinators/provide.d.ts.map +1 -1
  29. package/dist/Fx/combinators/provide.js +27 -0
  30. package/dist/Fx/combinators/result.d.ts +23 -0
  31. package/dist/Fx/combinators/result.d.ts.map +1 -0
  32. package/dist/Fx/combinators/result.js +32 -0
  33. package/dist/Fx/combinators/scan.d.ts +33 -0
  34. package/dist/Fx/combinators/scan.d.ts.map +1 -0
  35. package/dist/Fx/combinators/scan.js +38 -0
  36. package/dist/Fx/combinators/skip.d.ts +13 -0
  37. package/dist/Fx/combinators/skip.d.ts.map +1 -1
  38. package/dist/Fx/combinators/skip.js +11 -0
  39. package/dist/Fx/combinators/skipWhile.d.ts +49 -0
  40. package/dist/Fx/combinators/skipWhile.d.ts.map +1 -0
  41. package/dist/Fx/combinators/skipWhile.js +66 -0
  42. package/dist/Fx/combinators/slice.d.ts +13 -0
  43. package/dist/Fx/combinators/slice.d.ts.map +1 -1
  44. package/dist/Fx/combinators/slice.js +11 -0
  45. package/dist/Fx/combinators/take.d.ts +13 -0
  46. package/dist/Fx/combinators/take.d.ts.map +1 -1
  47. package/dist/Fx/combinators/take.js +11 -0
  48. package/dist/Fx/combinators/takeUntil.d.ts +14 -0
  49. package/dist/Fx/combinators/takeUntil.d.ts.map +1 -1
  50. package/dist/Fx/combinators/takeUntil.js +14 -0
  51. package/dist/Fx/combinators/takeWhile.d.ts +29 -0
  52. package/dist/Fx/combinators/takeWhile.d.ts.map +1 -0
  53. package/dist/Fx/combinators/takeWhile.js +23 -0
  54. package/dist/Fx/combinators/zip.d.ts +75 -0
  55. package/dist/Fx/combinators/zip.d.ts.map +1 -0
  56. package/dist/Fx/combinators/zip.js +100 -0
  57. package/dist/Fx/constructors/at.d.ts +2 -2
  58. package/dist/Fx/constructors/at.d.ts.map +1 -1
  59. package/dist/Fx/constructors/periodic.d.ts +1 -1
  60. package/dist/Fx/constructors/periodic.d.ts.map +1 -1
  61. package/dist/Push/Push.d.ts +64 -1
  62. package/dist/Push/Push.d.ts.map +1 -1
  63. package/dist/Push/Push.js +57 -0
  64. package/dist/RefSubject/RefArray.d.ts.map +1 -1
  65. package/dist/RefSubject/RefArray.js +2 -1
  66. package/dist/RefSubject/RefChunk.d.ts.map +1 -1
  67. package/dist/RefSubject/RefChunk.js +2 -1
  68. package/dist/RefSubject/RefDateTime.d.ts +4 -4
  69. package/dist/RefSubject/RefDateTime.d.ts.map +1 -1
  70. package/dist/RefSubject/RefHashMap.d.ts.map +1 -1
  71. package/dist/RefSubject/RefHashMap.js +5 -1
  72. package/dist/RefSubject/RefIterable.d.ts +1 -1
  73. package/dist/RefSubject/RefIterable.d.ts.map +1 -1
  74. package/dist/RefSubject/RefIterable.js +6 -1
  75. package/dist/RefSubject/RefRecord.d.ts.map +1 -1
  76. package/dist/RefSubject/RefRecord.js +3 -2
  77. package/dist/RefSubject/RefSubject.d.ts +48 -1
  78. package/dist/RefSubject/RefSubject.d.ts.map +1 -1
  79. package/dist/RefSubject/RefSubject.js +80 -1
  80. package/dist/RefSubject/RefTrie.d.ts +7 -7
  81. package/dist/RefSubject/RefTrie.d.ts.map +1 -1
  82. package/dist/RefSubject/RefTrie.js +8 -3
  83. package/dist/Sink/combinators.d.ts +57 -0
  84. package/dist/Sink/combinators.d.ts.map +1 -1
  85. package/dist/Sink/combinators.js +104 -1
  86. package/dist/Versioned/Versioned.d.ts +30 -0
  87. package/dist/Versioned/Versioned.d.ts.map +1 -1
  88. package/dist/Versioned/Versioned.js +18 -0
  89. package/package.json +10 -6
  90. package/src/Fx/combinators/additive.ts +142 -0
  91. package/src/Fx/combinators/catch.ts +256 -0
  92. package/src/Fx/combinators/changesWithEffect.ts +66 -0
  93. package/src/Fx/combinators/dropUntil.ts +47 -0
  94. package/src/Fx/combinators/flatMapConcurrently.ts +5 -2
  95. package/src/Fx/combinators/index.ts +10 -0
  96. package/src/Fx/combinators/keyed.ts +2 -2
  97. package/src/Fx/combinators/mapBoth.ts +40 -0
  98. package/src/Fx/combinators/mapError.ts +28 -0
  99. package/src/Fx/combinators/provide.ts +63 -1
  100. package/src/Fx/combinators/result.ts +39 -0
  101. package/src/Fx/combinators/scan.ts +82 -0
  102. package/src/Fx/combinators/skip.ts +21 -0
  103. package/src/Fx/combinators/skipWhile.ts +100 -0
  104. package/src/Fx/combinators/slice.ts +23 -0
  105. package/src/Fx/combinators/take.ts +21 -0
  106. package/src/Fx/combinators/takeUntil.ts +38 -0
  107. package/src/Fx/combinators/takeWhile.ts +47 -0
  108. package/src/Fx/combinators/zip.ts +175 -0
  109. package/src/Fx/constructors/at.ts +3 -3
  110. package/src/Fx/constructors/periodic.ts +1 -1
  111. package/src/Fx.additive-combinators.test.ts +126 -0
  112. package/src/Fx.catch-additive.test.ts +206 -0
  113. package/src/Fx.catch.test.ts +1 -2
  114. package/src/Fx.dropUntil.test.ts +61 -0
  115. package/src/Fx.lifecycle.test.ts +1 -2
  116. package/src/Fx.mapError-mapBoth.test.ts +101 -0
  117. package/src/Fx.provide-combinators.test.ts +94 -0
  118. package/src/Fx.result-changesWithEffect.test.ts +112 -0
  119. package/src/Fx.scan.test.ts +73 -0
  120. package/src/Fx.takeWhile-skipWhile.test.ts +84 -0
  121. package/src/Fx.zip-merge-additive.test.ts +171 -0
  122. package/src/Fx.zip.test.ts +133 -0
  123. package/src/Push/Push.ts +170 -1
  124. package/src/Push.additive.test.ts +256 -0
  125. package/src/RefSubject/RefArray.ts +4 -1
  126. package/src/RefSubject/RefChunk.ts +2 -1
  127. package/src/RefSubject/RefDateTime.ts +6 -6
  128. package/src/RefSubject/RefHashMap.ts +10 -1
  129. package/src/RefSubject/RefIterable.ts +11 -2
  130. package/src/RefSubject/RefRecord.ts +9 -2
  131. package/src/RefSubject/RefSubject.ts +108 -9
  132. package/src/RefSubject/RefTrie.ts +19 -10
  133. package/src/RefSubject.additive-parity.test.ts +101 -0
  134. package/src/Sink/combinators.ts +123 -1
  135. package/src/Sink.combinators.test.ts +88 -0
  136. package/src/Sink.reduce-collect-head-last.test.ts +107 -0
  137. package/src/Versioned/Versioned.ts +76 -0
  138. package/src/Versioned.filterMap.test.ts +91 -0
  139. package/tsconfig.json +0 -6
@@ -163,3 +163,259 @@ export const catchTag: {
163
163
  ),
164
164
  ),
165
165
  );
166
+
167
+ /**
168
+ * Recovers from a typed failure when a predicate holds, by switching to a fallback Fx.
169
+ * Only applies to `Cause.Fail`; defects and interrupts are not caught.
170
+ *
171
+ * Mirrors `Effect.catchIf`.
172
+ *
173
+ * @since 1.0.0
174
+ * @category combinators
175
+ */
176
+ export const catchIf: {
177
+ <E, A2, E2, R2>(
178
+ predicate: (e: E) => boolean,
179
+ f: (e: E) => Fx<A2, E2, R2>,
180
+ ): <A, R>(self: Fx<A, E, R>) => Fx<A | A2, E | E2, R | R2>;
181
+
182
+ <A, E, R, A2, E2, R2>(
183
+ self: Fx<A, E, R>,
184
+ predicate: (e: E) => boolean,
185
+ f: (e: E) => Fx<A2, E2, R2>,
186
+ ): Fx<A | A2, E | E2, R | R2>;
187
+ } = dual(
188
+ 3,
189
+ <A, E, R, A2, E2, R2>(
190
+ self: Fx<A, E, R>,
191
+ predicate: (e: E) => boolean,
192
+ f: (e: E) => Fx<A2, E2, R2>,
193
+ ): Fx<A | A2, E | E2, R | R2> =>
194
+ make<A | A2, E | E2, R | R2>((sink) =>
195
+ self.run(
196
+ makeSink((cause) => {
197
+ const result = Cause.findFail(cause);
198
+ if (Result.isFailure(result)) {
199
+ return sink.onFailure(result.failure);
200
+ }
201
+ const error = result.success.error;
202
+ if (predicate(error)) {
203
+ return f(error).run(sink);
204
+ }
205
+ return sink.onFailure(cause);
206
+ }, sink.onSuccess),
207
+ ),
208
+ ),
209
+ );
210
+
211
+ /**
212
+ * Recovers from a failure cause when a predicate on the cause holds,
213
+ * by switching to a fallback Fx.
214
+ *
215
+ * Mirrors `Effect.catchCauseIf`.
216
+ *
217
+ * @since 1.0.0
218
+ * @category combinators
219
+ */
220
+ export const catchCauseIf: {
221
+ <E, A2, E2, R2>(
222
+ predicate: (cause: Cause.Cause<E>) => boolean,
223
+ f: (cause: Cause.Cause<E>) => Fx<A2, E2, R2>,
224
+ ): <A, R>(self: Fx<A, E, R>) => Fx<A | A2, E | E2, R | R2>;
225
+
226
+ <A, E, R, A2, E2, R2>(
227
+ self: Fx<A, E, R>,
228
+ predicate: (cause: Cause.Cause<E>) => boolean,
229
+ f: (cause: Cause.Cause<E>) => Fx<A2, E2, R2>,
230
+ ): Fx<A | A2, E | E2, R | R2>;
231
+ } = dual(
232
+ 3,
233
+ <A, E, R, A2, E2, R2>(
234
+ self: Fx<A, E, R>,
235
+ predicate: (cause: Cause.Cause<E>) => boolean,
236
+ f: (cause: Cause.Cause<E>) => Fx<A2, E2, R2>,
237
+ ): Fx<A | A2, E | E2, R | R2> =>
238
+ make<A | A2, E | E2, R | R2>((sink) =>
239
+ self.run(
240
+ makeSink(
241
+ (cause) => (predicate(cause) ? f(cause).run(sink) : sink.onFailure(cause)),
242
+ sink.onSuccess,
243
+ ),
244
+ ),
245
+ ),
246
+ );
247
+
248
+ type TaggedCase<E> = {
249
+ [K in Extract<E, { _tag: string }>["_tag"]]+?: (
250
+ error: Extract<E, { _tag: K }>,
251
+ ) => Fx<unknown, unknown, unknown>;
252
+ };
253
+
254
+ /**
255
+ * Recovers from typed failures by matching on the `_tag` field with multiple handlers at once.
256
+ *
257
+ * Mirrors `Effect.catchTags`.
258
+ *
259
+ * @since 1.0.0
260
+ * @category combinators
261
+ */
262
+ export const catchTags: {
263
+ <E, Cases extends TaggedCase<E>>(
264
+ cases: Cases,
265
+ ): <A, R>(
266
+ self: Fx<A, E, R>,
267
+ ) => Fx<
268
+ | A
269
+ | {
270
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<infer A2, unknown, unknown>
271
+ ? A2
272
+ : never;
273
+ }[keyof Cases],
274
+ | Exclude<E, { _tag: keyof Cases }>
275
+ | {
276
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<unknown, infer E2, unknown>
277
+ ? E2
278
+ : never;
279
+ }[keyof Cases],
280
+ | R
281
+ | {
282
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<unknown, unknown, infer R2>
283
+ ? R2
284
+ : never;
285
+ }[keyof Cases]
286
+ >;
287
+
288
+ <A, E, R, Cases extends TaggedCase<E>>(
289
+ self: Fx<A, E, R>,
290
+ cases: Cases,
291
+ ): Fx<
292
+ | A
293
+ | {
294
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<infer A2, unknown, unknown>
295
+ ? A2
296
+ : never;
297
+ }[keyof Cases],
298
+ | Exclude<E, { _tag: keyof Cases }>
299
+ | {
300
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<unknown, infer E2, unknown>
301
+ ? E2
302
+ : never;
303
+ }[keyof Cases],
304
+ | R
305
+ | {
306
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<unknown, unknown, infer R2>
307
+ ? R2
308
+ : never;
309
+ }[keyof Cases]
310
+ >;
311
+ } = dual(
312
+ 2,
313
+ <A, E, R, Cases extends TaggedCase<E>>(
314
+ self: Fx<A, E, R>,
315
+ cases: Cases,
316
+ ): Fx<
317
+ | A
318
+ | {
319
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<infer A2, unknown, unknown>
320
+ ? A2
321
+ : never;
322
+ }[keyof Cases],
323
+ | Exclude<E, { _tag: keyof Cases }>
324
+ | {
325
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<unknown, infer E2, unknown>
326
+ ? E2
327
+ : never;
328
+ }[keyof Cases],
329
+ | R
330
+ | {
331
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<unknown, unknown, infer R2>
332
+ ? R2
333
+ : never;
334
+ }[keyof Cases]
335
+ > =>
336
+ make<
337
+ | A
338
+ | {
339
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<infer A2, unknown, unknown>
340
+ ? A2
341
+ : never;
342
+ }[keyof Cases],
343
+ | Exclude<E, { _tag: keyof Cases }>
344
+ | {
345
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<unknown, infer E2, unknown>
346
+ ? E2
347
+ : never;
348
+ }[keyof Cases],
349
+ | R
350
+ | {
351
+ [K in keyof Cases]: Cases[K] extends (e: unknown) => Fx<unknown, unknown, infer R2>
352
+ ? R2
353
+ : never;
354
+ }[keyof Cases]
355
+ >((sink) =>
356
+ self.run(
357
+ makeSink(
358
+ (cause) => {
359
+ const result = Cause.findFail(cause);
360
+ if (Result.isFailure(result)) {
361
+ return sink.onFailure(result.failure);
362
+ }
363
+ const error = result.success.error as E;
364
+ if (!hasTag(error)) {
365
+ return sink.onFailure(
366
+ cause as Cause.Cause<
367
+ | Exclude<E, { _tag: keyof Cases }>
368
+ | {
369
+ [K in keyof Cases]: Cases[K] extends (
370
+ e: unknown,
371
+ ) => Fx<unknown, infer E2, unknown>
372
+ ? E2
373
+ : never;
374
+ }[keyof Cases]
375
+ >,
376
+ );
377
+ }
378
+ const tag = (error as { _tag: string })._tag;
379
+ const handler = (cases as Record<
380
+ string,
381
+ (e: unknown) => Fx<unknown, unknown, unknown>
382
+ >)[tag];
383
+ if (handler !== undefined) {
384
+ return handler(error).run(
385
+ sink as import("../../Sink/Sink.js").Sink<
386
+ unknown,
387
+ unknown,
388
+ import("../../Sink/Sink.js").Context<typeof sink>
389
+ >,
390
+ );
391
+ }
392
+ return sink.onFailure(
393
+ cause as Cause.Cause<
394
+ | Exclude<E, { _tag: keyof Cases }>
395
+ | {
396
+ [K in keyof Cases]: Cases[K] extends (
397
+ e: unknown,
398
+ ) => Fx<unknown, infer E2, unknown>
399
+ ? E2
400
+ : never;
401
+ }[keyof Cases]
402
+ >,
403
+ );
404
+ },
405
+ sink.onSuccess,
406
+ ),
407
+ ) as import("effect/Effect").Effect<
408
+ unknown,
409
+ never,
410
+ | R
411
+ | {
412
+ [K in keyof Cases]: Cases[K] extends (
413
+ e: unknown,
414
+ ) => Fx<unknown, unknown, infer R2>
415
+ ? R2
416
+ : never;
417
+ }[keyof Cases]
418
+ | import("../../Sink/Sink.js").Context<typeof sink>
419
+ >,
420
+ ),
421
+ );
@@ -0,0 +1,66 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { dual } from "effect/Function";
3
+ import * as Option from "effect/Option";
4
+ import * as sinkCore from "../../Sink/combinators.js";
5
+ import { make as makeSink } from "../../Sink/Sink.js";
6
+ import { make } from "../constructors/make.js";
7
+ import type { Fx } from "../Fx.js";
8
+
9
+ /**
10
+ * Drops consecutive elements that are considered equal by an effectful predicate.
11
+ * When the effect returns `true`, the element is skipped; when `false`, it is emitted.
12
+ *
13
+ * This is the effectful variant of `skipRepeatsWith`: instead of a pure
14
+ * `Equivalence<A>`, you supply `(prev, next) => Effect<boolean>` where `true`
15
+ * means "equal" (skip) and `false` means "changed" (emit).
16
+ *
17
+ * @param f - Effectful function: `(prev, next) => Effect<boolean>`. Return `true` to skip (treat as duplicate), `false` to emit.
18
+ * @returns An `Fx` with consecutive "equal" elements removed.
19
+ * @since 1.0.0
20
+ * @category combinators
21
+ */
22
+ export const changesWithEffect: {
23
+ <A, E2, R2>(
24
+ f: (prev: A, next: A) => Effect.Effect<boolean, E2, R2>,
25
+ ): <E, R>(fx: Fx<A, E | E2, R>) => Fx<A, E | E2, R | R2>;
26
+
27
+ <A, E, R, E2, R2>(
28
+ fx: Fx<A, E | E2, R>,
29
+ f: (prev: A, next: A) => Effect.Effect<boolean, E2, R2>,
30
+ ): Fx<A, E | E2, R | R2>;
31
+ } = dual(
32
+ 2,
33
+ <A, E, R, E2, R2>(
34
+ fx: Fx<A, E | E2, R>,
35
+ f: (prev: A, next: A) => Effect.Effect<boolean, E2, R2>,
36
+ ): Fx<A, E | E2, R | R2> =>
37
+ make<A, E | E2, R | R2>((sink) =>
38
+ sinkCore.withStateSemaphore(sink, Option.none<A>() as Option.Option<A>, (s) =>
39
+ fx.run(
40
+ makeSink(s.onFailure, (a2) =>
41
+ Effect.matchCauseEffect(
42
+ Effect.flatMap(s.get, (state) =>
43
+ Option.match(state, {
44
+ onNone: () =>
45
+ Effect.flatMap(s.onSuccess(a2), () =>
46
+ s.updateEffect(() => Effect.succeed(Option.some(a2))),
47
+ ),
48
+ onSome: (prev) =>
49
+ Effect.matchCauseEffect(f(prev, a2), {
50
+ onFailure: (cause) => s.onFailure(cause),
51
+ onSuccess: (equal) =>
52
+ equal
53
+ ? s.updateEffect(() => Effect.succeed(Option.some(prev)))
54
+ : Effect.flatMap(s.onSuccess(a2), () =>
55
+ s.updateEffect(() => Effect.succeed(Option.some(a2))),
56
+ ),
57
+ }),
58
+ }),
59
+ ),
60
+ { onFailure: (cause) => s.onFailure(cause), onSuccess: () => Effect.void },
61
+ ),
62
+ ),
63
+ ),
64
+ ),
65
+ ),
66
+ );
@@ -0,0 +1,47 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { dual } from "effect/Function";
3
+ import type { Fx } from "../Fx.js";
4
+ import { skipWhile, skipWhileEffect } from "./skipWhile.js";
5
+
6
+ /**
7
+ * Drops elements from an Fx until a predicate returns true.
8
+ * Emits from the first element for which the predicate returns true (including that element) and all following elements.
9
+ *
10
+ * @param predicate - The predicate function.
11
+ * @returns An `Fx` that emits once the predicate first matches.
12
+ * @since 1.0.0
13
+ * @category combinators
14
+ */
15
+ export const dropUntil: {
16
+ <A>(predicate: (a: A) => boolean): <E, R>(fx: Fx<A, E, R>) => Fx<A, E, R>;
17
+ <A, E, R>(fx: Fx<A, E, R>, predicate: (a: A) => boolean): Fx<A, E, R>;
18
+ } = dual(
19
+ 2,
20
+ <A, E, R>(fx: Fx<A, E, R>, predicate: (a: A) => boolean): Fx<A, E, R> =>
21
+ skipWhile(fx, (a) => !predicate(a)),
22
+ );
23
+
24
+ /**
25
+ * Drops elements from an Fx until an effectful predicate returns true.
26
+ * Emits from the first element for which the predicate effect succeeds with true (including that element) and all following elements.
27
+ *
28
+ * @param predicate - Effectful predicate function.
29
+ * @returns An `Fx` that emits once the predicate first matches.
30
+ * @since 1.0.0
31
+ * @category combinators
32
+ */
33
+ export const dropUntilEffect: {
34
+ <A, E2, R2>(
35
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
36
+ ): <E, R>(fx: Fx<A, E, R>) => Fx<A, E | E2, R | R2>;
37
+ <A, E, R, E2, R2>(
38
+ fx: Fx<A, E, R>,
39
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
40
+ ): Fx<A, E | E2, R | R2>;
41
+ } = dual(
42
+ 2,
43
+ <A, E, R, E2, R2>(
44
+ fx: Fx<A, E, R>,
45
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
46
+ ): Fx<A, E | E2, R | R2> => skipWhileEffect(fx, (a) => Effect.map(predicate(a), (b) => !b)),
47
+ );
@@ -1,5 +1,6 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import * as FiberSet from "effect/FiberSet";
3
+ import * as Semaphore from "effect/Semaphore";
3
4
  import { dual } from "effect/Function";
4
5
  import type * as Scope from "effect/Scope";
5
6
  import { make as makeSink } from "../../Sink/Sink.js";
@@ -26,10 +27,12 @@ export const flatMapConcurrently: FlatMapLike<[concurrency: number]> = dual(
26
27
  ): Fx<B, E | E2, R | R2 | Scope.Scope> =>
27
28
  make<B, E | E2, R | R2 | Scope.Scope>(
28
29
  Effect.fn(function* (sink) {
29
- const semaphore = yield* Effect.makeSemaphore(concurrency);
30
+ const semaphore = yield* Semaphore.make(concurrency);
30
31
  const lock = semaphore.withPermits(1);
31
32
  const set = yield* FiberSet.make<void, never>();
32
- yield* self.run(makeSink(sink.onFailure, (a) => FiberSet.run(set, lock(f(a).run(sink)))));
33
+ yield* self.run(
34
+ makeSink(sink.onFailure, (a) => FiberSet.run(set, lock(Effect.asVoid(f(a).run(sink))))),
35
+ );
33
36
  yield* FiberSet.awaitEmpty(set);
34
37
  }, extendScope),
35
38
  ),
@@ -1,7 +1,10 @@
1
1
  export * from "./catch.js";
2
+ export * from "./changesWithEffect.js";
2
3
  export * from "./causes.js";
3
4
  export * from "./compact.js";
4
5
  export * from "./continueWith.js";
6
+ export * from "./dropUntil.js";
7
+ export * from "./additive.js";
5
8
  export * from "./ensuring.js";
6
9
  export * from "./exhaustLatestMap.js";
7
10
  export * from "./exhaustLatestMapEffect.js";
@@ -29,23 +32,30 @@ export * from "./loopCause.js";
29
32
  export * from "./loopCauseEffect.js";
30
33
  export * from "./loopEffect.js";
31
34
  export * from "./map.js";
35
+ export * from "./mapBoth.js";
32
36
  export * from "./mapEffect.js";
37
+ export * from "./mapError.js";
33
38
  export * from "./mergeAll.js";
34
39
  export * from "./mergeOrdered.js";
35
40
  export * from "./onError.js";
36
41
  export * from "./onExit.js";
37
42
  export * from "./onInterrupt.js";
38
43
  export * from "./provide.js";
44
+ export * from "./result.js";
45
+ export * from "./scan.js";
39
46
  export * from "./skip.js";
40
47
  export * from "./skipRepeats.js";
48
+ export * from "./skipWhile.js";
41
49
  export * from "./skipRepeatsWith.js";
42
50
  export * from "./slice.js";
43
51
  export * from "./switchMap.js";
44
52
  export * from "./switchMapEffect.js";
45
53
  export * from "./take.js";
46
54
  export * from "./takeUntil.js";
55
+ export * from "./takeWhile.js";
47
56
  export * from "./tapEffect.js";
48
57
  export * from "./tuple.js";
49
58
  export * from "./unwrap.js";
50
59
  export * from "./unwrapScoped.js";
51
60
  export * from "./when.js";
61
+ export * from "./zip.js";
@@ -37,7 +37,7 @@ export interface KeyedOptions<A, B, C, E2, R2> {
37
37
  /**
38
38
  * Optional debounce duration for emission.
39
39
  */
40
- readonly debounce?: Duration.DurationInput;
40
+ readonly debounce?: Duration.Input;
41
41
  }
42
42
 
43
43
  /**
@@ -323,7 +323,7 @@ function withDebounceFork<A, E, R>(
323
323
  fork: <R>(effect: Effect.Effect<A, never, R>) => Effect.Effect<void, never, R>,
324
324
  scope: Scope.Scope,
325
325
  ) => Effect.Effect<A, E, R>,
326
- duration: Duration.DurationInput,
326
+ duration: Duration.Input,
327
327
  ): Effect.Effect<unknown, E, R | Scope.Scope> {
328
328
  return withScopedFork(
329
329
  (fork, scope) =>
@@ -0,0 +1,40 @@
1
+ import * as Cause from "effect/Cause";
2
+ import { dual } from "effect/Function";
3
+ import { make as makeSink } from "../../Sink/Sink.js";
4
+ import { make } from "../constructors/make.js";
5
+ import type { Fx } from "../Fx.js";
6
+
7
+ /**
8
+ * Transforms both the success and error channels of an Fx using the provided options.
9
+ *
10
+ * Mirrors `Effect.mapBoth`: `onSuccess` maps emitted values, `onFailure` maps the
11
+ * typed failure (via `Cause.map`); defects and interrupts are preserved.
12
+ *
13
+ * @since 1.0.0
14
+ * @category combinators
15
+ */
16
+ export const mapBoth: {
17
+ <E, E2, A, A2>(options: {
18
+ readonly onFailure: (e: E) => E2;
19
+ readonly onSuccess: (a: A) => A2;
20
+ }): <R>(self: Fx<A, E, R>) => Fx<A2, E2, R>;
21
+
22
+ <A, E, R, E2, A2>(
23
+ self: Fx<A, E, R>,
24
+ options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 },
25
+ ): Fx<A2, E2, R>;
26
+ } = dual(
27
+ 2,
28
+ <A, E, R, E2, A2>(
29
+ self: Fx<A, E, R>,
30
+ options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 },
31
+ ): Fx<A2, E2, R> =>
32
+ make<A2, E2, R>((sink) =>
33
+ self.run(
34
+ makeSink(
35
+ (cause) => sink.onFailure(Cause.map(cause, options.onFailure)),
36
+ (a) => sink.onSuccess(options.onSuccess(a)),
37
+ ),
38
+ ),
39
+ ),
40
+ );
@@ -0,0 +1,28 @@
1
+ import * as Cause from "effect/Cause";
2
+ import { dual } from "effect/Function";
3
+ import { make as makeSink } from "../../Sink/Sink.js";
4
+ import { make } from "../constructors/make.js";
5
+ import type { Fx } from "../Fx.js";
6
+
7
+ /**
8
+ * Transforms the error channel of an Fx using the provided function.
9
+ *
10
+ * Failures (Cause) are mapped via `Cause.map`, so only the typed failure (`Fail`)
11
+ * is transformed; defects and interrupts are preserved unchanged.
12
+ *
13
+ * Mirrors `Effect.mapError`.
14
+ *
15
+ * @since 1.0.0
16
+ * @category combinators
17
+ */
18
+ export const mapError: {
19
+ <E, E2>(f: (e: E) => E2): <A, R>(self: Fx<A, E, R>) => Fx<A, E2, R>;
20
+
21
+ <A, E, R, E2>(self: Fx<A, E, R>, f: (e: E) => E2): Fx<A, E2, R>;
22
+ } = dual(
23
+ 2,
24
+ <A, E, R, E2>(self: Fx<A, E, R>, f: (e: E) => E2): Fx<A, E2, R> =>
25
+ make<A, E2, R>((sink) =>
26
+ self.run(makeSink((cause) => sink.onFailure(Cause.map(cause, f)), sink.onSuccess)),
27
+ ),
28
+ );
@@ -3,7 +3,7 @@ import * as Exit from "effect/Exit";
3
3
  import { dual } from "effect/Function";
4
4
  import * as Layer from "effect/Layer";
5
5
  import * as Scope from "effect/Scope";
6
- import type * as ServiceMap from "effect/ServiceMap";
6
+ import * as ServiceMap from "effect/ServiceMap";
7
7
  import { make } from "../constructors/make.js";
8
8
  import type { Fx } from "../Fx.js";
9
9
 
@@ -60,3 +60,65 @@ export const provideServices: {
60
60
  <A, E, R, R2>(fx: Fx<A, E, R>, services: ServiceMap.ServiceMap<R2>): Fx<A, E, Exclude<R, R2>> =>
61
61
  provide(fx, Layer.succeedServices(services)),
62
62
  );
63
+
64
+ /**
65
+ * Provides a single service to an Fx.
66
+ *
67
+ * Equivalent to `provideServices(fx, ServiceMap.make(tag, service))`. The service
68
+ * is available for the entire Fx stream, scoped to the stream lifetime.
69
+ *
70
+ * @param tag - The service tag (identifier).
71
+ * @param service - The service implementation.
72
+ * @returns An `Fx` with the required service provided.
73
+ * @since 1.0.0
74
+ * @category combinators
75
+ */
76
+ export const provideService: {
77
+ <Id, S>(
78
+ tag: ServiceMap.Service<Id, S>,
79
+ service: S,
80
+ ): <A, E, R>(fx: Fx<A, E, R>) => Fx<A, E, Exclude<R, Id>>;
81
+ <A, E, R, Id, S>(
82
+ fx: Fx<A, E, R>,
83
+ tag: ServiceMap.Service<Id, S>,
84
+ service: S,
85
+ ): Fx<A, E, Exclude<R, Id>>;
86
+ } = dual(
87
+ 3,
88
+ <A, E, R, Id, S>(
89
+ fx: Fx<A, E, R>,
90
+ tag: ServiceMap.Service<Id, S>,
91
+ service: S,
92
+ ): Fx<A, E, Exclude<R, Id>> => provideServices(fx, ServiceMap.make(tag, service)),
93
+ );
94
+
95
+ /**
96
+ * Provides a single service to an Fx by running an effect that produces the service.
97
+ *
98
+ * The effect is run when the Fx is run; the resulting service is provided to the
99
+ * entire stream. Equivalent to `provide(fx, Layer.effect(tag, serviceEffect))`.
100
+ *
101
+ * @param tag - The service tag (identifier).
102
+ * @param serviceEffect - Effect that produces the service (may have its own requirements).
103
+ * @returns An `Fx` with the required service provided.
104
+ * @since 1.0.0
105
+ * @category combinators
106
+ */
107
+ export const provideServiceEffect: {
108
+ <Id, S, E2, R2>(
109
+ tag: ServiceMap.Service<Id, S>,
110
+ serviceEffect: Effect.Effect<S, E2, R2>,
111
+ ): <A, E, R>(fx: Fx<A, E, R>) => Fx<A, E | E2, Exclude<R, Id> | R2>;
112
+ <A, E, R, Id, S, E2, R2>(
113
+ fx: Fx<A, E, R>,
114
+ tag: ServiceMap.Service<Id, S>,
115
+ serviceEffect: Effect.Effect<S, E2, R2>,
116
+ ): Fx<A, E | E2, Exclude<R, Id> | R2>;
117
+ } = dual(
118
+ 3,
119
+ <A, E, R, Id, S, E2, R2>(
120
+ fx: Fx<A, E, R>,
121
+ tag: ServiceMap.Service<Id, S>,
122
+ serviceEffect: Effect.Effect<S, E2, R2>,
123
+ ): Fx<A, E | E2, Exclude<R, Id> | R2> => provide(fx, Layer.effect(tag, serviceEffect)),
124
+ );
@@ -0,0 +1,39 @@
1
+ import * as Cause from "effect/Cause";
2
+ import * as Result from "effect/Result";
3
+ import type { Sink } from "../../Sink/Sink.js";
4
+ import { make } from "../constructors/make.js";
5
+ import type { Fx } from "../Fx.js";
6
+
7
+ /**
8
+ * Materializes success and failure of an Fx as `Result` values.
9
+ *
10
+ * - **Success**: each emitted value is wrapped as `Result.succeed(value)`.
11
+ * - **Failure**: any failure (including typed error, defect, and interrupt) is
12
+ * materialized as `Result.fail(cause)`. The output error type is `Cause<E>`,
13
+ * so defects and interrupts are explicitly represented in the `Result` and
14
+ * the resulting Fx has error type `never`.
15
+ *
16
+ * The resulting Fx never fails at the stream level; all outcomes are emitted as
17
+ * `Result<A, Cause<E>>`. Consumers can use `Result.match` or `Result.isSuccess` /
18
+ * `Result.isFailure` to handle success vs failure (including defect/interrupt).
19
+ *
20
+ * @param fx - The `Fx` stream.
21
+ * @returns An `Fx` emitting `Result<A, Cause<E>>`.
22
+ * @since 1.0.0
23
+ * @category combinators
24
+ */
25
+ export const result = <A, E, R>(fx: Fx<A, E, R>): Fx<Result.Result<A, Cause.Cause<E>>, never, R> =>
26
+ make<Result.Result<A, Cause.Cause<E>>, never, R>((sink) => fx.run(new ResultSink(sink)));
27
+
28
+ class ResultSink<A, E, R> implements Sink<A, E, R> {
29
+ readonly sink: Sink<Result.Result<A, Cause.Cause<E>>, never, R>;
30
+ readonly onSuccess: (value: A) => import("effect/Effect").Effect<unknown, never, R>;
31
+ readonly onFailure: (cause: Cause.Cause<E>) => import("effect/Effect").Effect<unknown, never, R>;
32
+
33
+ constructor(sink: Sink<Result.Result<A, Cause.Cause<E>>, never, R>) {
34
+ this.sink = sink;
35
+ const s = sink;
36
+ this.onSuccess = (value) => s.onSuccess(Result.succeed(value));
37
+ this.onFailure = (cause) => s.onSuccess(Result.fail(cause));
38
+ }
39
+ }