@soffinal/stream 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +135 -637
  2. package/dist/index.d.ts +0 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +2 -2
  5. package/dist/index.js.map +10 -12
  6. package/dist/stream.d.ts +25 -6
  7. package/dist/stream.d.ts.map +1 -1
  8. package/dist/transformers/filter/filter.d.ts +18 -0
  9. package/dist/transformers/filter/filter.d.ts.map +1 -0
  10. package/dist/transformers/filter/index.d.ts +2 -0
  11. package/dist/transformers/filter/index.d.ts.map +1 -0
  12. package/dist/transformers/flat/flat.d.ts +3 -0
  13. package/dist/transformers/flat/flat.d.ts.map +1 -0
  14. package/dist/transformers/flat/index.d.ts +2 -0
  15. package/dist/transformers/flat/index.d.ts.map +1 -0
  16. package/dist/transformers/gate/gate.d.ts +43 -0
  17. package/dist/transformers/gate/gate.d.ts.map +1 -0
  18. package/dist/transformers/gate/index.d.ts +2 -0
  19. package/dist/transformers/gate/index.d.ts.map +1 -0
  20. package/dist/transformers/index.d.ts +6 -4
  21. package/dist/transformers/index.d.ts.map +1 -1
  22. package/dist/transformers/map/index.d.ts +2 -0
  23. package/dist/transformers/map/index.d.ts.map +1 -0
  24. package/dist/transformers/map/map.d.ts +14 -0
  25. package/dist/transformers/map/map.d.ts.map +1 -0
  26. package/dist/transformers/merge/index.d.ts +2 -0
  27. package/dist/transformers/merge/index.d.ts.map +1 -0
  28. package/dist/transformers/merge/merge.d.ts +3 -0
  29. package/dist/transformers/merge/merge.d.ts.map +1 -0
  30. package/dist/transformers/state/index.d.ts +2 -0
  31. package/dist/transformers/state/index.d.ts.map +1 -0
  32. package/dist/transformers/state/state.d.ts +35 -0
  33. package/dist/transformers/state/state.d.ts.map +1 -0
  34. package/package.json +15 -17
  35. package/dist/reactive/index.d.ts +0 -5
  36. package/dist/reactive/index.d.ts.map +0 -1
  37. package/dist/reactive/list.d.ts +0 -171
  38. package/dist/reactive/list.d.ts.map +0 -1
  39. package/dist/reactive/map.d.ts +0 -107
  40. package/dist/reactive/map.d.ts.map +0 -1
  41. package/dist/reactive/set.d.ts +0 -102
  42. package/dist/reactive/set.d.ts.map +0 -1
  43. package/dist/reactive/state.d.ts +0 -79
  44. package/dist/reactive/state.d.ts.map +0 -1
  45. package/dist/transformers/filter.d.ts +0 -96
  46. package/dist/transformers/filter.d.ts.map +0 -1
  47. package/dist/transformers/flat.d.ts +0 -31
  48. package/dist/transformers/flat.d.ts.map +0 -1
  49. package/dist/transformers/map.d.ts +0 -106
  50. package/dist/transformers/map.d.ts.map +0 -1
  51. package/dist/transformers/merge.d.ts +0 -33
  52. package/dist/transformers/merge.d.ts.map +0 -1
  53. package/src/transformers/filter.md +0 -326
  54. package/src/transformers/flat.md +0 -56
  55. package/src/transformers/map.md +0 -430
  56. package/src/transformers/merge.md +0 -79
