@soffinal/stream 0.2.3 → 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.
- package/README.md +137 -581
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +10 -12
- package/dist/stream.d.ts +66 -6
- package/dist/stream.d.ts.map +1 -1
- package/dist/transformers/filter/filter.d.ts +18 -0
- package/dist/transformers/filter/filter.d.ts.map +1 -0
- package/dist/transformers/filter/index.d.ts +2 -0
- package/dist/transformers/filter/index.d.ts.map +1 -0
- package/dist/transformers/flat/flat.d.ts +3 -0
- package/dist/transformers/flat/flat.d.ts.map +1 -0
- package/dist/transformers/flat/index.d.ts +2 -0
- package/dist/transformers/flat/index.d.ts.map +1 -0
- package/dist/transformers/gate/gate.d.ts +43 -0
- package/dist/transformers/gate/gate.d.ts.map +1 -0
- package/dist/transformers/gate/index.d.ts +2 -0
- package/dist/transformers/gate/index.d.ts.map +1 -0
- package/dist/transformers/index.d.ts +6 -4
- package/dist/transformers/index.d.ts.map +1 -1
- package/dist/transformers/map/index.d.ts +2 -0
- package/dist/transformers/map/index.d.ts.map +1 -0
- package/dist/transformers/map/map.d.ts +14 -0
- package/dist/transformers/map/map.d.ts.map +1 -0
- package/dist/transformers/merge/index.d.ts +2 -0
- package/dist/transformers/merge/index.d.ts.map +1 -0
- package/dist/transformers/merge/merge.d.ts +3 -0
- package/dist/transformers/merge/merge.d.ts.map +1 -0
- package/dist/transformers/state/index.d.ts +2 -0
- package/dist/transformers/state/index.d.ts.map +1 -0
- package/dist/transformers/state/state.d.ts +35 -0
- package/dist/transformers/state/state.d.ts.map +1 -0
- package/package.json +15 -17
- package/dist/reactive/index.d.ts +0 -5
- package/dist/reactive/index.d.ts.map +0 -1
- package/dist/reactive/list.d.ts +0 -171
- package/dist/reactive/list.d.ts.map +0 -1
- package/dist/reactive/map.d.ts +0 -107
- package/dist/reactive/map.d.ts.map +0 -1
- package/dist/reactive/set.d.ts +0 -102
- package/dist/reactive/set.d.ts.map +0 -1
- package/dist/reactive/state.d.ts +0 -79
- package/dist/reactive/state.d.ts.map +0 -1
- package/dist/transformers/filter.d.ts +0 -96
- package/dist/transformers/filter.d.ts.map +0 -1
- package/dist/transformers/flat.d.ts +0 -31
- package/dist/transformers/flat.d.ts.map +0 -1
- package/dist/transformers/map.d.ts +0 -106
- package/dist/transformers/map.d.ts.map +0 -1
- package/dist/transformers/merge.d.ts +0 -33
- package/dist/transformers/merge.d.ts.map +0 -1
- package/src/transformers/filter.md +0 -326
- package/src/transformers/flat.md +0 -56
- package/src/transformers/map.md +0 -430
- 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.
|
package/src/transformers/flat.md
DELETED
|
@@ -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.
|