@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
package/src/transformers/map.md
DELETED
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
# Map Transformer
|
|
2
|
-
|
|
3
|
-
The `map` transformer transforms values flowing through a stream. It supports synchronous and asynchronous transformations, stateful operations, type conversions, and multiple concurrency strategies for optimal performance.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { Stream, map } from "@soffinal/stream";
|
|
9
|
-
|
|
10
|
-
const numbers = new Stream<number>();
|
|
11
|
-
|
|
12
|
-
// Simple transformation
|
|
13
|
-
const doubled = numbers.pipe(map((n) => n * 2));
|
|
14
|
-
|
|
15
|
-
doubled.listen(console.log);
|
|
16
|
-
numbers.push(1, 2, 3); // Outputs: 2, 4, 6
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Basic Usage
|
|
20
|
-
|
|
21
|
-
### Synchronous Transformations
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
// Number to string
|
|
25
|
-
stream.pipe(map((n) => n.toString()));
|
|
26
|
-
|
|
27
|
-
// Object property extraction
|
|
28
|
-
stream.pipe(map((user) => user.name));
|
|
29
|
-
|
|
30
|
-
// Complex transformations
|
|
31
|
-
stream.pipe(
|
|
32
|
-
map((data) => ({
|
|
33
|
-
id: data.id,
|
|
34
|
-
name: data.name.toUpperCase(),
|
|
35
|
-
timestamp: Date.now(),
|
|
36
|
-
}))
|
|
37
|
-
);
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Type Transformations
|
|
41
|
-
|
|
42
|
-
Map excels at converting between types with full TypeScript inference:
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
const numbers = new Stream<number>();
|
|
46
|
-
|
|
47
|
-
// number → string
|
|
48
|
-
const strings = numbers.pipe(map((n) => n.toString()));
|
|
49
|
-
|
|
50
|
-
// number → object
|
|
51
|
-
const objects = numbers.pipe(
|
|
52
|
-
map((n) => ({
|
|
53
|
-
value: n,
|
|
54
|
-
squared: n * n,
|
|
55
|
-
isEven: n % 2 === 0,
|
|
56
|
-
}))
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
// Chained transformations
|
|
60
|
-
const result = numbers
|
|
61
|
-
.pipe(map((n) => n * 2)) // number → number
|
|
62
|
-
.pipe(map((n) => n.toString())) // number → string
|
|
63
|
-
.pipe(map((s) => s.length)); // string → number
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Asynchronous Transformations
|
|
67
|
-
|
|
68
|
-
### Sequential Processing (Default)
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
const urls = new Stream<string>();
|
|
72
|
-
|
|
73
|
-
const responses = urls.pipe(
|
|
74
|
-
map(async (url) => {
|
|
75
|
-
const response = await fetch(url);
|
|
76
|
-
return await response.json();
|
|
77
|
-
})
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
// Requests processed one at a time, maintaining order
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
### Concurrent Strategies
|
|
84
|
-
|
|
85
|
-
For expensive async operations, choose a concurrency strategy:
|
|
86
|
-
|
|
87
|
-
#### Concurrent Unordered
|
|
88
|
-
|
|
89
|
-
Transformations run in parallel, results emit as they complete:
|
|
90
|
-
|
|
91
|
-
```typescript
|
|
92
|
-
const ids = new Stream<string>();
|
|
93
|
-
|
|
94
|
-
const users = ids.pipe(
|
|
95
|
-
map(
|
|
96
|
-
async (id) => {
|
|
97
|
-
const user = await fetchUser(id);
|
|
98
|
-
return user;
|
|
99
|
-
},
|
|
100
|
-
{ strategy: "concurrent-unordered" }
|
|
101
|
-
)
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
// Users emit as API calls complete, potentially out of order
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
#### Concurrent Ordered
|
|
108
|
-
|
|
109
|
-
Parallel processing but maintains original order:
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
const images = new Stream<string>();
|
|
113
|
-
|
|
114
|
-
const processed = images.pipe(
|
|
115
|
-
map(
|
|
116
|
-
async (imageUrl) => {
|
|
117
|
-
const processed = await processImage(imageUrl);
|
|
118
|
-
return processed;
|
|
119
|
-
},
|
|
120
|
-
{ strategy: "concurrent-ordered" }
|
|
121
|
-
)
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Images always emit in original order despite varying processing times
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Stateful Transformations
|
|
128
|
-
|
|
129
|
-
Maintain state across transformations for complex operations:
|
|
130
|
-
|
|
131
|
-
### Basic Stateful Mapping
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
const numbers = new Stream<number>();
|
|
135
|
-
|
|
136
|
-
// Running sum
|
|
137
|
-
const runningSums = numbers.pipe(
|
|
138
|
-
map({ sum: 0 }, (state, value) => {
|
|
139
|
-
const newSum = state.sum + value;
|
|
140
|
-
return [newSum, { sum: newSum }];
|
|
141
|
-
})
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
numbers.push(1, 2, 3, 4);
|
|
145
|
-
// Outputs: 1, 3, 6, 10
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Indexing and Counting
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
const items = new Stream<string>();
|
|
152
|
-
|
|
153
|
-
// Add indices
|
|
154
|
-
const indexed = items.pipe(
|
|
155
|
-
map({ index: 0 }, (state, value) => {
|
|
156
|
-
const result = { item: value, index: state.index };
|
|
157
|
-
return [result, { index: state.index + 1 }];
|
|
158
|
-
})
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
items.push("a", "b", "c");
|
|
162
|
-
// Outputs: {item: "a", index: 0}, {item: "b", index: 1}, {item: "c", index: 2}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### Complex State Management
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
const events = new Stream<{ type: string; data: any }>();
|
|
169
|
-
|
|
170
|
-
// Event aggregation with history
|
|
171
|
-
const aggregated = events.pipe(
|
|
172
|
-
map(
|
|
173
|
-
{
|
|
174
|
-
counts: new Map<string, number>(),
|
|
175
|
-
history: [] as string[],
|
|
176
|
-
total: 0,
|
|
177
|
-
},
|
|
178
|
-
(state, event) => {
|
|
179
|
-
const newCounts = new Map(state.counts);
|
|
180
|
-
const currentCount = newCounts.get(event.type) || 0;
|
|
181
|
-
newCounts.set(event.type, currentCount + 1);
|
|
182
|
-
|
|
183
|
-
const newHistory = [...state.history, event.type].slice(-10); // Keep last 10
|
|
184
|
-
const newTotal = state.total + 1;
|
|
185
|
-
|
|
186
|
-
const result = {
|
|
187
|
-
event: event.type,
|
|
188
|
-
count: currentCount + 1,
|
|
189
|
-
totalEvents: newTotal,
|
|
190
|
-
recentHistory: newHistory,
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
return [
|
|
194
|
-
result,
|
|
195
|
-
{
|
|
196
|
-
counts: newCounts,
|
|
197
|
-
history: newHistory,
|
|
198
|
-
total: newTotal,
|
|
199
|
-
},
|
|
200
|
-
];
|
|
201
|
-
}
|
|
202
|
-
)
|
|
203
|
-
);
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Async Stateful Transformations
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
const requests = new Stream<string>();
|
|
210
|
-
|
|
211
|
-
// Caching with async operations
|
|
212
|
-
const cached = requests.pipe(
|
|
213
|
-
map(
|
|
214
|
-
{
|
|
215
|
-
cache: new Map<string, any>(),
|
|
216
|
-
},
|
|
217
|
-
async (state, url) => {
|
|
218
|
-
// Check cache first
|
|
219
|
-
if (state.cache.has(url)) {
|
|
220
|
-
return [state.cache.get(url), state];
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Fetch and cache
|
|
224
|
-
const data = await fetch(url).then((r) => r.json());
|
|
225
|
-
const newCache = new Map(state.cache);
|
|
226
|
-
newCache.set(url, data);
|
|
227
|
-
|
|
228
|
-
return [data, { cache: newCache }];
|
|
229
|
-
}
|
|
230
|
-
)
|
|
231
|
-
);
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
## Performance Considerations
|
|
235
|
-
|
|
236
|
-
### When to Use Concurrency
|
|
237
|
-
|
|
238
|
-
- **Sequential**: Default choice, maintains order, lowest overhead
|
|
239
|
-
- **Concurrent-unordered**: Use when order doesn't matter and transformations are expensive
|
|
240
|
-
- **Concurrent-ordered**: Use when order matters but transformations are expensive
|
|
241
|
-
|
|
242
|
-
### Benchmarking Example
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
const urls = Array.from({ length: 100 }, (_, i) => `https://api.example.com/item/${i}`);
|
|
246
|
-
const stream = new Stream<string>();
|
|
247
|
-
|
|
248
|
-
// Sequential: ~10 seconds (100ms per request)
|
|
249
|
-
const sequential = stream.pipe(map(async (url) => await fetch(url)));
|
|
250
|
-
|
|
251
|
-
// Concurrent-unordered: ~1 second (parallel requests)
|
|
252
|
-
const concurrent = stream.pipe(
|
|
253
|
-
map(async (url) => await fetch(url), {
|
|
254
|
-
strategy: "concurrent-unordered",
|
|
255
|
-
})
|
|
256
|
-
);
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Memory Management
|
|
260
|
-
|
|
261
|
-
For stateful transformations, manage memory carefully:
|
|
262
|
-
|
|
263
|
-
```typescript
|
|
264
|
-
// Good: Bounded state
|
|
265
|
-
map(
|
|
266
|
-
{
|
|
267
|
-
recentItems: [] as T[],
|
|
268
|
-
maxSize: 100,
|
|
269
|
-
},
|
|
270
|
-
(state, value) => {
|
|
271
|
-
const newItems = [...state.recentItems, value].slice(-state.maxSize);
|
|
272
|
-
return [
|
|
273
|
-
processItems(newItems),
|
|
274
|
-
{
|
|
275
|
-
recentItems: newItems,
|
|
276
|
-
maxSize: state.maxSize,
|
|
277
|
-
},
|
|
278
|
-
];
|
|
279
|
-
}
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
// Avoid: Unbounded growth
|
|
283
|
-
map({ history: [] }, (state, value) => {
|
|
284
|
-
// This grows indefinitely!
|
|
285
|
-
return [value, { history: [...state.history, value] }];
|
|
286
|
-
});
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
## Error Handling
|
|
290
|
-
|
|
291
|
-
Handle transformation errors gracefully:
|
|
292
|
-
|
|
293
|
-
```typescript
|
|
294
|
-
const stream = new Stream<string>();
|
|
295
|
-
|
|
296
|
-
const safe = stream.pipe(
|
|
297
|
-
map(async (data) => {
|
|
298
|
-
try {
|
|
299
|
-
return await riskyTransformation(data);
|
|
300
|
-
} catch (error) {
|
|
301
|
-
console.error("Transformation failed:", error);
|
|
302
|
-
return { error: error.message, original: data };
|
|
303
|
-
}
|
|
304
|
-
})
|
|
305
|
-
);
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
## Common Patterns
|
|
309
|
-
|
|
310
|
-
### Enrichment
|
|
311
|
-
|
|
312
|
-
```typescript
|
|
313
|
-
const enrich = <T>(enrichFn: (item: T) => Promise<any>) =>
|
|
314
|
-
map(async (item: T) => {
|
|
315
|
-
const enrichment = await enrichFn(item);
|
|
316
|
-
return { ...item, ...enrichment };
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
users.pipe(
|
|
320
|
-
enrich(async (user) => ({
|
|
321
|
-
avatar: await getAvatar(user.id),
|
|
322
|
-
permissions: await getPermissions(user.role),
|
|
323
|
-
}))
|
|
324
|
-
);
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
### Batching
|
|
328
|
-
|
|
329
|
-
```typescript
|
|
330
|
-
const batch = <T>(size: number) =>
|
|
331
|
-
map<T, { buffer: T[] }, T[]>({ buffer: [] }, (state, value) => {
|
|
332
|
-
const newBuffer = [...state.buffer, value];
|
|
333
|
-
|
|
334
|
-
if (newBuffer.length >= size) {
|
|
335
|
-
return [newBuffer, { buffer: [] }];
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return [null, { buffer: newBuffer }];
|
|
339
|
-
}).pipe(filter((batch) => batch !== null));
|
|
340
|
-
|
|
341
|
-
stream.pipe(batch(5)); // Emit arrays of 5 items
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### Debouncing with State
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
const debounce = <T>(ms: number) =>
|
|
348
|
-
map<T, { lastValue: T | null; timer: any }, T | null>(
|
|
349
|
-
{
|
|
350
|
-
lastValue: null,
|
|
351
|
-
timer: null,
|
|
352
|
-
},
|
|
353
|
-
(state, value) => {
|
|
354
|
-
if (state.timer) clearTimeout(state.timer);
|
|
355
|
-
|
|
356
|
-
const timer = setTimeout(() => {
|
|
357
|
-
// This would need additional mechanism to emit
|
|
358
|
-
}, ms);
|
|
359
|
-
|
|
360
|
-
return [null, { lastValue: value, timer }];
|
|
361
|
-
}
|
|
362
|
-
);
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
### Windowing
|
|
366
|
-
|
|
367
|
-
```typescript
|
|
368
|
-
const slidingWindow = <T>(size: number) =>
|
|
369
|
-
map<T, { window: T[] }, T[]>({ window: [] }, (state, value) => {
|
|
370
|
-
const newWindow = [...state.window, value].slice(-size);
|
|
371
|
-
return [newWindow, { window: newWindow }];
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
stream.pipe(slidingWindow(3)); // Always emit last 3 items
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
## Advanced Patterns
|
|
378
|
-
|
|
379
|
-
### Conditional Transformation
|
|
380
|
-
|
|
381
|
-
```typescript
|
|
382
|
-
const conditionalMap = <T, U>(condition: (value: T) => boolean, transform: (value: T) => U) =>
|
|
383
|
-
map((value: T) => (condition(value) ? transform(value) : value));
|
|
384
|
-
|
|
385
|
-
stream.pipe(
|
|
386
|
-
conditionalMap(
|
|
387
|
-
(n) => n > 10,
|
|
388
|
-
(n) => n * 2
|
|
389
|
-
)
|
|
390
|
-
);
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
### Multi-step Processing
|
|
394
|
-
|
|
395
|
-
```typescript
|
|
396
|
-
const pipeline = <T>(steps: Array<(value: any) => any>) =>
|
|
397
|
-
map((value: T) => steps.reduce((acc, step) => step(acc), value));
|
|
398
|
-
|
|
399
|
-
stream.pipe(
|
|
400
|
-
pipeline([(x) => x.toString(), (x) => x.toUpperCase(), (x) => x.split(""), (x) => x.reverse(), (x) => x.join("")])
|
|
401
|
-
);
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
## Type Signatures
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
// Simple mapper with optional concurrency
|
|
408
|
-
map<VALUE, MAPPED>(
|
|
409
|
-
mapper: (value: VALUE) => MAPPED | Promise<MAPPED>,
|
|
410
|
-
options?: { strategy: "sequential" | "concurrent-unordered" | "concurrent-ordered" }
|
|
411
|
-
): (stream: Stream<VALUE>) => Stream<MAPPED>
|
|
412
|
-
|
|
413
|
-
// Stateful mapper (always sequential)
|
|
414
|
-
map<VALUE, STATE, MAPPED>(
|
|
415
|
-
initialState: STATE,
|
|
416
|
-
mapper: (state: STATE, value: VALUE) => [MAPPED, STATE] | Promise<[MAPPED, STATE]>
|
|
417
|
-
): (stream: Stream<VALUE>) => Stream<MAPPED>
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
## Best Practices
|
|
421
|
-
|
|
422
|
-
1. **Choose the right strategy**: Use sequential for simple transformations, concurrent for expensive async operations
|
|
423
|
-
2. **Manage state size**: Keep stateful transformation state bounded to prevent memory leaks
|
|
424
|
-
3. **Handle errors gracefully**: Wrap risky transformations in try-catch blocks
|
|
425
|
-
4. **Leverage TypeScript**: Use proper typing for better development experience and runtime safety
|
|
426
|
-
5. **Consider performance**: Profile your transformations to choose optimal concurrency strategies
|
|
427
|
-
6. **Compose transformations**: Chain multiple simple maps rather than one complex transformation
|
|
428
|
-
7. **Use immutable updates**: Always return new state objects in stateful transformations
|
|
429
|
-
|
|
430
|
-
The map transformer is the workhorse of stream processing, enabling powerful data transformations that scale from simple synchronous operations to complex stateful async processing with optimal performance characteristics.
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# Merge Transformer
|
|
2
|
-
|
|
3
|
-
## Temporal Orchestration
|
|
4
|
-
|
|
5
|
-
The `merge` transformer combines multiple streams into one unified flow while preserving temporal order - like conducting multiple instruments into a single symphony.
|
|
6
|
-
|
|
7
|
-
**Core Concept**: Multiple streams → One unified stream with union types
|
|
8
|
-
|
|
9
|
-
## Usage
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
stream.pipe(merge(...otherStreams));
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
- **Input**: `Stream<T>` + `Stream<U>[]`
|
|
16
|
-
- **Output**: `Stream<T | U>`
|
|
17
|
-
- **Order**: Maintains chronological sequence across all streams
|
|
18
|
-
|
|
19
|
-
## Basic Example
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
const numbers = new Stream<number>();
|
|
23
|
-
const strings = new Stream<string>();
|
|
24
|
-
|
|
25
|
-
const combined = numbers.pipe(merge(strings));
|
|
26
|
-
// Type: Stream<number | string>
|
|
27
|
-
|
|
28
|
-
combined.listen((value) => {
|
|
29
|
-
if (typeof value === "number") {
|
|
30
|
-
console.log("Number:", value);
|
|
31
|
-
} else {
|
|
32
|
-
console.log("String:", value);
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
numbers.push(1, 2);
|
|
37
|
-
strings.push("a", "b");
|
|
38
|
-
// Output: 1, 2, "a", "b" (in temporal order)
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Multiple Streams
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
const events = new Stream<Event>();
|
|
45
|
-
const errors = new Stream<Error>();
|
|
46
|
-
const logs = new Stream<LogEntry>();
|
|
47
|
-
|
|
48
|
-
const allActivity = events.pipe(merge(errors, logs));
|
|
49
|
-
// Type: Stream<Event | Error | LogEntry>
|
|
50
|
-
|
|
51
|
-
// All streams flow into one unified timeline
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Type Safety
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
interface UserEvent {
|
|
58
|
-
type: "user";
|
|
59
|
-
data: any;
|
|
60
|
-
}
|
|
61
|
-
interface SystemEvent {
|
|
62
|
-
type: "system";
|
|
63
|
-
data: any;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const userEvents = new Stream<UserEvent>();
|
|
67
|
-
const systemEvents = new Stream<SystemEvent>();
|
|
68
|
-
|
|
69
|
-
const allEvents = userEvents.pipe(merge(systemEvents));
|
|
70
|
-
|
|
71
|
-
allEvents.listen((event) => {
|
|
72
|
-
switch (event.type) {
|
|
73
|
-
case "user" /* handle user event */:
|
|
74
|
-
break;
|
|
75
|
-
case "system" /* handle system event */:
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
```
|