@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.
Files changed (56) hide show
  1. package/README.md +137 -581
  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 +66 -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,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
- ```