@effectionx/stream-helpers 0.7.1 → 0.7.3

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 (53) hide show
  1. package/README.md +149 -0
  2. package/batch.test.ts +2 -2
  3. package/batch.ts +0 -1
  4. package/dist/batch.d.ts.map +1 -1
  5. package/dist/batch.js +0 -1
  6. package/dist/drain.d.ts +24 -0
  7. package/dist/drain.d.ts.map +1 -0
  8. package/dist/drain.js +33 -0
  9. package/dist/first.d.ts +28 -0
  10. package/dist/first.d.ts.map +1 -0
  11. package/dist/first.js +67 -0
  12. package/dist/for-each.d.ts.map +1 -1
  13. package/dist/for-each.js +12 -8
  14. package/dist/last.d.ts +29 -0
  15. package/dist/last.d.ts.map +1 -0
  16. package/dist/last.js +81 -0
  17. package/dist/mod.d.ts +6 -0
  18. package/dist/mod.d.ts.map +1 -1
  19. package/dist/mod.js +6 -0
  20. package/dist/subject.d.ts.map +1 -1
  21. package/dist/subject.js +0 -1
  22. package/dist/take-until.d.ts +46 -0
  23. package/dist/take-until.d.ts.map +1 -0
  24. package/dist/take-until.js +68 -0
  25. package/dist/take-while.d.ts +41 -0
  26. package/dist/take-while.d.ts.map +1 -0
  27. package/dist/take-while.js +64 -0
  28. package/dist/take.d.ts +36 -0
  29. package/dist/take.d.ts.map +1 -0
  30. package/dist/take.js +59 -0
  31. package/dist/test-helpers/faucet.d.ts.map +1 -1
  32. package/dist/test-helpers/faucet.js +28 -24
  33. package/dist/tsconfig.tsbuildinfo +1 -1
  34. package/drain.test.ts +52 -0
  35. package/drain.ts +35 -0
  36. package/first.test.ts +53 -0
  37. package/first.ts +74 -0
  38. package/for-each.ts +12 -8
  39. package/last.test.ts +65 -0
  40. package/last.ts +96 -0
  41. package/mod.ts +6 -0
  42. package/package.json +5 -4
  43. package/subject.ts +0 -1
  44. package/take-until.test.ts +113 -0
  45. package/take-until.ts +76 -0
  46. package/take-while.test.ts +89 -0
  47. package/take-while.ts +74 -0
  48. package/take.test.ts +83 -0
  49. package/take.ts +67 -0
  50. package/test-helpers/faucet.test.ts +3 -3
  51. package/test-helpers/faucet.ts +28 -24
  52. package/tracker.test.ts +4 -4
  53. package/valve.test.ts +2 -2
package/README.md CHANGED
@@ -165,6 +165,155 @@ function* example() {
165
165
  }
