@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
@@ -0,0 +1,82 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { dual, pipe } from "effect/Function";
3
+ import { loop as sinkLoop, loopEffect as sinkLoopEffect } from "../../Sink/combinators.js";
4
+ import { make } from "../constructors/make.js";
5
+ import type { Fx } from "../Fx.js";
6
+ import { Sink } from "../../Sink.js";
7
+
8
+ /**
9
+ * Scans the stream with a pure function, emitting the accumulated state after each element.
10
+ * Emits the initial value first, then for each input `a` emits `f(state, a)` and updates state.
11
+ *
12
+ * Semantics align with Effect Stream's `scan`: output is `initial`, `f(initial, a1)`, `f(..., a2)`, ...
13
+ *
14
+ * @param initial - Initial state (first value emitted).
15
+ * @param f - Reducer `(state, value) => nextState`.
16
+ * @returns An `Fx` that emits the accumulated state at each step.
17
+ * @since 1.0.0
18
+ * @category combinators
19
+ */
20
+ export const scan: {
21
+ <S, A>(initial: S, f: (s: S, a: A) => S): <E, R>(fx: Fx<A, E, R>) => Fx<S, E, R>;
22
+ <A, E, R, S>(fx: Fx<A, E, R>, initial: S, f: (s: S, a: A) => S): Fx<S, E, R>;
23
+ } = dual(
24
+ 3,
25
+ <A, E, R, S>(fx: Fx<A, E, R>, initial: S, f: (s: S, a: A) => S): Fx<S, E, R> =>
26
+ make<S, E, R>((sink) =>
27
+ Effect.gen(function* () {
28
+ yield* sink.onSuccess(initial);
29
+ yield* fx.run(
30
+ sinkLoop(sink, initial, (s, a) => {
31
+ const next = f(s, a);
32
+ return [next, next] as const;
33
+ }),
34
+ );
35
+ }),
36
+ ),
37
+ );
38
+
39
+ /**
40
+ * Scans the stream with an effectful function, emitting the accumulated state after each element.
41
+ * Emits the initial value first, then for each input `a` runs `f(state, a)` and emits the resulting state.
42
+ *
43
+ * @param initial - Initial state (first value emitted).
44
+ * @param f - Effectful reducer `(state, value) => Effect<nextState>`.
45
+ * @returns An `Fx` that emits the accumulated state at each step.
46
+ * @since 1.0.0
47
+ * @category combinators
48
+ */
49
+ export const scanEffect: {
50
+ <S, A, E2, R2>(
51
+ initial: S,
52
+ f: (s: S, a: A) => Effect.Effect<S, E2, R2>,
53
+ ): <E, R>(fx: Fx<A, E, R>) => Fx<S, E | E2, R | R2>;
54
+ <A, E, R, S, E2, R2>(
55
+ fx: Fx<A, E, R>,
56
+ initial: S,
57
+ f: (s: S, a: A) => Effect.Effect<S, E2, R2>,
58
+ ): Fx<S, E | E2, R | R2>;
59
+ } = dual(
60
+ 3,
61
+ <A, E, R, S, E2, R2>(
62
+ fx: Fx<A, E, R>,
63
+ initial: S,
64
+ f: (s: S, a: A) => Effect.Effect<S, E2, R2>,
65
+ ): Fx<S, E | E2, R | R2> =>
66
+ make<S, E | E2, R | R2>(<RSink>(sink: Sink<S, E | E2, RSink>) =>
67
+ Effect.gen(function* () {
68
+ const services = yield* Effect.services<R | R2 | RSink>();
69
+ yield* sink.onSuccess(initial);
70
+ yield* fx.run(
71
+ sinkLoopEffect(sink, initial, (s, a) =>
72
+ pipe(
73
+ f(s, a),
74
+ Effect.catchCause((cause) => sink.onFailure(cause).pipe(Effect.as(s))),
75
+ Effect.map((next) => [next, next] as const),
76
+ Effect.provideServices(services),
77
+ ),
78
+ ),
79
+ );
80
+ }),
81
+ ),
82
+ );
@@ -1,6 +1,8 @@
1
+ import * as Effect from "effect/Effect";
1
2
  import { dual } from "effect/Function";
2
3
  import type { Fx } from "../Fx.js";
3
4
  import { slice } from "./slice.js";
5
+ import { unwrap } from "./unwrap.js";
4
6
 