@@ -1,96 +0,0 @@
1
- import { Stream } from "../stream.ts";
2
- /**
3
- * Adaptive filter transformer that maintains state and can terminate streams.
4
- * Supports multiple concurrency strategies for async predicates.
5
- *
6
- * @template VALUE - The type of values flowing through the stream
7
- * @template STATE - The type of the internal state object
8
- * @template FILTERED - The type of filtered values (for type guards)
9
- *
10
- * @param initialStateOrPredicate - Initial state object or predicate function
11
- * @param statefulPredicateOrOptions - Stateful predicate function or options for simple predicates
12
- *
13
- * @returns A transformer function that can be used with `.pipe()`
14
- *
15
- * @see {@link Stream} - Complete copy-paste transformers library
16
- *
17
- * @example
18
- * // Simple synchronous filtering
19
- * stream.pipe(filter((value) => value > 0))
20
- *
21
- * @example
22
- * // Type guard filtering (synchronous only)
23
- * stream.pipe(filter((value): value is number => typeof value === "number"))
24
- *
25
- * @example
26
- * // Async filtering with sequential strategy (default)
27
- * stream.pipe(
28
- * filter(async (value) => {
29
- * const valid = await validateAsync(value);
30
- * return valid;
31
- * })
32
- * )
33
- *
34
- * @example
35
- * // Async filtering with concurrent-unordered strategy
36
- * stream.pipe(
37
- * filter(async (value) => {
38
- * const result = await expensiveCheck(value);
39
- * return result;
40
- * }, { strategy: "concurrent-unordered" })
41
- * )
42
- *
43
- * @example
44
- * // Async filtering with concurrent-ordered strategy
45
- * stream.pipe(
46
- * filter(async (value) => {
47
- * const result = await apiValidation(value);
48
- * return result;
49
- * }, { strategy: "concurrent-ordered" })
50
- * )
51
- *
52
- * @example
53
- * // Stateful filtering (always sequential)
54
- * stream.pipe(
55
- * filter({ count: 0 }, (state, value) => {
56
- * if (state.count >= 10) return; // Terminate after 10 items
57
- * return [value > 0, { count: state.count + 1 }];
58
- * })
59
- * )
60
- *
61
- * @example
62
- * // Stateful filtering with complex state
63
- * stream.pipe(
64
- * filter({ seen: new Set() }, (state, value) => {
65
- * if (state.seen.has(value)) return [false, state]; // Duplicate
66
- * state.seen.add(value);
67
- * return [true, state]; // First occurrence
68
- * })
69
- * )
70
- *
71
- * @example
72
- * // Stream termination
73
- * stream.pipe(
74
- * filter(async (value) => {
75
- * if (value === "STOP") return; // Terminates stream
76
- * return value.length > 3;
77
- * })
78
- * )
79
- */
80
- export declare const filter: filter.Filter;
81
- export declare namespace filter {
82
- type Options = {
83
- strategy: "sequential" | "concurrent-unordered" | "concurrent-ordered";
84
- };
85
- type Predicate<VALUE = unknown> = (value: VALUE) => boolean | void | Promise<boolean | void>;
86
- type GuardPredicate<VALUE = unknown, FILTERED extends VALUE = VALUE> = (value: VALUE) => value is FILTERED;
87
- type StatefulPredicate<VALUE = unknown, STATE extends Record<string, unknown> = {}> = (state: STATE, value: VALUE) => [boolean, STATE] | void | Promise<[boolean, STATE] | void>;
88
- type StatefulGuardPredicate<VALUE = unknown, STATE extends Record<string, unknown> = {}, FILTERED extends VALUE = VALUE> = (state: STATE, value: VALUE) => [boolean, STATE, FILTERED] | void | Promise<[boolean, STATE, FILTERED] | void>;
89
- interface Filter {
90
- <VALUE, FILTERED extends VALUE = VALUE>(predicate: GuardPredicate<VALUE, FILTERED>): (stream: Stream<VALUE>) => Stream<FILTERED>;
91
- <VALUE>(predicate: Predicate<VALUE>, options?: Options): (stream: Stream<VALUE>) => Stream<VALUE>;
92
- <VALUE, STATE extends Record<string, unknown> = {}>(initialState: STATE, predicate: StatefulPredicate<VALUE, STATE>): (stream: Stream<VALUE>) => Stream<VALUE>;
93
- <VALUE, STATE extends Record<string, unknown> = {}, FILTERED extends VALUE = VALUE>(initialState: STATE, predicate: StatefulGuardPredicate<VALUE, STATE, FILTERED>): (stream: Stream<VALUE>) => Stream<FILTERED>;
94
- }
95
- }
96
- //# sourceMappingURL=filter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/transformers/filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AACH,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MA+G3B,CAAC;AAEF,yBAAiB,MAAM,CAAC;IACtB,KAAY,OAAO,GAAG;QAAE,QAAQ,EAAE,YAAY,GAAG,sBAAsB,GAAG,oBAAoB,CAAA;KAAE,CAAC;IACjG,KAAY,SAAS,CAAC,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACpG,KAAY,cAAc,CAAC,KAAK,GAAG,OAAO,EAAE,QAAQ,SAAS,KAAK,GAAG,KAAK,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,IAAI,QAAQ,CAAC;IAClH,KAAY,iBAAiB,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,CAC3F,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,KACT,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IAChE,KAAY,sBAAsB,CAChC,KAAK,GAAG,OAAO,EACf,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,EAC1C,QAAQ,SAAS,KAAK,GAAG,KAAK,IAC5B,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;IACnH,UAAiB,MAAM;QACrB,CAAC,KAAK,EAAE,QAAQ,SAAS,KAAK,GAAG,KAAK,EAAE,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,CACnF,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAClB,MAAM,CAAC,QAAQ,CAAC,CAAC;QAEtB,CAAC,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC;QAElG,CAAC,KAAK,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,EAChD,YAAY,EAAE,KAAK,EACnB,SAAS,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,GACzC,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC;QAE5C,CAAC,KAAK,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,SAAS,KAAK,GAAG,KAAK,EAChF,YAAY,EAAE,KAAK,EACnB,SAAS,EAAE,sBAAsB,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,GACxD,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC;KAChD;CACF"}
@@ -1,31 +0,0 @@
1
- import { Stream } from "../stream.ts";
2
- /**
3
- * Flatten arrays in a stream, converting 1 array event into N individual events.
4
- *
5
- * @template VALUE - The type of values in the stream (should be arrays)
6
- * @template DEPTH - The depth of flattening (0 = one level, 1 = two levels, etc.)
7
- *
8
- * @param depth - How many levels deep to flatten (default: 0 = one level)
9
- *
10
- * @returns A transformer that flattens array values into individual events
11
- *
12
- * @see {@link Stream} - Complete copy-paste transformers library
13
- *
14
- * @example
15
- * // Basic flattening - 1 array → N events
16
- * const arrayStream = new Stream<number[]>();
17
- * const individualNumbers = arrayStream.pipe(flat());
18
- *
19
- * arrayStream.push([1, 2, 3]); // Emits: 1, 2, 3 as separate events
20
- *
21
- * @example
22
- * // Deep flattening
23
- * const deepArrays = new Stream<number[][]>();
24
- * const flattened = deepArrays.pipe(flat(1)); // Flatten 2 levels
25
- *
26
- * deepArrays.push([[1, 2], [3, 4]]);
27
- * // Emits: 1, 2, 3, 4 as separate events
28
- *
29
- */
30
- export declare function flat<VALUE, DEPTH extends number = 0>(depth?: DEPTH): (stream: Stream<VALUE>) => Stream<FlatArray<VALUE, DEPTH>>;
31
- //# sourceMappingURL=flat.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"flat.d.ts","sourceRoot":"","sources":["../../src/transformers/flat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,KAAK,SAAS,MAAM,GAAG,CAAC,EAClD,KAAK,GAAE,KAAkB,GACxB,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAe5D"}
@@ -1,106 +0,0 @@
1
- import { Stream } from "../stream.ts";
2
- /**
3
- * Adaptive map transformer that transforms values while maintaining state.
4
- * Supports multiple concurrency strategies for async mappers.
5
- *
6
- * @template VALUE - The type of input values
7
- * @template STATE - The type of the internal state object
8
- * @template MAPPED - The type of output values after transformation
9
- *
10
- * @param initialStateOrMapper - Initial state object or mapper function
11
- * @param statefulMapperOrOptions - Stateful mapper function or options for simple mappers
12
- *
13
- * @returns A transformer function that can be used with `.pipe()`
14
- *
15
- * @see {@link Stream} - Complete copy-paste transformers library
16
- *
17
- * @example
18
- * // Simple synchronous transformation
19
- * stream.pipe(map((value) => value * 2))
20
- *
21
- * @example
22
- * // Type transformation
23
- * stream.pipe(map((value: number) => value.toString()))
24
- *
25
- * @example
26
- * // Async transformation with sequential strategy (default)
27
- * stream.pipe(
28
- * map(async (value) => {
29
- * const result = await processAsync(value);
30
- * return result;
31
- * })
32
- * )
33
- *
34
- * @example
35
- * // Async transformation with concurrent-unordered strategy
36
- * stream.pipe(
37
- * map(async (value) => {
38
- * const enriched = await enrichWithAPI(value);
39
- * return enriched;
40
- * }, { strategy: "concurrent-unordered" })
41
- * )
42
- *
43
- * @example
44
- * // Async transformation with concurrent-ordered strategy
45
- * stream.pipe(
46
- * map(async (value) => {
47
- * const processed = await heavyProcessing(value);
48
- * return processed;
49
- * }, { strategy: "concurrent-ordered" })
50
- * )
51
- *
52
- * @example
53
- * // Stateful transformation (always sequential)
54
- * stream.pipe(
55
- * map({ sum: 0 }, (state, value) => {
56
- * const newSum = state.sum + value;
57
- * return [{ value, runningSum: newSum }, { sum: newSum }];
58
- * })
59
- * )
60
- *
61
- * @example
62
- * // Complex stateful transformation
63
- * stream.pipe(
64
- * map({ count: 0, items: [] }, (state, value) => {
65
- * const newItems = [...state.items, value];
66
- * const newCount = state.count + 1;
67
- * return [
68
- * {
69
- * item: value,
70
- * index: newCount,
71
- * total: newItems.length,
72
- * history: newItems
73
- * },
74
- * { count: newCount, items: newItems }
75
- * ];
76
- * })
77
- * )
78
- *
79
- * @example
80
- * // Async stateful transformation
81
- * stream.pipe(
82
- * map({ cache: new Map() }, async (state, value) => {
83
- * const cached = state.cache.get(value);
84
- * if (cached) return [cached, state];
85
- *
86
- * const processed = await expensiveOperation(value);
87
- * const newCache = new Map(state.cache);
88
- * newCache.set(value, processed);
89
- *
90
- * return [processed, { cache: newCache }];
91
- * })
92
- * )
93
- */
94
- export declare const map: map.Map;
95
- export declare namespace map {
96
- type Options = {
97
- strategy: "sequential" | "concurrent-unordered" | "concurrent-ordered";
98
- };
99
- type Mapper<VALUE = unknown, MAPPED = VALUE> = (value: VALUE) => MAPPED | Promise<MAPPED>;
100
- type StatefulMapper<VALUE = unknown, STATE extends Record<string, unknown> = {}, MAPPED = VALUE> = (state: STATE, value: VALUE) => [MAPPED, STATE] | Promise<[MAPPED, STATE]>;
101
- interface Map {
102
- <VALUE, MAPPED>(mapper: Mapper<VALUE, MAPPED>, options?: Options): (stream: Stream<VALUE>) => Stream<MAPPED>;
103
- <VALUE, STATE extends Record<string, unknown> = {}, MAPPED = VALUE>(initialState: STATE, mapper: StatefulMapper<VALUE, STATE, MAPPED>): (stream: Stream<VALUE>) => Stream<MAPPED>;
104
- }
105
- }
106
- //# sourceMappingURL=map.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../src/transformers/map.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FG;AACH,eAAO,MAAM,GAAG,EAAE,GAAG,CAAC,GAwFrB,CAAC;AAEF,yBAAiB,GAAG,CAAC;IACnB,KAAY,OAAO,GAAG;QAAE,QAAQ,EAAE,YAAY,GAAG,sBAAsB,GAAG,oBAAoB,CAAA;KAAE,CAAC;IACjG,KAAY,MAAM,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjG,KAAY,cAAc,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,GAAG,KAAK,IAAI,CACxG,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,KACT,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IAEhD,UAAiB,GAAG;QAClB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7G,CAAC,KAAK,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,GAAG,KAAK,EAChE,YAAY,EAAE,KAAK,EACnB,MAAM,EAAE,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAC3C,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC;KAC9C;CACF"}
@@ -1,33 +0,0 @@
1
- import { Stream } from "../stream.ts";
2
- /**
3
- * Merge multiple streams into a single stream with temporal ordering.
4
- *
5
- * @template VALUE - The type of values from the source stream
6
- * @template STREAMS - Tuple type of additional streams to merge
7
- *
8
- * @param streams - Additional streams to merge with the source stream
9
- *
10
- * @returns A transformer that merges all streams into one with union types
11
- *
12
- * @see {@link Stream} - Complete copy-paste transformers library
13
- *
14
- * @example
15
- * // Basic merge with type safety
16
- * const numbers = new Stream<number>();
17
- * const strings = new Stream<string>();
18
- * const merged = numbers.pipe(merge(strings));
19
- * // Type: Stream<number | string>
20
- *
21
- * @example
22
- * // Multiple streams
23
- * const stream1 = new Stream<number>();
24
- * const stream2 = new Stream<string>();
25
- * const stream3 = new Stream<boolean>();
26
- *
27
- * const combined = stream1.pipe(merge(stream2, stream3));
28
- * // Type: Stream<number | string | boolean>
29
- *
30
-
31
- */
32
- export declare function merge<VALUE, STREAMS extends [Stream<any>, ...Stream<any>[]]>(...streams: STREAMS): (stream: Stream<VALUE>) => Stream<VALUE | Stream.ValueOf<STREAMS[number]>>;
33
- //# sourceMappingURL=merge.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/transformers/merge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAC1E,GAAG,OAAO,EAAE,OAAO,GAClB,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CA0B5E"}
@@ -1,326 +0,0 @@
1
- # Filter Transformer
2
-
3
- The `filter` transformer selectively passes values through a stream based on predicate functions. It supports synchronous and asynchronous filtering, type guards, stateful operations, and multiple concurrency strategies.
4
-
5
- ## Quick Start
6
-
7
- ```typescript
8
- import { Stream, filter } from "@soffinal/stream";
9
-
10
- const numbers = new Stream<number>();
11
-
12
- // Simple filtering
13
- const positives = numbers.pipe(filter((n) => n > 0));
14
-
15
- positives.listen(console.log);
16
- numbers.push(-1, 2, -3, 4); // Outputs: 2, 4
17
- ```
18
-
19
- ## Basic Usage
20
-
21
- ### Synchronous Filtering
22
-
23
- ```typescript
24
- // Basic predicate
25
- stream.pipe(filter((value) => value > 10));
26
-
27
- // Complex conditions
28
- stream.pipe(filter((user) => user.active && user.age >= 18));
29
-
30
- // Null/undefined filtering
31
- stream.pipe(filter((value) => value != null));
32
- ```
33
-
34
- ### Type Guards
35
-
36
- Filter supports TypeScript type guards for type narrowing:
37
-
38
- ```typescript
39
- const mixed = new Stream<string | number>();
40
-
41
- // Type narrows to Stream<number>
42
- const numbers = mixed.pipe(filter((value): value is number => typeof value === "number"));
43
-
44
- // Type narrows to Stream<string>
45
- const strings = mixed.pipe(filter((value): value is string => typeof value === "string"));
46
- ```
47
-
48
- ## Asynchronous Filtering
49
-
50
- ### Sequential Processing (Default)
51
-
52
- ```typescript
53
- const stream = new Stream<string>();
54
-
55
- const validated = stream.pipe(
56
- filter(async (email) => {
57
- const isValid = await validateEmail(email);
58
- return isValid;
59
- })
60
- );
61
- ```
62
-
63
- ### Concurrent Strategies
64
-
65
- For expensive async predicates, choose a concurrency strategy:
66
-
67
- #### Concurrent Unordered
68
-
69
- Results emit as soon as they complete, potentially out of order:
70
-
71
- ```typescript
72
- const stream = new Stream<string>();
73
-
74
- const validated = stream.pipe(
75
- filter(
76
- async (url) => {
77
- const isReachable = await checkURL(url);
78
- return isReachable;
79
- },
80
- { strategy: "concurrent-unordered" }
81
- )
82
- );
83
-
84
- // URLs may emit in different order based on response times
85
- ```
86
-
87
- #### Concurrent Ordered
88
-
89
- Parallel processing but maintains original order:
90
-
91
- ```typescript
92
- const stream = new Stream<User>();
93
-
94
- const verified = stream.pipe(
95
- filter(
96
- async (user) => {
97
- const isVerified = await verifyUser(user.id);
98
- return isVerified;
99
- },
100
- { strategy: "concurrent-ordered" }
101
- )
102
- );
103
-
104
- // Users always emit in original order despite varying verification times
105
- ```
106
-
107
- ## Stateful Filtering
108
-
109
- Maintain state across filter operations for complex logic:
110
-
111
- ### Basic Stateful Filtering
112
-
113
- ```typescript
114
- const stream = new Stream<number>();
115
-
116
- // Take only first 5 items
117
- const limited = stream.pipe(
118
- filter({ count: 0 }, (state, value) => {
119
- if (state.count >= 5) return; // Terminate stream
120
- return [true, { count: state.count + 1 }];
121
- })
122
- );
123
- ```
124
-
125
- ### Advanced State Management
126
-
127
- ```typescript
128
- const stream = new Stream<string>();
129
-
130
- // Deduplicate values
131
- const unique = stream.pipe(
132
- filter({ seen: new Set<string>() }, (state, value) => {
133
- if (state.seen.has(value)) {
134
- return [false, state]; // Skip duplicate
135
- }
136
-
137
- const newSeen = new Set(state.seen);
138
- newSeen.add(value);
139
- return [true, { seen: newSeen }];
140
- })
141
- );
142
- ```
143
-
144
- ### Complex Stateful Logic
145
-
146
- ```typescript
147
- const events = new Stream<{ type: string; timestamp: number }>();
148
-
149
- // Rate limiting: max 5 events per second
150
- const rateLimited = events.pipe(
151
- filter(
152
- {
153
- timestamps: [] as number[],
154
- maxPerSecond: 5,
155
- },
156
- (state, event) => {
157
- const now = event.timestamp;
158
- const recent = state.timestamps.filter((t) => now - t < 1000);
159
-
160
- if (recent.length >= state.maxPerSecond) {
161
- return [false, { ...state, timestamps: recent }];
162
- }
163
-
164
- return [
165
- true,
166
- {
167
- ...state,
168
- timestamps: [...recent, now],
169
- },
170
- ];
171
- }
172
- )
173
- );
174
- ```
175
-
176
- ## Stream Termination
177
-
178
- Filters can terminate streams by returning `undefined`:
179
-
180
- ```typescript
181
- const stream = new Stream<string>();
182
-
183
- const untilStop = stream.pipe(
184
- filter((value) => {
185
- if (value === "STOP") return; // Terminates stream
186
- return value.length > 0;
187
- })
188
- );
189
-
190
- stream.push("hello", "world", "STOP", "ignored");
191
- // Only "hello" and "world" are emitted
192
- ```
193
-
194
- ### Conditional Termination
195
-
196
- ```typescript
197
- const numbers = new Stream<number>();
198
-
199
- const untilNegative = numbers.pipe(
200
- filter({ sum: 0 }, (state, value) => {
201
- const newSum = state.sum + value;
202
- if (newSum < 0) return; // Terminate when sum goes negative
203
-
204
- return [value > 0, { sum: newSum }];
205
- })
206
- );
207
- ```
208
-
209
- ## Performance Considerations
210
-
211
- ### When to Use Concurrency
212
-
213
- - **Sequential**: Default choice, maintains order, lowest overhead
214
- - **Concurrent-unordered**: Use when order doesn't matter and predicates are expensive
215
- - **Concurrent-ordered**: Use when order matters but predicates are expensive
216
-
217
- ### Memory Management
218
-
219
- Stateful filters maintain state objects. For large datasets:
220
-
221
- ```typescript
222
- // Good: Bounded state
223
- filter({ count: 0, limit: 1000 }, (state, value) => {
224
- if (state.count >= state.limit) return;
225
- return [predicate(value), { ...state, count: state.count + 1 }];
226
- });
227
-
228
- // Avoid: Unbounded state growth
229
- filter({ history: [] }, (state, value) => {
230
- // This grows indefinitely!
231
- return [true, { history: [...state.history, value] }];
232
- });
233
- ```
234
-
235
- ## Error Handling
236
-
237
- Errors in predicates will propagate and potentially terminate the stream:
238
-
239
- ```typescript
240
- const stream = new Stream<number>();
241
-
242
- const safe = stream.pipe(
243
- filter((value) => {
244
- try {
245
- return riskyPredicate(value);
246
- } catch (error) {
247
- console.error("Filter error:", error);
248
- return false; // Skip problematic values
249
- }
250
- })
251
- );
252
- ```
253
-
254
- ## Common Patterns
255
-
256
- ### Throttling
257
-
258
- ```typescript
259
- const throttle = <T>(ms: number) =>
260
- filter<T, { lastEmit: number }>({ lastEmit: 0 }, (state, value) => {
261
- const now = Date.now();
262
- if (now - state.lastEmit < ms) {
263
- return [false, state];
264
- }
265
- return [true, { lastEmit: now }];
266
- });
267
-
268
- stream.pipe(throttle(1000)); // Max one value per second
269
- ```
270
-
271
- ### Sampling
272
-
273
- ```typescript
274
- const sample = <T>(n: number) =>
275
- filter<T, { count: number }>({ count: 0 }, (state, value) => {
276
- const shouldEmit = (state.count + 1) % n === 0;
277
- return [shouldEmit, { count: state.count + 1 }];
278
- });
279
-
280
- stream.pipe(sample(3)); // Every 3rd value
281
- ```
282
-
283
- ### Windowing
284
-
285
- ```typescript
286
- const slidingWindow = <T>(size: number) =>
287
- filter<T, { window: T[] }>({ window: [] }, (state, value) => {
288
- const newWindow = [...state.window, value].slice(-size);
289
- const shouldEmit = newWindow.length === size;
290
-
291
- return [shouldEmit, { window: newWindow }];
292
- });
293
-
294
- stream.pipe(slidingWindow(5)); // Emit when window is full
295
- ```
296
-
297
- ## Type Signatures
298
-
299
- ```typescript
300
- // Simple predicate with optional concurrency
301
- filter<VALUE>(
302
- predicate: (value: VALUE) => boolean | void | Promise<boolean | void>,
303
- options?: { strategy: "sequential" | "concurrent-unordered" | "concurrent-ordered" }
304
- ): (stream: Stream<VALUE>) => Stream<VALUE>
305
-
306
- // Type guard predicate (synchronous only)
307
- filter<VALUE, FILTERED extends VALUE>(
308
- predicate: (value: VALUE) => value is FILTERED
309
- ): (stream: Stream<VALUE>) => Stream<FILTERED>
310
-
311
- // Stateful predicate (always sequential)
312
- filter<VALUE, STATE>(
313
- initialState: STATE,
314
- predicate: (state: STATE, value: VALUE) => [boolean, STATE] | void
315
- ): (stream: Stream<VALUE>) => Stream<VALUE>
316
- ```
317
-
318
- ## Best Practices
319
-
320
- 1. **Choose the right strategy**: Use sequential for simple predicates, concurrent for expensive async operations
321
- 2. **Manage state size**: Keep stateful filter state bounded to prevent memory leaks
322
- 3. **Handle errors gracefully**: Wrap risky predicates in try-catch blocks
323
- 4. **Use type guards**: Leverage TypeScript's type narrowing for better type safety
324
- 5. **Consider termination**: Use `return undefined` to cleanly terminate streams when conditions are met
325
-
326
- The filter transformer is a powerful tool for stream processing that scales from simple synchronous predicates to complex stateful async operations with optimal performance characteristics.
@@ -1,56 +0,0 @@
1
- # Flat Transformer
2
-
3
- ## Event Multiplication
4
-
5
- The `flat` transformer converts array events into individual events - essentially `Array.prototype.flat()` for streams.
6
-
7
- **Core Concept**: 1 array event → N individual events
8
-
9
- ## Usage
10
-
11
- ```typescript
12
- stream.pipe(flat(depth?));
13
- ```
14
-
15
- - **Input**: `Stream<T[]>`
16
- - **Output**: `Stream<T>`
17
- - **Transformation**: Each array becomes separate events
18
-
19
- ## Basic Example
20
-
21
- ```typescript
22
- const arrayStream = new Stream<number[]>();
23
- const flattened = arrayStream.pipe(flat());
24
-
25
- arrayStream.push([1, 2, 3]);
26
- // Emits: 1, 2, 3 as separate events
27
-
28
- flattened.listen((value) => console.log(value));
29
- // Logs: 1, 2, 3
30
- ```
31
-
32
- ## Depth Control
33
-
34
- ```typescript
35
- const nested = new Stream<number[][]>();
36
- const flattened = nested.pipe(flat(1)); // Flatten 2 levels
37
-
38
- nested.push([
39
- [1, 2],
40
- [3, 4],
41
- ]);
42
- // Emits: 1, 2, 3, 4 as separate events
43
- ```
44
-
45
- ## Common Pattern
46
-
47
- ```typescript
48
- // Map then flatten
49
- const sentences = new Stream<string>();
50
- const characters = sentences.pipe(map({}, (_, s) => [s.split(""), {}])).pipe(flat());
51
-
52
- sentences.push("hello");
53
- // Emits: 'h', 'e', 'l', 'l', 'o' as separate events
54
- ```
55
-
56
- That's it. Simple event multiplication for arrays.