166
166
  ```
167
167
 
168
+ ### Drain
169
+
170
+ The `drain` helper exhausts a stream, discarding all yielded values, and returns
171
+ the close value. This is useful when you only care about the final result of a
172
+ stream, not the intermediate values.
173
+
174
+ ```typescript
175
+ import { drain } from "@effectionx/stream-helpers";
176
+
177
+ function* example() {
178
+ // Get the response from a request channel (ignoring any progress)
179
+ const channel = yield* transport.send(request);
180
+ const response = yield* drain(channel);
181
+ console.log(response); // The close value
182
+ }
183
+ ```
184
+
185
+ ### First
186
+
187
+ The `first` helper returns the first value yielded by a stream, or `undefined`
188
+ if the stream closes without yielding any values. Use `first.expect()` to throw
189
+ an error instead of returning `undefined`.
190
+
191
+ ```typescript
192
+ import { first } from "@effectionx/stream-helpers";
193
+ import { streamOf } from "@effectionx/stream-helpers";
194
+
195
+ function* example() {
196
+ const stream = streamOf([1, 2, 3]);
197
+ const value = yield* first(stream);
198
+ console.log(value); // 1
199
+
200
+ const empty = streamOf([]);
201
+ const none = yield* first(empty);
202
+ console.log(none); // undefined
203
+
204
+ // Use first.expect() to throw if stream is empty
205
+ const required = yield* first.expect(stream); // throws if empty
206
+ }
207
+ ```
208
+
209
+ ### Last
210
+
211
+ The `last` helper returns the last value yielded by a stream, or `undefined`
212
+ if the stream closes without yielding any values. It exhausts the entire stream
213
+ to find the last value. Use `last.expect()` to throw an error instead of
214
+ returning `undefined`.
215
+
216
+ ```typescript
217
+ import { last } from "@effectionx/stream-helpers";
218
+ import { streamOf } from "@effectionx/stream-helpers";
219
+
220
+ function* example() {
221
+ const stream = streamOf([1, 2, 3]);
222
+ const value = yield* last(stream);
223
+ console.log(value); // 3
224
+
225
+ const empty = streamOf([]);
226
+ const none = yield* last(empty);
227
+ console.log(none); // undefined
228
+
229
+ // Use last.expect() to throw if stream is empty
230
+ const required = yield* last.expect(stream); // throws if empty
231
+ }
232
+ ```
233
+
234
+ ### Take
235
+
236
+ The `take` helper creates a stream transformer that yields the first `n` values
237
+ from the source stream, then closes with the last taken value.
238
+
239
+ ```typescript
240
+ import { take, forEach, streamOf } from "@effectionx/stream-helpers";
241
+ import { pipe } from "remeda";
242
+
243
+ function* example() {
244
+ const stream = streamOf([1, 2, 3, 4, 5]);
245
+ // yields 1, 2, then closes with 3
246
+ const closeValue = yield* forEach(function* (value) {
247
+ console.log(value); // 1, then 2
248
+ }, take(3)(stream));
249
+ console.log(closeValue); // 3
250
+ }
251
+
252
+ // Works with pipe
253
+ const limited = pipe(source, take(3));
254
+ ```
255
+
256
+ ### TakeWhile
257
+
258
+ The `takeWhile` helper creates a stream transformer that yields values while
259
+ the predicate returns true. When the predicate returns false, the stream closes
260
+ immediately (the failing value is not included).
261
+
262
+ ```typescript
263
+ import { takeWhile, forEach, streamOf } from "@effectionx/stream-helpers";
264
+ import { pipe } from "remeda";
265
+
266
+ function* example() {
267
+ const stream = streamOf([1, 2, 3, 4, 5]);
268
+ // yields 1, 2 (stops when value >= 3)
269
+ yield* forEach(function* (value) {
270
+ console.log(value); // 1, then 2
271
+ }, takeWhile((x) => x < 3)(stream));
272
+ }
273
+
274
+ // Works with pipe
275
+ const filtered = pipe(source, takeWhile((x) => x.isValid));
276
+ ```
277
+
278
+ ### TakeUntil
279
+
280
+ The `takeUntil` helper creates a stream transformer that yields values until
281
+ the predicate returns true. When the predicate matches, the stream closes with
282
+ the matching value. This is useful for "iterate until a condition is met"
283
+ patterns.
284
+
285
+ ```typescript
286
+ import { takeUntil, forEach } from "@effectionx/stream-helpers";
287
+ import { pipe } from "remeda";
288
+
289
+ function* example() {
290
+ // Iterate validation progress until we get a terminal status
291
+ const result = yield* forEach(
292
+ function* (progress) {
293
+ showSpinner(progress.status);
294
+ },
295
+ takeUntil((p) => p.status === "valid" || p.status === "invalid")(
296
+ validationStream,
297
+ ),
298
+ );
299
+
300
+ // result is the validation object with terminal status
301
+ if (result.status === "valid") {
302
+ console.log("Validation passed!");
303
+ }
304
+ }
305
+
306
+ // Works with pipe
307
+ const untilDone = pipe(source, takeUntil((x) => x.done));
308
+ ```
309
+
310
+ **Key difference between `takeWhile` and `takeUntil`:**
311
+
312
+ | Helper | Yields | Closes With | Use Case |
313
+ | ----------- | ----------------------- | -------------- | ----------------------- |
314
+ | `takeWhile` | While predicate is true | `undefined` | "Keep going while good" |
315
+ | `takeUntil` | Until predicate is true | Matching value | "Stop when you find it" |
316
+
168
317
  ### ForEach
169
318
 
170
319
  The `forEach` helper invokes a function for each item passing through a stream.
package/batch.test.ts CHANGED
@@ -57,7 +57,7 @@ describe("batch", () => {
57
57
  }, stream),
58
58
  );
59
59
 
60
- yield* sleep(1);
60
+ yield* sleep(0);
61
61
 
62
62
  yield* faucet.pour(function* (send) {
63
63
  for (let i = 1; i <= 10; i++) {
@@ -89,7 +89,7 @@ describe("batch", () => {
89
89
  }, stream),
90
90
  );
91
91
 
92
- yield* sleep(1);
92
+ yield* sleep(0);
93
93
 
94
94
  yield* faucet.pour([1, 2, 3, 4, 5, 6]);
95
95
 
package/batch.ts CHANGED
@@ -41,7 +41,6 @@ export function batch(
41
41
  value: undefined as never,
42
42
  };
43
43
  if (lastPull && options.maxTime) {
44
- // biome-ignore lint/style/noNonNullAssertion: lastPull checked above
45
44
  const timeout = yield* timebox(options.maxTime, () => lastPull!);
46
45
  if (timeout.timeout) {
47
46
  yield* lastPull.halt();
@@ -1 +1 @@
1
- {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../batch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,WAAW,CAAC;AAE1D,KAAK,iBAAiB,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,CAC9D,CAAC,EACD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CACvB,GACC;KACG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC,IAAI,CAAC,CAAC;AAEV,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,iBAAiB,CAAC,YAAY,CAAC,GACvC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CA8E/D"}
1
+ {"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../batch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,WAAW,CAAC;AAE1D,KAAK,iBAAiB,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,IAAI,CAC9D,CAAC,EACD,OAAO,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CACvB,GACC;KACG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC,IAAI,CAAC,CAAC;AAEV,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CACnB,OAAO,EAAE,iBAAiB,CAAC,YAAY,CAAC,GACvC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CA6E/D"}
package/dist/batch.js CHANGED
@@ -24,7 +24,6 @@ export function batch(options) {
24
24
  value: undefined,
25
25
  };
26
26
  if (lastPull && options.maxTime) {
27
- // biome-ignore lint/style/noNonNullAssertion: lastPull checked above
28
27
  const timeout = yield* timebox(options.maxTime, () => lastPull);
29
28
  if (timeout.timeout) {
30
29
  yield* lastPull.halt();
@@ -0,0 +1,24 @@
1
+ import type { Operation, Stream } from "effection";
2
+ /**
3
+ * Exhausts a stream, discarding all yielded values, and returns the close value.
4
+ *
5
+ * Use this when you only care about the final result of a stream, not the
6
+ * intermediate values. This is common for request/response patterns where
7
+ * the response is the close value and there may be no progress events.
8
+ *
9
+ * @template T - The type of items in the stream (discarded)
10
+ * @template TClose - The type of the close value returned when the stream ends
11
+ * @param stream - The stream to drain
12
+ * @returns The close value of the stream
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { drain } from "./drain.ts";
17
+ *
18
+ * // Get the response from a request channel (ignoring any progress)
19
+ * const channel = yield* transport.send(request);
20
+ * const response = yield* drain(channel);
21
+ * ```
22
+ */
23
+ export declare function drain<T, TClose>(stream: Stream<T, TClose>): Operation<TClose>;
24
+ //# sourceMappingURL=drain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drain.d.ts","sourceRoot":"","sources":["../drain.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAW7E"}
package/dist/drain.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Exhausts a stream, discarding all yielded values, and returns the close value.
3
+ *
4
+ * Use this when you only care about the final result of a stream, not the
5
+ * intermediate values. This is common for request/response patterns where
6
+ * the response is the close value and there may be no progress events.
7
+ *
8
+ * @template T - The type of items in the stream (discarded)
9
+ * @template TClose - The type of the close value returned when the stream ends
10
+ * @param stream - The stream to drain
11
+ * @returns The close value of the stream
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { drain } from "./drain.ts";
16
+ *
17
+ * // Get the response from a request channel (ignoring any progress)
18
+ * const channel = yield* transport.send(request);
19
+ * const response = yield* drain(channel);
20
+ * ```
21
+ */
22
+ export function drain(stream) {
23
+ return {
24
+ *[Symbol.iterator]() {
25
+ const subscription = yield* stream;
26
+ let result = yield* subscription.next();
27
+ while (!result.done) {
28
+ result = yield* subscription.next();
29
+ }
30
+ return result.value;
31
+ },
32
+ };
33
+ }
@@ -0,0 +1,28 @@
1
+ import type { Operation, Stream } from "effection";
2
+ /**
3
+ * Returns the first value yielded by a stream, or `undefined` if the stream
4
+ * closes without yielding any values.
5
+ *
6
+ * Use `first.expect()` if you want to throw an error when the stream is empty.
7
+ *
8
+ * @template T - The type of items in the stream
9
+ * @template TClose - The type of the close value (unused)
10
+ * @param stream - The stream to get the first value from
11
+ * @returns The first value yielded by the stream, or `undefined` if empty
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { first } from "./first.ts";
16
+ *
17
+ * const stream = streamOf([1, 2, 3]);
18
+ * const value = yield* first(stream); // returns 1
19
+ *
20
+ * const empty = streamOf([]);
21
+ * const value = yield* first(empty); // returns undefined
22
+ * ```
23
+ */
24
+ export declare function first<T, TClose>(stream: Stream<T, TClose>): Operation<T | undefined>;
25
+ export declare namespace first {
26
+ var expect: <T, TClose>(stream: Stream<T, TClose>) => Operation<T>;
27
+ }
28
+ //# sourceMappingURL=first.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"first.d.ts","sourceRoot":"","sources":["../first.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,EAC7B,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GACxB,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC,CAW1B;yBAbe,KAAK;iBAoCe,CAAC,EAAE,MAAM,UACnC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KACxB,SAAS,CAAC,CAAC,CAAC"}
package/dist/first.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Returns the first value yielded by a stream, or `undefined` if the stream
3
+ * closes without yielding any values.
4
+ *
5
+ * Use `first.expect()` if you want to throw an error when the stream is empty.
6
+ *
7
+ * @template T - The type of items in the stream
8
+ * @template TClose - The type of the close value (unused)
9
+ * @param stream - The stream to get the first value from
10
+ * @returns The first value yielded by the stream, or `undefined` if empty
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * import { first } from "./first.ts";
15
+ *
16
+ * const stream = streamOf([1, 2, 3]);
17
+ * const value = yield* first(stream); // returns 1
18
+ *
19
+ * const empty = streamOf([]);
20
+ * const value = yield* first(empty); // returns undefined
21
+ * ```
22
+ */
23
+ export function first(stream) {
24
+ return {
25
+ *[Symbol.iterator]() {
26
+ const subscription = yield* stream;
27
+ const result = yield* subscription.next();
28
+ if (result.done) {
29
+ return undefined;
30
+ }
31
+ return result.value;
32
+ },
33
+ };
34
+ }
35
+ /**
36
+ * Returns the first value yielded by a stream.
37
+ * Throws an error if the stream closes without yielding any values.
38
+ *
39
+ * @template T - The type of items in the stream
40
+ * @template TClose - The type of the close value (unused)
41
+ * @param stream - The stream to get the first value from
42
+ * @returns The first value yielded by the stream
43
+ * @throws Error if the stream closes without yielding any values
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * import { first } from "./first.ts";
48
+ *
49
+ * const stream = streamOf([1, 2, 3]);
50
+ * const value = yield* first.expect(stream); // returns 1
51
+ *
52
+ * const empty = streamOf([]);
53
+ * const value = yield* first.expect(empty); // throws Error
54
+ * ```
55
+ */
56
+ first.expect = function expectFirst(stream) {
57
+ return {
58
+ *[Symbol.iterator]() {
59
+ const subscription = yield* stream;
60
+ const result = yield* subscription.next();
61
+ if (result.done) {
62
+ throw new Error("Stream closed without yielding any values");
63
+ }
64
+ return result.value;
65
+ },
66
+ };
67
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"for-each.d.ts","sourceRoot":"","sources":["../for-each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAiB,OAAO,CAAC,CAAC,EAAE,MAAM,EAChC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,EAChC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GACxB,SAAS,CAAC,MAAM,CAAC,CAQnB"}
1
+ {"version":3,"file":"for-each.d.ts","sourceRoot":"","sources":["../for-each.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAC/B,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,EAChC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GACxB,SAAS,CAAC,MAAM,CAAC,CAYnB"}
package/dist/for-each.js CHANGED
@@ -22,12 +22,16 @@
22
22
  * yield* stream.send(2);
23
23
  * ```
24
24
  */
25
- export function* forEach(fn, stream) {
26
- let subscription = yield* stream;
27
- let next = yield* subscription.next();
28
- while (!next.done) {
29
- yield* fn(next.value);
30
- next = yield* subscription.next();
31
- }
32
- return next.value;
25
+ export function forEach(fn, stream) {
26
+ return {
27
+ *[Symbol.iterator]() {
28
+ let subscription = yield* stream;
29
+ let next = yield* subscription.next();
30
+ while (!next.done) {
31
+ yield* fn(next.value);
32
+ next = yield* subscription.next();
33
+ }
34
+ return next.value;
35
+ },
36
+ };
33
37
  }
package/dist/last.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { Operation, Stream } from "effection";
2
+ /**
3
+ * Returns the last value yielded by a stream, or `undefined` if the stream
4
+ * closes without yielding any values.
5
+ *
6
+ * Exhausts the entire stream to find the last value.
7
+ * Use `last.expect()` if you want to throw an error when the stream is empty.
8
+ *
9
+ * @template T - The type of items in the stream
10
+ * @template TClose - The type of the close value (unused)
11
+ * @param stream - The stream to get the last value from
12
+ * @returns The last value yielded by the stream, or `undefined` if empty
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { last } from "./last.ts";
17
+ *
18
+ * const stream = streamOf([1, 2, 3]);
19
+ * const value = yield* last(stream); // returns 3
20
+ *
21
+ * const empty = streamOf([]);
22
+ * const value = yield* last(empty); // returns undefined
23
+ * ```
24
+ */
25
+ export declare function last<T, TClose>(stream: Stream<T, TClose>): Operation<T | undefined>;
26
+ export declare namespace last {
27
+ var expect: <T, TClose>(stream: Stream<T, TClose>) => Operation<T>;
28
+ }
29
+ //# sourceMappingURL=last.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"last.d.ts","sourceRoot":"","sources":["../last.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAC5B,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GACxB,SAAS,CAAC,CAAC,GAAG,SAAS,CAAC,CAqB1B;yBAvBe,IAAI;iBA+Cc,CAAC,EAAE,MAAM,UACjC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KACxB,SAAS,CAAC,CAAC,CAAC"}
package/dist/last.js ADDED
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Returns the last value yielded by a stream, or `undefined` if the stream
3
+ * closes without yielding any values.
4
+ *
5
+ * Exhausts the entire stream to find the last value.
6
+ * Use `last.expect()` if you want to throw an error when the stream is empty.
7
+ *
8
+ * @template T - The type of items in the stream
9
+ * @template TClose - The type of the close value (unused)
10
+ * @param stream - The stream to get the last value from
11
+ * @returns The last value yielded by the stream, or `undefined` if empty
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { last } from "./last.ts";
16
+ *
17
+ * const stream = streamOf([1, 2, 3]);
18
+ * const value = yield* last(stream); // returns 3
19
+ *
20
+ * const empty = streamOf([]);
21
+ * const value = yield* last(empty); // returns undefined
22
+ * ```
23
+ */
24
+ export function last(stream) {
25
+ return {
26
+ *[Symbol.iterator]() {
27
+ const subscription = yield* stream;
28
+ const first = yield* subscription.next();
29
+ if (first.done) {
30
+ return undefined;
31
+ }
32
+ let lastValue = first.value;
33
+ let result = yield* subscription.next();
34
+ while (!result.done) {
35
+ lastValue = result.value;
36
+ result = yield* subscription.next();
37
+ }
38
+ return lastValue;
39
+ },
40
+ };
41
+ }
42
+ /**
43
+ * Returns the last value yielded by a stream.
44
+ * Exhausts the entire stream to find the last value.
45
+ * Throws an error if the stream closes without yielding any values.
46
+ *
47
+ * @template T - The type of items in the stream
48
+ * @template TClose - The type of the close value (unused)
49
+ * @param stream - The stream to get the last value from
50
+ * @returns The last value yielded by the stream
51
+ * @throws Error if the stream closes without yielding any values
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * import { last } from "./last.ts";
56
+ *
57
+ * const stream = streamOf([1, 2, 3]);
58
+ * const value = yield* last.expect(stream); // returns 3
59
+ *
60
+ * const empty = streamOf([]);
61
+ * const value = yield* last.expect(empty); // throws Error
62
+ * ```
63
+ */
64
+ last.expect = function expectLast(stream) {
65
+ return {
66
+ *[Symbol.iterator]() {
67
+ const subscription = yield* stream;
68
+ const first = yield* subscription.next();
69
+ if (first.done) {
70
+ throw new Error("Stream closed without yielding any values");
71
+ }
72
+ let lastValue = first.value;
73
+ let result = yield* subscription.next();
74
+ while (!result.done) {
75
+ lastValue = result.value;
76
+ result = yield* subscription.next();
77
+ }
78
+ return lastValue;
79
+ },
80
+ };
81
+ };
package/dist/mod.d.ts CHANGED
@@ -8,4 +8,10 @@ export * from "./subject.ts";
8
8
  export * from "./lines.ts";
9
9
  export * from "./stream-of.ts";
10
10
  export * from "./reduce.ts";
11
+ export * from "./drain.ts";
12
+ export * from "./first.ts";
13
+ export * from "./last.ts";
14
+ export * from "./take.ts";
15
+ export * from "./take-while.ts";
16
+ export * from "./take-until.ts";
11
17
  //# sourceMappingURL=mod.d.ts.map
package/dist/mod.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../mod.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC"}
package/dist/mod.js CHANGED
@@ -8,3 +8,9 @@ export * from "./subject.js";
8
8
  export * from "./lines.js";
9
9
  export * from "./stream-of.js";
10
10
  export * from "./reduce.js";
11
+ export * from "./drain.js";
12
+ export * from "./first.js";
13
+ export * from "./last.js";
14
+ export * from "./take.js";
15
+ export * from "./take-while.js";
16
+ export * from "./take-until.js";
@@ -1 +1 @@
1
- {"version":3,"file":"subject.d.ts","sourceRoot":"","sources":["../subject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,WAAW,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,aAAa,CAAC,CAAC,KAAK,CAAC,MAAM,EACzC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KACtB,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CA2BrB"}
1
+ {"version":3,"file":"subject.d.ts","sourceRoot":"","sources":["../subject.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,WAAW,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,aAAa,CAAC,CAAC,KAAK,CAAC,MAAM,EACzC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KACtB,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CA0BrB"}
package/dist/subject.js CHANGED
@@ -42,7 +42,6 @@ export function createSubject() {
42
42
  ? {
43
43
  *next() {
44
44
  iterator = upstream;
45
- // biome-ignore lint/style/noNonNullAssertion: current checked in ternary condition
46
45
  return current;
47
46
  },
48
47
  }
@@ -0,0 +1,46 @@
1
+ import type { Stream } from "effection";
2
+ /**
3
+ * Creates a stream transformer that yields values from the source stream
4
+ * until the predicate returns true. Closes with the matching value when
5
+ * the predicate returns true.
6
+ *
7
+ * This is useful for "iterate until a condition is met" patterns, where
8
+ * the matching value is meaningful (e.g., a terminal status).
9
+ *
10
+ * If the source stream closes before the predicate returns true, the
11
+ * resulting stream closes with the source's close value.
12
+ *
13
+ * @template T - The type of items in the stream
14
+ * @template TClose - The type of the close value
15
+ * @param predicate - A function that returns true to stop taking values
16
+ * @returns A stream transformer that yields values until predicate is true,
17
+ * closing with the matching value
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { takeUntil, forEach } from "@effectionx/stream-helpers";
22
+ *
23
+ * // Iterate validation progress until we get a terminal status
24
+ * const result = yield* forEach(function*(progress) {
25
+ * showSpinner(progress.status);
26
+ * }, takeUntil((p) => p.status === "valid" || p.status === "invalid")(channel));
27
+ *
28
+ * // result is the validation object with terminal status
29
+ * if (result.status === "valid") {
30
+ * // proceed
31
+ * }
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * import { takeUntil, map } from "@effectionx/stream-helpers";
37
+ * import { pipe } from "remeda";
38
+ *
39
+ * const limited = pipe(
40
+ * source,
41
+ * takeUntil((x) => x.done),
42
+ * );
43
+ * ```
44
+ */
45
+ export declare function takeUntil<T>(predicate: (item: T) => boolean): <TClose>(stream: Stream<T, TClose>) => Stream<T, T | TClose>;
46
+ //# sourceMappingURL=take-until.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"take-until.d.ts","sourceRoot":"","sources":["../take-until.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,SAAS,CAAC,CAAC,EACzB,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,GAC9B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CA4B9D"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Creates a stream transformer that yields values from the source stream
3
+ * until the predicate returns true. Closes with the matching value when
4
+ * the predicate returns true.
5
+ *
6
+ * This is useful for "iterate until a condition is met" patterns, where
7
+ * the matching value is meaningful (e.g., a terminal status).
8
+ *
9
+ * If the source stream closes before the predicate returns true, the
10
+ * resulting stream closes with the source's close value.
11
+ *
12
+ * @template T - The type of items in the stream
13
+ * @template TClose - The type of the close value
14
+ * @param predicate - A function that returns true to stop taking values
15
+ * @returns A stream transformer that yields values until predicate is true,
16
+ * closing with the matching value
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { takeUntil, forEach } from "@effectionx/stream-helpers";
21
+ *
22
+ * // Iterate validation progress until we get a terminal status
23
+ * const result = yield* forEach(function*(progress) {
24
+ * showSpinner(progress.status);
25
+ * }, takeUntil((p) => p.status === "valid" || p.status === "invalid")(channel));
26
+ *
27
+ * // result is the validation object with terminal status
28
+ * if (result.status === "valid") {
29
+ * // proceed
30
+ * }
31
+ * ```
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * import { takeUntil, map } from "@effectionx/stream-helpers";
36
+ * import { pipe } from "remeda";
37
+ *
38
+ * const limited = pipe(
39
+ * source,
40
+ * takeUntil((x) => x.done),
41
+ * );
42
+ * ```
43
+ */
44
+ export function takeUntil(predicate) {
45
+ return (stream) => ({
46
+ *[Symbol.iterator]() {
47
+ const subscription = yield* stream;
48
+ let done = false;
49
+ return {
50
+ *next() {
51
+ if (done) {
52
+ return { done: true, value: undefined };
53
+ }
54
+ const result = yield* subscription.next();
55
+ if (result.done) {
56
+ return result;
57
+ }
58
+ if (predicate(result.value)) {
59
+ done = true;
60
+ // Close with the matching value
61
+ return { done: true, value: result.value };
62
+ }
63
+ return result;
64
+ },
65
+ };
66
+ },
67
+ });
68
+ }