5
7
  /**
6
8
  * Skips the first `n` elements of an Fx.
@@ -18,3 +20,22 @@ export const skip: {
18
20
  2,
19
21
  <A, E, R>(fx: Fx<A, E, R>, n: number): Fx<A, E, R> => slice(fx, { skip: n, take: Infinity }),
20
22
  );
23
+
24
+ /**
25
+ * Skips the first `n` elements where `n` is produced by an Effect.
26
+ *
27
+ * @param count - Effect that produces the number of elements to skip.
28
+ * @returns An `Fx` that emits values after the first `n` elements.
29
+ * @since 1.0.0
30
+ * @category combinators
31
+ */
32
+ export const skipEffect: {
33
+ <E2, R2>(
34
+ count: Effect.Effect<number, E2, R2>,
35
+ ): <A, E, R>(fx: Fx<A, E, R>) => Fx<A, E | E2, R | R2>;
36
+ <A, E, R, E2, R2>(fx: Fx<A, E, R>, count: Effect.Effect<number, E2, R2>): Fx<A, E | E2, R | R2>;
37
+ } = dual(
38
+ 2,
39
+ <A, E, R, E2, R2>(fx: Fx<A, E, R>, count: Effect.Effect<number, E2, R2>): Fx<A, E | E2, R | R2> =>
40
+ unwrap(Effect.map(count, (n) => skip(fx, n))),
41
+ );
@@ -0,0 +1,100 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { dual } from "effect/Function";
3
+ import * as Ref from "effect/Ref";
4
+ import { make as makeFx } from "../constructors/make.js";
5
+ import type { Fx } from "../Fx.js";
6
+ import { make as makeSink } from "../../Sink/Sink.js";
7
+
8
+ /**
9
+ * Skips elements from an Fx while a predicate returns true.
10
+ * Emits from the first element for which the predicate returns false (including that element) and all following elements.
11
+ *
12
+ * @param predicate - The predicate function.
13
+ * @returns An `Fx` that emits once the predicate first fails.
14
+ * @since 1.0.0
15
+ * @category combinators
16
+ */
17
+ export const skipWhile: {
18
+ <A>(predicate: (a: A) => boolean): <E, R>(fx: Fx<A, E, R>) => Fx<A, E, R>;
19
+ <A, E, R>(fx: Fx<A, E, R>, predicate: (a: A) => boolean): Fx<A, E, R>;
20
+ } = dual(
21
+ 2,
22
+ <A, E, R>(fx: Fx<A, E, R>, predicate: (a: A) => boolean): Fx<A, E, R> =>
23
+ makeFx<A, E, R>((sink) =>
24
+ Effect.gen(function* () {
25
+ const skippingRef = yield* Ref.make(true);
26
+ const skipSink = makeSink(sink.onFailure, (a: A) =>
27
+ Effect.gen(function* () {
28
+ const skipping = yield* Ref.get(skippingRef);
29
+ if (skipping) {
30
+ if (predicate(a)) return;
31
+ yield* Ref.set(skippingRef, false);
32
+ }
33
+ return yield* sink.onSuccess(a);
34
+ }),
35
+ );
36
+ return yield* fx.run(skipSink);
37
+ }),
38
+ ),
39
+ );
40
+
41
+ /**
42
+ * Skips elements from an Fx while an effectful predicate returns true.
43
+ * Emits from the first element for which the predicate effect succeeds with false (including that element) and all following elements.
44
+ *
45
+ * @param predicate - Effectful predicate function.
46
+ * @returns An `Fx` that emits once the predicate first fails.
47
+ * @since 1.0.0
48
+ * @category combinators
49
+ */
50
+ export const skipWhileEffect: {
51
+ <A, E2, R2>(
52
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
53
+ ): <E, R>(fx: Fx<A, E, R>) => Fx<A, E | E2, R | R2>;
54
+ <A, E, R, E2, R2>(
55
+ fx: Fx<A, E, R>,
56
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
57
+ ): Fx<A, E | E2, R | R2>;
58
+ } = dual(
59
+ 2,
60
+ <A, E, R, E2, R2>(
61
+ fx: Fx<A, E, R>,
62
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
63
+ ): Fx<A, E | E2, R | R2> =>
64
+ makeFx<A, E | E2, R | R2>((sink) =>
65
+ Effect.gen(function* () {
66
+ const skippingRef = yield* Ref.make(true);
67
+ const skipSink = makeSink(sink.onFailure, (a: A) =>
68
+ Effect.matchCauseEffect(predicate(a), {
69
+ onFailure: sink.onFailure,
70
+ onSuccess: (ok) =>
71
+ Effect.gen(function* () {
72
+ const skipping = yield* Ref.get(skippingRef);
73
+ if (skipping) {
74
+ if (ok) return;
75
+ yield* Ref.set(skippingRef, false);
76
+ }
77
+ return yield* sink.onSuccess(a);
78
+ }),
79
+ }),
80
+ );
81
+ return yield* fx.run(skipSink);
82
+ }),
83
+ ),
84
+ );
85
+
86
+ /**
87
+ * Alias of `skipWhile` for Effect parity (`dropWhile` naming).
88
+ *
89
+ * @since 1.0.0
90
+ * @category combinators
91
+ */
92
+ export const dropWhile = skipWhile;
93
+
94
+ /**
95
+ * Alias of `skipWhileEffect` for Effect parity (`dropWhileEffect` naming).
96
+ *
97
+ * @since 1.0.0
98
+ * @category combinators
99
+ */
100
+ export const dropWhileEffect = skipWhileEffect;
@@ -1,7 +1,9 @@
1
+ import * as Effect from "effect/Effect";
1
2
  import { dual } from "effect/Function";
2
3
  import * as sinkCore from "../../Sink/combinators.js";
3
4
  import { make } from "../constructors/make.js";
4
5
  import type { Fx } from "../Fx.js";
6
+ import { unwrap } from "./unwrap.js";
5
7
 
6
8
  /**
7
9
  * Defines the bounds for slicing an Fx stream.
@@ -30,3 +32,24 @@ export const slice: {
30
32
  <A, E, R>(fx: Fx<A, E, R>, bounds: Bounds): Fx<A, E, R> =>
31
33
  make<A, E, R>((sink) => sinkCore.slice(sink, bounds, (sink) => fx.run(sink))),
32
34
  );
35
+
36
+ /**
37
+ * Slices an Fx with bounds produced by an Effect.
38
+ *
39
+ * @param bounds - Effect that produces slice bounds.
40
+ * @returns An `Fx` representing the slice.
41
+ * @since 1.0.0
42
+ * @category combinators
43
+ */
44
+ export const sliceEffect: {
45
+ <E2, R2>(
46
+ bounds: Effect.Effect<Bounds, E2, R2>,
47
+ ): <A, E, R>(fx: Fx<A, E, R>) => Fx<A, E | E2, R | R2>;
48
+ <A, E, R, E2, R2>(fx: Fx<A, E, R>, bounds: Effect.Effect<Bounds, E2, R2>): Fx<A, E | E2, R | R2>;
49
+ } = dual(
50
+ 2,
51
+ <A, E, R, E2, R2>(
52
+ fx: Fx<A, E, R>,
53
+ bounds: Effect.Effect<Bounds, E2, R2>,
54
+ ): Fx<A, E | E2, R | R2> => unwrap(Effect.map(bounds, (b) => slice(fx, b))),
55
+ );
@@ -1,6 +1,8 @@
1
+ import * as Effect from "effect/Effect";
1
2
  import { dual } from "effect/Function";
2
3
  import type { Fx } from "../Fx.js";
3
4
  import { slice } from "./slice.js";
5
+ import { unwrap } from "./unwrap.js";
4
6
 
5
7
  /**
6
8
  * Takes the first `n` elements from an Fx and then completes.
@@ -15,3 +17,22 @@ export const take: {
15
17
 
16
18
  <A, E, R>(fx: Fx<A, E, R>, n: number): Fx<A, E, R>;
17
19
  } = dual(2, <A, E, R>(fx: Fx<A, E, R>, n: number): Fx<A, E, R> => slice(fx, { skip: 0, take: n }));
20
+
21
+ /**
22
+ * Takes the first `n` elements where `n` is produced by an Effect.
23
+ *
24
+ * @param count - Effect that produces the number of elements to take.
25
+ * @returns An `Fx` that emits at most `n` elements.
26
+ * @since 1.0.0
27
+ * @category combinators
28
+ */
29
+ export const takeEffect: {
30
+ <E2, R2>(
31
+ count: Effect.Effect<number, E2, R2>,
32
+ ): <A, E, R>(fx: Fx<A, E, R>) => Fx<A, E | E2, R | R2>;
33
+ <A, E, R, E2, R2>(fx: Fx<A, E, R>, count: Effect.Effect<number, E2, R2>): Fx<A, E | E2, R | R2>;
34
+ } = dual(
35
+ 2,
36
+ <A, E, R, E2, R2>(fx: Fx<A, E, R>, count: Effect.Effect<number, E2, R2>): Fx<A, E | E2, R | R2> =>
37
+ unwrap(Effect.map(count, (n) => take(fx, n))),
38
+ );
@@ -1,3 +1,4 @@
1
+ import * as Effect from "effect/Effect";
1
2
  import { dual } from "effect/Function";
2
3
  import * as combinators from "../../Sink/combinators.js";
3
4
  import { make as makeSink } from "../../Sink/Sink.js";
@@ -31,6 +32,43 @@ export const takeUntil: {
31
32
  );
32
33
  });
33
34
 
35
+ /**
36
+ * Takes elements from an Fx until an effectful predicate returns true.
37
+ * The element that satisfies the predicate is not included in the output.
38
+ *
39
+ * @param predicate - Effectful predicate function.
40
+ * @returns An `Fx` that completes when the predicate matches.
41
+ * @since 1.0.0
42
+ * @category combinators
43
+ */
44
+ export const takeUntilEffect: {
45
+ <A, E2, R2>(
46
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
47
+ ): <E, R>(fx: Fx<A, E, R>) => Fx<A, E | E2, R | R2>;
48
+ <A, E, R, E2, R2>(
49
+ fx: Fx<A, E, R>,
50
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
51
+ ): Fx<A, E | E2, R | R2>;
52
+ } = dual(
53
+ 2,
54
+ <A, E, R, E2, R2>(
55
+ fx: Fx<A, E, R>,
56
+ predicate: (a: A) => Effect.Effect<boolean, E2, R2>,
57
+ ): Fx<A, E | E2, R | R2> =>
58
+ makeFx<A, E | E2, R | R2>((sink) =>
59
+ combinators.withEarlyExit(sink, (sink) =>
60
+ fx.run(
61
+ makeSink(sink.onFailure, (a) =>
62
+ Effect.matchCauseEffect(predicate(a), {
63
+ onFailure: sink.onFailure,
64
+ onSuccess: (matches) => (matches ? sink.earlyExit : sink.onSuccess(a)),
65
+ }),
66
+ ),
67
+ ),
68
+ ),
69
+ ),
70
+ );
71
+
34
72
  /**
35
73
  * Drops elements from an Fx after a predicate returns true.
36
74
  * The element that satisfies the predicate is included in the output.
@@ -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 { takeUntil, takeUntilEffect } from "./takeUntil.js";
5
+
6
+ /**
7
+ * Takes elements from an Fx while a predicate returns true.
8
+ * Stops at the first element for which the predicate returns false; that element is not included.
9
+ *
10
+ * @param predicate - The predicate function.
11
+ * @returns An `Fx` that emits while the predicate holds.
12
+ * @since 1.0.0
13
+ * @category combinators
14
+ */
15
+ export const takeWhile: {
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
+ takeUntil(fx, (a) => !predicate(a)),
22
+ );
23
+
24
+ /**
25
+ * Takes elements from an Fx while an effectful predicate returns true.
26
+ * Stops at the first element for which the predicate effect succeeds with false; that element is not included.
27
+ *
28
+ * @param predicate - Effectful predicate function.
29
+ * @returns An `Fx` that emits while the predicate holds.
30
+ * @since 1.0.0
31
+ * @category combinators
32
+ */
33
+ export const takeWhileEffect: {
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> => takeUntilEffect(fx, (a) => Effect.map(predicate(a), (b) => !b)),
47
+ );
@@ -0,0 +1,175 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Exit from "effect/Exit";
3
+ import * as Fiber from "effect/Fiber";
4
+ import { dual } from "effect/Function";
5
+ import * as Option from "effect/Option";
6
+ import * as Queue from "effect/Queue";
7
+ import { make as makeSink } from "../../Sink/Sink.js";
8
+ import { make } from "../constructors/make.js";
9
+ import type { Fx } from "../Fx.js";
10
+ import { map } from "./map.js";
11
+ import { tuple } from "./tuple.js";
12
+
13
+ /**
14
+ * Zips two Fx streams in strict lockstep: emits a pair `[a, b]` only when both
15
+ * streams have produced their next value. Emits the i-th pair when both have
16
+ * produced at least i values.
17
+ *
18
+ * **Completion:** The zipped stream completes when the **first** of the two
19
+ * streams completes (no further pairs are emitted). The other stream is
20
+ * interrupted.
21
+ *
22
+ * **Errors:** The first failure from either stream fails the zipped stream.
23
+ *
24
+ * @param that - The second Fx stream.
25
+ * @returns An Fx that emits pairs `[A, B]`.
26
+ * @since 1.0.0
27
+ * @category combinators
28
+ */
29
+ export const zip: {
30
+ <A2, E2, R2>(
31
+ that: Fx<A2, E2, R2>,
32
+ ): <A, E, R>(self: Fx<A, E, R>) => Fx<readonly [A, A2], E | E2, R | R2>;
33
+ <A, E, R, A2, E2, R2>(
34
+ self: Fx<A, E, R>,
35
+ that: Fx<A2, E2, R2>,
36
+ ): Fx<readonly [A, A2], E | E2, R | R2>;
37
+ } = dual(
38
+ 2,
39
+ <A, E, R, A2, E2, R2>(
40
+ self: Fx<A, E, R>,
41
+ that: Fx<A2, E2, R2>,
42
+ ): Fx<readonly [A, A2], E | E2, R | R2> =>
43
+ make<readonly [A, A2], E | E2, R | R2>((sink) =>
44
+ Effect.gen(function* () {
45
+ const leftQueue = yield* Queue.unbounded<Option.Option<A>>();
46
+ const rightQueue = yield* Queue.unbounded<Option.Option<A2>>();
47
+
48
+ const leftSink = makeSink(sink.onFailure, (a: A) => Queue.offer(leftQueue, Option.some(a)));
49
+ const rightSink = makeSink(sink.onFailure, (b: A2) =>
50
+ Queue.offer(rightQueue, Option.some(b)),
51
+ );
52
+
53
+ const runLeft = self
54
+ .run(leftSink)
55
+ .pipe(Effect.ensuring(Queue.offer(leftQueue, Option.none())));
56
+ const runRight = that
57
+ .run(rightSink)
58
+ .pipe(Effect.ensuring(Queue.offer(rightQueue, Option.none())));
59
+
60
+ const consumer = Effect.gen(function* () {
61
+ while (true) {
62
+ const exitA = yield* Effect.exit(Queue.take(leftQueue));
63
+ if (Exit.isFailure(exitA)) return;
64
+ if (Option.isNone(exitA.value)) return;
65
+ const exitB = yield* Effect.exit(Queue.take(rightQueue));
66
+ if (Exit.isFailure(exitB)) return;
67
+ if (Option.isNone(exitB.value)) return;
68
+ yield* sink.onSuccess([exitA.value.value, exitB.value.value] as const);
69
+ }
70
+ });
71
+
72
+ const leftFiber = yield* Effect.forkChild(runLeft);
73
+ const rightFiber = yield* Effect.forkChild(runRight);
74
+ yield* consumer.pipe(
75
+ Effect.ensuring(Fiber.interrupt(leftFiber)),
76
+ Effect.ensuring(Fiber.interrupt(rightFiber)),
77
+ );
78
+ }),
79
+ ),
80
+ );
81
+
82
+ /**
83
+ * Zips two Fx streams in strict lockstep and combines each pair with a function.
84
+ * Emits `f(a, b)` when both streams have produced their next value.
85
+ *
86
+ * **Completion:** Completes when the **first** of the two streams completes.
87
+ * **Errors:** The first failure from either stream fails the result.
88
+ *
89
+ * @param that - The second Fx stream.
90
+ * @param f - Function to combine the pair `(a, b)` into a single value.
91
+ * @returns An Fx that emits combined values.
92
+ * @since 1.0.0
93
+ * @category combinators
94
+ */
95
+ export const zipWith: {
96
+ <A, A2, E2, R2, B>(
97
+ that: Fx<A2, E2, R2>,
98
+ f: (a: A, b: A2) => B,
99
+ ): <E, R>(self: Fx<A, E, R>) => Fx<B, E | E2, R | R2>;
100
+ <A, E, R, A2, E2, R2, B>(
101
+ self: Fx<A, E, R>,
102
+ that: Fx<A2, E2, R2>,
103
+ f: (a: A, b: A2) => B,
104
+ ): Fx<B, E | E2, R | R2>;
105
+ } = dual(
106
+ 3,
107
+ <A, E, R, A2, E2, R2, B>(
108
+ self: Fx<A, E, R>,
109
+ that: Fx<A2, E2, R2>,
110
+ f: (a: A, b: A2) => B,
111
+ ): Fx<B, E | E2, R | R2> => map(zip(self, that), (pair) => f(pair[0], pair[1])),
112
+ );
113
+
114
+ /**
115
+ * Zips two Fx streams by latest values: waits for both to emit at least once,
116
+ * then emits `[left, right]` whenever **either** stream emits (using the latest
117
+ * value from the other). No strict pairing; output count is the sum of
118
+ * emissions from both after the first pair.
119
+ *
120
+ * **Completion:** Completes when **both** streams have completed.
121
+ * **Errors:** The first failure from either stream fails the result.
122
+ *
123
+ * @param that - The second Fx stream.
124
+ * @returns An Fx that emits `[AL, AR]` on every update from either stream.
125
+ * @since 1.0.0
126
+ * @category combinators
127
+ */
128
+ export const zipLatest: {
129
+ <AR, ER, RR>(
130
+ that: Fx<AR, ER, RR>,
131
+ ): <AL, EL, RL>(self: Fx<AL, EL, RL>) => Fx<readonly [AL, AR], EL | ER, RL | RR>;
132
+ <AL, EL, RL, AR, ER, RR>(
133
+ self: Fx<AL, EL, RL>,
134
+ that: Fx<AR, ER, RR>,
135
+ ): Fx<readonly [AL, AR], EL | ER, RL | RR>;
136
+ } = dual(
137
+ 2,
138
+ <AL, EL, RL, AR, ER, RR>(
139
+ self: Fx<AL, EL, RL>,
140
+ that: Fx<AR, ER, RR>,
141
+ ): Fx<readonly [AL, AR], EL | ER, RL | RR> => tuple(self, that),
142
+ );
143
+
144
+ /**
145
+ * Zips two Fx streams by latest values and combines each pair with a function.
146
+ * Waits for both to emit at least once, then emits `f(left, right)` whenever
147
+ * either stream emits.
148
+ *
149
+ * **Completion:** Completes when **both** streams have completed.
150
+ * **Errors:** The first failure from either stream fails the result.
151
+ *
152
+ * @param that - The second Fx stream.
153
+ * @param f - Function to combine the latest `(left, right)` into a single value.
154
+ * @returns An Fx that emits combined values.
155
+ * @since 1.0.0
156
+ * @category combinators
157
+ */
158
+ export const zipLatestWith: {
159
+ <AL, AR, ER, RR, B>(
160
+ that: Fx<AR, ER, RR>,
161
+ f: (left: AL, right: AR) => B,
162
+ ): <EL, RL>(self: Fx<AL, EL, RL>) => Fx<B, EL | ER, RL | RR>;
163
+ <AL, EL, RL, AR, ER, RR, B>(
164
+ self: Fx<AL, EL, RL>,
165
+ that: Fx<AR, ER, RR>,
166
+ f: (left: AL, right: AR) => B,
167
+ ): Fx<B, EL | ER, RL | RR>;
168
+ } = dual(
169
+ 3,
170
+ <AL, EL, RL, AR, ER, RR, B>(
171
+ self: Fx<AL, EL, RL>,
172
+ that: Fx<AR, ER, RR>,
173
+ f: (left: AL, right: AR) => B,
174
+ ): Fx<B, EL | ER, RL | RR> => map(zipLatest(self, that), (pair) => f(pair[0], pair[1])),
175
+ );
@@ -14,10 +14,10 @@ import { make } from "./make.js";
14
14
  * @category constructors
15
15
  */
16
16
  export const at: {
17
- (delay: Duration.DurationInput): <A>(value: A) => Fx<A>;
18
- <A>(value: A, delay: Duration.DurationInput): Fx<A>;
17
+ (delay: Duration.Input): <A>(value: A) => Fx<A>;
18
+ <A>(value: A, delay: Duration.Input): Fx<A>;
19
19
  } = dual(
20
20
  2,
21
- <A>(value: A, delay: Duration.DurationInput): Fx<A> =>
21
+ <A>(value: A, delay: Duration.Input): Fx<A> =>
22
22
  make<A, never, never>((sink) => flatMap(sleep(delay), () => sink.onSuccess(value))),
23
23
  );
@@ -11,5 +11,5 @@ import { fromSchedule } from "./fromSchedule.js";
11
11
  * @since 1.0.0
12
12
  * @category constructors
13
13
  */
14
- export const periodic = (period: Duration.DurationInput): Fx<void> =>
14
+ export const periodic = (period: Duration.Input): Fx<void> =>
15
15
  /*#__PURE__*/ fromSchedule(spaced(period));