@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
package/README.md CHANGED
@@ -3,44 +3,10 @@
3
3
  [![npm version](https://badge.fury.io/js/@soffinal%2Fstream.svg)](https://badge.fury.io/js/@soffinal%2Fstream)
4
4
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
- [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@soffinal/stream)](https://bundlephobia.com/package/@soffinal/stream)
7
-
8
- > **Multicast Event Pipelines with functional composition**
9
-
10
- Stream provides multicast event pipelines with functional composition capabilities. It supports both push-based events (`stream.push()`) and pull-based data sources (async generators for WebSockets, file readers, EventSource, etc.). Features include async operations, stateful transformations, and built-in concurrency control with complete TypeScript integration.
11
-
12
- ## Table of Contents
13
-
14
- - [Features](#features)
15
- - [Quick Start](#quick-start)
16
- - [Installation](#installation)
17
- - [Core Concepts](#core-concepts)
18
- - [API Reference](#api-reference)
19
- - [Examples](#examples)
20
- - [Performance](#performance)
21
- - [Migration Guide](#migration-guide)
22
- - [Documentation](#documentation)
23
- - [Contributing](#contributing)
24
- - [License](#license)
25
-
26
- ## Features
27
-
28
- - **Adaptive Constraints** - Transformers that learn and evolve based on stream history
29
- - **Universal Primitives** - Four primitives: `filter`, `map`, `merge`, `flat`
30
- - **Documentation-as-Distribution** - Copy-paste transformers embedded in JSDoc, no separate packages needed
31
- - **Async-First** - Native async/await support with configurable concurrency control
32
- - **Concurrency Strategies** - Sequential, concurrent-unordered, concurrent-ordered processing
33
- - **Multicast Streams** - One stream, unlimited consumers
34
- - **Awaitable** - `await stream` for next value
35
- - **Async Iterable** - Native `for await` loop support
36
- - **Pipe Composition** - Stream-to-stream functional composition
37
- - **Type Guards** - Built-in TypeScript type narrowing support
38
- - **Reactive State** - Stateful values with automatic change propagation
39
- - **Reactive Collections** - Lists, Maps, Sets with fine-grained events
40
- - **Stream Termination** - Declarative stream lifecycle control
41
- - **Zero Dependencies** - Lightweight and tree-shakeable
42
- - **Universal** - Node.js, browsers, Deno, Bun, Cloudflare Workers
43
- - **Full TypeScript** - Complete type safety without the burden
6
+
7
+ > **Composable reactive primitives for TypeScript**
8
+
9
+ Minimal reactive streaming library: type-safe events, stateful transformations, async iteration, automatic cleanup.
44
10
 
45
11
  ## Quick Start
46
12
 
@@ -49,647 +15,237 @@ import { Stream } from "@soffinal/stream";
49
15
 
50
16
  const events = new Stream<string>();
51
17
 
52
- events.listen(console.log);
18
+ events.listen((value) => console.log(value));
19
+ events.push("Hello", "World");
20
+ // Output: Hello, World
21
+ ```
22
+
23
+ ## Installation
53
24
 
54
- events.push("Hello"); //log: Hello
25
+ ```bash
26
+ npm install @soffinal/stream
55
27
  ```
56
28
 
57
- ## Examples
29
+ ## Core Concepts
30
+
31
+ ### 1. Stream - Push & Pull
58
32
 
59
33
  ```typescript
60
- import { Stream, State, filter, map, merge } from "@soffinal/stream";
34
+ // Push: Emit values manually
35
+ const stream = new Stream<number>();
36
+ stream.listen((n) => console.log(n));
37
+ stream.push(1, 2, 3);
61
38
 
62
- // Create streams
63
- const events = new Stream<string>();
64
- const numbers = new Stream<number>();
39
+ // Pull: Generate values from async source
40
+ const countdown = new Stream(async function* () {
41
+ for (let i = 5; i > 0; i--) {
42
+ yield i;
43
+ await new Promise(resolve => setTimeout(resolve, 1000));
44
+ }
45
+ });
65
46
 
66
- // Pull-based stream from async generator
67
- const websocketStream = new Stream(async function* () {
68
- const ws = new WebSocket("ws://localhost:8080");
47
+ // Both: Push to a pull-based stream
48
+ const messages = new Stream(async function* () {
49
+ const ws = new WebSocket("ws://localhost");
69
50
  while (ws.readyState === WebSocket.OPEN) {
70
- yield await new Promise((resolve) => {
71
- ws.onmessage = (event) => resolve(JSON.parse(event.data));
51
+ yield await new Promise(resolve => {
52
+ ws.onmessage = e => resolve(e.data);
72
53
  });
73
54
  }
74
55
  });
75
56
 
76
- // Simple transformations
77
- const processed = events
78
- .pipe(filter((msg) => msg.length > 3)) // Simple filtering
79
- .pipe(map((msg) => msg.toUpperCase())); // Transform to uppercase
80
-
81
- // Async transformations with concurrency
82
- const validated = events.pipe(
83
- filter(
84
- async (msg) => {
85
- const isValid = await validateAsync(msg);
86
- return isValid;
87
- },
88
- { strategy: "concurrent-ordered" }
89
- ) // Parallel validation, ordered results
90
- );
91
-
92
- // Stateful transformers that learn and adapt
93
- const runningAverage = numbers
94
- .pipe(
95
- filter({ count: 0 }, (state, value) => {
96
- // Only pass every 3rd number, terminate after 10
97
- if (state.count >= 10) return; // Stream termination
98
- return [(state.count + 1) % 3 === 0, { count: state.count + 1 }];
99
- })
100
- )
101
- .pipe(
102
- map({ sum: 0, count: 0 }, (state, value) => {
103
- const newSum = state.sum + value;
104
- const newCount = state.count + 1;
105
- const average = newSum / newCount;
106
- return [
107
- { value, average },
108
- { sum: newSum, count: newCount },
109
- ];
110
- })
111
- );
112
-
113
- // Copy-paste transformers from JSDoc
114
- const limited = numbers.pipe(take(5)); // Limit to 5 items
115
- const indexed = events.pipe(withIndex()); // Add indices
116
- const delayed = processed.pipe(delay(100)); // Delay each value
117
-
118
- // Multiple consumers
119
- processed.listen((msg) => console.log("Processed:", msg));
120
- validated.listen((msg) => console.log("Validated:", msg));
121
- runningAverage.listen(({ value, average }) => console.log(`Value: ${value}, Running Average: ${average}`));
122
-
123
- // Reactive state
124
- const counter = new State(0);
125
- counter.listen((count) => (document.title = `Count: ${count}`));
126
- counter.value++; // UI updates automatically
57
+ messages.push("manual event"); // Can still push!
127
58
  ```
128
59
 
129
- ## Installation
130
-
131
- ### Package Managers
132
-
133
- ```bash
134
- # npm
135
- npm install @soffinal/stream
60
+ ### 2. Automatic Cleanup
136
61
 
137
- # yarn
138
- yarn add @soffinal/stream
62
+ ```typescript
63
+ // Disposable pattern
64
+ using stream.listen(handler);
139
65
 
140
- # pnpm
141
- pnpm add @soffinal/stream
66
+ // Manual cleanup
67
+ const cleanup = stream.listen(handler);
68
+ cleanup();
142
69
 
143
- # bun
144
- bun add @soffinal/stream
70
+ // AbortSignal
71
+ stream.listen(handler, controller.signal);
145
72
 
146
- # Deno
147
- deno add jsr:@soffinal/stream
73
+ // Stream as signal
74
+ const stop = new Stream<void>();
75
+ stream.listen(handler, stop);
76
+ stop.push(); // Remove listener
148
77
  ```
149
78
 
150
- ### CDN (Browser)
151
-
152
- ```html
153
- <!-- Production (minified) -->
154
- <script type="module">
155
- import { Stream, State } from "https://cdn.jsdelivr.net/npm/@soffinal/stream@latest/dist/index.js";
156
- </script>
79
+ ### 3. Transform with pipe
157
80
 
158
- <!-- Alternative CDNs -->
159
- <script type="module">
160
- import { Stream } from "https://esm.sh/@soffinal/stream";
161
- import { Stream } from "https://cdn.skypack.dev/@soffinal/stream";
162
- </script>
163
- ```
81
+ ```typescript
82
+ const numbers = new Stream<number>();
164
83
 
165
- ## Core Concepts
84
+ const doubled = numbers.pipe(filter((n) => n > 0)).pipe(map((n) => n * 2));
166
85
 
167
- ### Streams: Multicast Event Pipelines
86
+ doubled.listen((n) => console.log(n));
87
+ numbers.push(-1, 2, 3);
88
+ // Output: 4, 6
89
+ ```
168
90
 
169
- A `Stream` is a multicast, async iterable that pushes values to multiple listeners while being awaitable for the next value.
91
+ ### 4. Async Iteration
170
92
 
171
93
  ```typescript
172
- const userEvents = new Stream<UserEvent>();
173
-
174
- // Multiple consumers automatically share the same data
175
- userEvents.listen((event) => analytics.track(event));
176
- userEvents.listen((event) => notifications.send(event));
177
- userEvents.listen((event) => database.save(event));
178
-
179
- // Await the next event
180
- const nextEvent = await userEvents;
181
-
182
- // Async iteration
183
- for await (const event of userEvents) {
184
- if (event.type === "critical") break;
185
- processEvent(event);
94
+ for await (const value of stream) {
95
+ console.log(value);
96
+ if (done) break;
186
97
  }
187
98
  ```
188
99
 
189
- ### Pipe: Stream-to-Stream Composition
100
+ ## Transformers
190
101
 
191
- The `pipe` method enforces composition - it only accepts functions that return Stream instances, maintaining the infinite pipeline:
102
+ ### state - Reactive State
192
103
 
193
104
  ```typescript
194
- // All transformers return Streams - infinite chaining
195
- stream.pipe(filter((v) => v > 0)); // → Stream<T>
196
- stream.pipe(map((v) => v.toString())); // → Stream<string>
197
- stream.pipe(toState("initial")); // State<string> (extends Stream)
198
-
199
- // Infinite chaining - every pipe returns a Stream
200
- const result = stream
201
- .pipe(filter((v) => v > 0))
202
- .pipe(map((v) => v * 2))
203
- .pipe(take(5))
204
- .pipe(delay(100))
205
- .pipe(distinct()); // Always chainable
105
+ const counter = new Stream<number>().pipe(state(0));
106
+
107
+ counter.listen((n) => console.log(n));
108
+ counter.state.value = 5; // Triggers listener
109
+ console.log(counter.state.value); // 5
206
110
  ```
207
111
 
208
- **Streams are infinite** - Like event emitters, they don't terminate naturally. The `pipe` constraint ensures you maintain the reactive paradigm throughout your entire pipeline.
112
+ **[📖 Full Documentation →](src/transformers/state/state.md)**
209
113
 
210
- **Perfect TypeScript inference** - no annotations needed:
114
+ ### gate - Flow Control
211
115
 
212
116
  ```typescript
213
- const numbers = new Stream<number>();
117
+ const stream = new Stream<number>().pipe(gate());
214
118
 
215
- // TypeScript knows these are all Streams
216
- const doubled = numbers.pipe(map((n) => n * 2)); // Stream<number>
217
- const strings = numbers.pipe(map((n) => n.toString())); // Stream<string>
218
- const state = numbers.pipe(toState(0)); // State<number>
119
+ stream.listen((n) => console.log(n));
120
+ stream.push(1); // Logs: 1
121
+ stream.gate.close();
122
+ stream.push(2); // Blocked
123
+ stream.gate.open();
124
+ stream.push(3); // Logs: 3
219
125
  ```
220
126
 
221
- ### Universal Primitives: The Four Algebraic Operations
127
+ **[📖 Full Documentation →](src/transformers/gate/gate.md)**
222
128
 
223
- All stream operations are built from four universal primitives with **Adaptive Constraints**:
224
-
225
- #### 1. Filter
129
+ ### filter - Remove Values
226
130
 
227
131
  ```typescript
228
- import { filter } from "@soffinal/stream";
229
-
230
- // Simple filtering
231
- stream.pipe(filter((value) => value > 0));
132
+ // Simple
133
+ stream.pipe(filter((n) => n > 0));
232
134
 
233
- // Type guard filtering
234
- stream.pipe(filter((value): value is number => typeof value === "number"));
135
+ // Async
136
+ stream.pipe(filter(async (n) => await validate(n)));
235
137
 
236
- // Async filtering with concurrency strategies
138
+ // Stateful
237
139
  stream.pipe(
238
- filter(
239
- async (value) => {
240
- const isValid = await validateAsync(value);
241
- return isValid;
242
- },
243
- { strategy: "concurrent-ordered" }
244
- ) // Parallel validation, ordered results
245
- );
246
-
247
- // Stateful filtering with termination
248
- stream.pipe(
249
- filter({ count: 0 }, (state, value) => {
250
- if (state.count >= 10) return; // Terminate after 10 items
251
- return [value > 0, { count: state.count + 1 }];
252
- })
140
+ filter({ count: 0 }, (state, n) => {
141
+ if (state.count >= 10) return; // Stop after 10
142
+ return [n > 0, { count: state.count + 1 }];
143
+ }),
253
144
  );
254
145
  ```
255
146
 
256
- **[📖 Complete Filter Documentation →](src/transformers/filter.md)**
147
+ **[📖 Full Documentation →](src/transformers/filter/filter.md)**
257
148
 
258
- #### 2. Map - Adaptive Transformer
149
+ ### map - Transform Values
259
150
 
260
151
  ```typescript
261
- import { map } from "@soffinal/stream";
262
-
263
- // Simple transformation
264
- stream.pipe(map((value) => value * 2));
152
+ // Simple
153
+ stream.pipe(map((n) => n * 2));
265
154
 
266
- // Type transformation
267
- stream.pipe(map((value: number) => value.toString()));
155
+ // Async
156
+ stream.pipe(map(async (n) => await process(n)));
268
157
 
269
- // Async transformation with concurrency strategies
158
+ // Stateful
270
159
  stream.pipe(
271
- map(
272
- async (value) => {
273
- const enriched = await enrichWithAPI(value);
274
- return enriched;
275
- },
276
- { strategy: "concurrent-unordered" }
277
- ) // Parallel processing, results as completed
278
- );
279
-
280
- // Stateful transformation with context
281
- stream.pipe(
282
- map({ sum: 0 }, (state, value) => {
283
- const newSum = state.sum + value;
284
- return [{ value, runningSum: newSum }, { sum: newSum }];
285
- })
160
+ map({ sum: 0 }, (state, n) => {
161
+ const newSum = state.sum + n;
162
+ return [newSum, { sum: newSum }];
163
+ }),
286
164
  );
287
165
  ```
288
166
 
289
- **[📖 Complete Map Documentation →](src/transformers/map.md)**
167
+ **[📖 Full Documentation →](src/transformers/map/map.md)**
290
168
 
291
- #### 3. Merge - Stream Orchestration
169
+ ### merge - Combine Streams
292
170
 
293
171
  ```typescript
294
- import { merge } from "@soffinal/stream";
295
-
296
- const stream1 = new Stream<number>();
297
- const stream2 = new Stream<string>();
172
+ const numbers = new Stream<number>();
173
+ const strings = new Stream<string>();
298
174
 
299
- // Combine multiple streams with type safety
300
- const combined = stream1.pipe(merge(stream2));
175
+ const combined = numbers.pipe(merge(strings));
301
176
  // Type: Stream<number | string>
302
-
303
- combined.listen((value) => {
304
- if (typeof value === "number") {
305
- console.log("Number:", value);
306
- } else {
307
- console.log("String:", value);
308
- }
309
- });
310
177
  ```
311
178
 
312
- **[📖 Complete Merge Documentation →](src/transformers/merge.md)**
179
+ **[📖 Full Documentation →](src/transformers/merge/merge.md)**
313
180
 
314
- #### 4. Flat - Event Multiplication
181
+ ### flat - Flatten Arrays
315
182
 
316
183
  ```typescript
317
- import { flat } from "@soffinal/stream";
318
-
319
- // Transform 1 array event → N individual events
320
- const arrayStream = new Stream<number[]>();
321
- const individualNumbers = arrayStream.pipe(flat());
322
-
323
- arrayStream.push([1, 2, 3]); // Emits: 1, 2, 3 as separate events
324
- // Type: Stream<number>
184
+ const arrays = new Stream<number[]>();
185
+ const numbers = arrays.pipe(flat());
325
186
 
326
- // Configurable depth flattening
327
- const deepArrays = new Stream<number[][][]>();
328
- const flattened = deepArrays.pipe(flat(2)); // Flatten 2 levels deep
329
- // Type: Stream<number>
187
+ arrays.push([1, 2, 3]); // Emits: 1, 2, 3
330
188
  ```
331
189
 
332
- **[📖 Complete Flat Documentation →](src/transformers/flat.md)**
190
+ **[📖 Full Documentation →](src/transformers/flat/flat.md)**
333
191
 
334
- ### Documentation-as-Distribution: Copy-Paste Transformers
192
+ ## Write Your Own
335
193
 
336
- No separate repos, no CLI tools, no package management - just copy-paste ready transformers embedded in JSDoc!
337
-
338
- #### The Educational Transparency
339
-
340
- Our approach makes **every implementation pattern visible and learnable**:
194
+ A transformer is just a function:
341
195
 
342
196
  ```typescript
343
- // 📦 All transformers are copy-pastable from IntelliSense!
344
- // Hover over 'Stream' to see the complete transformers library
345
-
346
- // Example: Users don't just get functions - they get implementation education
347
- const searchInput = new Stream<string>(); // ← Hover here for full library
348
- const searchResults = searchInput
349
- .pipe(distinct()) // Copy from Stream JSDoc - learn deduplication patterns
350
- .pipe(take(10)) // Copy from Stream JSDoc - learn termination patterns
351
- .pipe(delay(300)) // Copy from Stream JSDoc - learn async transformation
352
- .pipe(simpleMap((query) => searchAPI(query))); // Copy from Stream JSDoc - learn mapping patterns
353
- ```
354
-
355
- #### What Users Actually Learn
356
-
357
- When users hover over any function in JSDoc, they see **complete implementation patterns**:
358
-
359
- ```typescript
360
- // Users see EXACTLY how to build transformers
361
- const take = <T>(n: number) =>
362
- filter<T, { count: number }>({ count: 0 }, (state, value) => {
363
- if (state.count >= n) return; // ← Learn termination patterns
364
- return [true, { count: state.count + 1 }]; // ← Learn state evolution
197
+ const double = (stream: Stream<number>) =>
198
+ new Stream(async function* () {
199
+ for await (const n of stream) yield n * 2;
365
200
  });
366
201
 
367
- const distinct = <T>() =>
368
- filter<T, { seen: Set<T> }>({ seen: new Set() }, (state, value) => {
369
- if (state.seen.has(value)) return [false, state]; // ← Learn deduplication logic
370
- state.seen.add(value); // ← Learn state mutation patterns
371
- return [true, state];
372
- });
202
+ stream.pipe(double);
373
203
  ```
374
204
 
375
- #### From Consumers to Creators
376
-
377
- This transparency empowers users to become **transformer architects**:
378
-
379
- ```typescript
380
- // After learning from JSDoc examples, users create their own:
381
- const withTimestamp = <T>() =>
382
- map<T, {}, { value: T; timestamp: number }>(
383
- {}, // ← Learned: empty state when no memory needed
384
- (_, value) => [
385
- { value, timestamp: Date.now() }, // ← Learned: transformation pattern
386
- {}, // ← Learned: state management
387
- ]
388
- );
389
-
390
- const rateLimited = <T>(maxPerSecond: number) =>
391
- filter<T, { timestamps: number[] }>({ timestamps: [] }, (state, value) => {
392
- const now = Date.now();
393
- const recent = state.timestamps.filter((t) => now - t < 1000);
394
- if (recent.length >= maxPerSecond) return [false, { timestamps: recent }];
395
- return [true, { timestamps: [...recent, now] }];
396
- });
397
- ```
398
-
399
- #### Benefits Beyond Bundle Size
400
-
401
- - ✅ **Zero friction** - Copy-paste ready transformers
402
- - ✅ **Perfect discoverability** - IntelliSense shows all available transformers
403
- - ✅ **Always up-to-date** - Examples match current API version
404
- - ✅ **No ecosystem fragmentation** - Everything in one place
405
- - ✅ **Educational transparency** - Users learn implementation patterns
406
- - ✅ **Infinite extensibility** - Users become transformer creators
407
- - ✅ **Self-documenting** - Usage examples included with working code
408
- - ✅ **Zero bundle cost** - JSDoc stripped at compile time
409
-
410
- #### The Network Effect
411
-
412
- Documentation-as-Distribution creates **multiplicative value**:
413
-
414
- 1. **User discovers** transformer in JSDoc
415
- 2. **User learns** implementation pattern
416
- 3. **User creates** custom transformers for their domain
417
- 4. **User shares** patterns with their team
418
- 5. **Team creates** hundreds of variations
419
- 6. **Knowledge multiplies** exponentially across the community
420
-
421
- **How it works:**
422
-
423
- 1. Hover over `Stream` in your IDE to see the complete transformers library
424
- 2. Or hover over individual functions for quick references
425
- 3. Copy the transformer you need
426
- 4. Use immediately - perfect TypeScript inference included!
427
- 5. **Learn the patterns** and create your own infinite variations
428
-
429
- **Available Transformers (via JSDoc):**
430
-
431
- - `take(n)`, `skip(n)`, `distinct()`, `tap(fn)` - Essential filtering patterns
432
- - `withIndex()`, `delay(ms)`, `pluck(key)`, `scan(fn, initial)` - Common transformation patterns
433
- - `toState(initialValue)` - Convert streams to reactive state
434
- - More transformers added with each release!
435
-
436
- **📊 Bundle Size Impact:**
437
-
438
- - **Package size**: Currently ~15KB, grows with JSDoc transformer examples over time
439
- - **Your app bundle**: Always only 5.5KB (runtime code only, zero JSDoc overhead)
440
- - **Tree-shaking**: Only imported functions included in final bundle
441
- - **JSDoc transformers**: "Free" - rich transformer library without production cost
442
-
443
- **You're not just building applications - you're learning a paradigm that scales infinitely.**
444
-
445
- ### Manual Composition
446
-
447
- ```typescript
448
- // You can still build transformers manually
449
- const customTransformer = <T>(count: number) =>
450
- filter<T, { taken: number }>({ taken: 0 }, (state, value) => {
451
- if (state.taken >= count) return; // Terminate after N items
452
- return [true, { taken: state.taken + 1 }];
453
- });
454
- ```
455
-
456
- ### Reactive State: Stateful Values
457
-
458
- `State` extends `Stream` with a current value that can be read and written:
459
-
460
- ```typescript
461
- const user = new State<User | null>(null);
462
- const theme = new State<"light" | "dark">("light");
463
- const counter = new State(0);
464
-
465
- // Read current value
466
- console.log(counter.value); // 0
467
-
468
- // Write triggers all listeners
469
- counter.value = 5;
470
-
471
- // State from transformed streams
472
- const source = new Stream<number>();
473
- const derivedState = new State(0, source.pipe(map((v) => v * 2)));
474
-
475
- // Derived state using transformers
476
- const isLoggedIn = user.pipe(map((u) => u !== null));
477
-
478
- const userDisplayName = user.pipe(
479
- filter((u) => u !== null),
480
- map((u) => `${u.firstName} ${u.lastName}`)
481
- );
482
-
483
- // Convert streams to state with toState transformer
484
- const processedState = source
485
- .pipe(filter((v) => v > 0))
486
- .pipe(map((v) => v.toString()))
487
- .pipe(toState("0")); // Explicit initial value
488
-
489
- // Automatic UI updates
490
- isLoggedIn.listen((loggedIn) => {
491
- document.body.classList.toggle("authenticated", loggedIn);
492
- });
493
-
494
- // State changes propagate through the pipeline
495
- user.value = { firstName: "John", lastName: "Doe" };
496
- // Triggers: isLoggedIn → true, userDisplayName → 'John Doe'
497
- ```
498
-
499
- ### Reactive Collections: Fine-Grained Change Events
500
-
501
- Collections that emit specific change events for efficient UI updates:
502
-
503
- ```typescript
504
- import { List, Map, Set } from "@soffinal/stream";
505
-
506
- const todos = new List<Todo>();
507
- const userCache = new Map<string, User>();
508
- const activeUsers = new Set<string>();
509
-
510
- // React to specific operations
511
- todos.insert.listen(([index, todo]) => {
512
- console.log(`Todo inserted at ${index}:`, todo);
513
- renderTodoAtIndex(index, todo);
514
- });
515
-
516
- todos.delete.listen(([index, todo]) => {
517
- console.log(`Todo removed from ${index}:`, todo);
518
- removeTodoFromDOM(index);
519
- });
520
-
521
- // Map changes
522
- userCache.set.listen(([key, user]) => {
523
- console.log(`User cached: ${key}`, user);
524
- updateUserInUI(key, user);
525
- });
526
-
527
- // Set changes
528
- activeUsers.add.listen((userId) => {
529
- console.log(`User ${userId} came online`);
530
- showOnlineIndicator(userId);
531
- });
532
-
533
- activeUsers.delete.listen((userId) => {
534
- console.log(`User ${userId} went offline`);
535
- hideOnlineIndicator(userId);
536
- });
537
-
538
- // Use like normal collections
539
- todos.push({ id: 1, text: "Learn streams", done: false });
540
- userCache.set("user1", { name: "Alice", email: "alice@example.com" });
541
- activeUsers.add("user1");
542
- ```
543
-
544
- ## API Reference
205
+ ## API
545
206
 
546
207
  ### Stream\<T>
547
208
 
548
- #### Core Methods
549
-
550
- - `push(...values: T[]): void` - Emit values to all listeners
551
- - `listen(callback: (value: T) => void, signal?: AbortSignal | Stream<any>): () => void` - Add listener, returns cleanup
552
- - `pipe<OUTPUT extends Stream<any>>(transformer: (stream: this) => OUTPUT): OUTPUT` - Apply any transformer
209
+ - `push(...values: T[])` - Emit values
210
+ - `listen(callback, context?)` - Add listener
211
+ - `pipe(transformer)` - Transform stream
212
+ - `clear()` - Remove all listeners
553
213
 
554
- #### Async Interface
214
+ ### Async
555
215
 
556
- - `then<U>(callback?: (value: T) => U): Promise<U>` - Promise interface for next value
557
- - `[Symbol.asyncIterator](): AsyncIterator<T>` - Async iteration support
216
+ - `await stream` - Wait for next value
217
+ - `for await (const value of stream)` - Iterate
558
218
 
559
- #### Properties
219
+ ## Philosophy
560
220
 
561
- - `hasListeners: boolean` - Whether stream has active listeners
562
- - `listenerAdded: Stream<void>` - Emits when listener is added
563
- - `listenerRemoved: Stream<void>` - Emits when listener is removed
221
+ **2 primitives**: `Stream` + `pipe`
564
222
 
565
- ### State\<T> extends Stream\<T>
223
+ **6 transformers**: `state`, `gate`, `filter`, `map`, `merge`, `flat`
566
224
 
567
- #### Constructor
225
+ Everything else you compose yourself.
568
226
 
569
- - `new State(initialValue: T)` - Create state with initial value
570
- - `new State(initialValue: T, stream: Stream<T>)` - Create state from stream
571
-
572
- #### Additional Properties
573
-
574
- - `value: T` - Current state value (get/set)
575
-
576
- ### Universal Transformers
577
-
578
- #### filter(predicate, options?)
579
-
580
- - **Simple**: `filter((value) => boolean)`
581
- - **Type Guard**: `filter((value): value is Type => boolean)` (sync only)
582
- - **Async**: `filter(async (value) => boolean, { strategy? })` with concurrency options
583
- - **Stateful**: `filter(state, (state, value) => [boolean, newState])` (always sequential)
584
- - **Termination**: Return `undefined` to terminate stream
585
- - **Strategies**: `"sequential"` | `"concurrent-unordered"` | `"concurrent-ordered"`
586
-
587
- #### map(mapper, options?)
588
-
589
- - **Simple**: `map((value) => newValue)`
590
- - **Async**: `map(async (value) => newValue, { strategy? })` with concurrency options
591
- - **Stateful**: `map(state, (state, value) => [newValue, newState])` (always sequential)
592
- - **Strategies**: `"sequential"` | `"concurrent-unordered"` | `"concurrent-ordered"`
593
-
594
- #### merge(...streams)
595
-
596
- - **Basic**: `stream.pipe(merge(stream2, stream3))`
597
- - **Type-Safe**: Automatically creates union types
598
- - **Temporal Order**: Maintains chronological sequence
599
-
600
- #### flat(depth?)
601
-
602
- - **Basic**: `stream.pipe(flat())` - Flatten one level
603
- - **Deep**: `stream.pipe(flat(2))` - Flatten N levels
604
- - **Event Multiplication**: 1 array event → N individual events
605
-
606
- ### Reactive Collections
607
-
608
- #### List\<T>
609
-
610
- - `insert: Stream<[number, T]>` - Insertion events
611
- - `delete: Stream<[number, T]>` - Deletion events
612
- - `clear: Stream<void>` - Clear events
613
-
614
- #### Map\<K,V> extends globalThis.Map\<K,V>
615
-
616
- - `set: Stream<[K, V]>` - Set events (only on changes)
617
- - `delete: Stream<[K, V]>` - Delete events
618
- - `clear: Stream<void>` - Clear events
619
-
620
- #### Set\<T> extends globalThis.Set\<T>
621
-
622
- - `add: Stream<T>` - Add events (only new values)
623
- - `delete: Stream<T>` - Delete events
624
- - `clear: Stream<void>` - Clear events
625
-
626
- ## Performance
627
-
628
- ### Bundle Size
629
-
630
- - **Runtime bundle** - 5.5KB minified, 1.6KB gzipped
631
- - **Package size** - Starts small, grows with JSDoc transformer library
632
- - **Your production app** - Always gets only the 5.5KB runtime code
633
- - **Tree-shakeable** - Import only what you use
634
-
635
- ### Benchmarks
636
-
637
- - **Fast startup** - Zero dependencies, instant initialization
638
- - **Efficient pipelines** - Optimized transformer composition
639
- - **Memory bounded** - Built-in backpressure handling
640
-
641
- ## Runtime Support
642
-
643
- - **Modern browsers** supporting ES2020+
644
- - **Node.js** 16+
645
- - **Deno** 1.0+
646
- - **Bun** 1.0+
647
- - **Cloudflare Workers**
648
-
649
- ## Migration Guide
650
-
651
- ### From EventEmitter
227
+ **Efficient by design**: Transformers execute once per value. Multiple listeners share the same computation:
652
228
 
653
229
  ```typescript
654
- // EventEmitter
655
- import { EventEmitter } from "events";
656
- const emitter = new EventEmitter();
657
- emitter.on("data", console.log);
658
- emitter.emit("data", "hello");
230
+ const expensive = source.pipe(map(async v => await heavyComputation(v)));
659
231
 
660
- // @soffinal/stream
661
- import { Stream } from "@soffinal/stream";
662
- const stream = new Stream();
663
- stream.listen(console.log);
664
- stream.push("hello");
232
+ expensive.listen(v => updateUI(v));
233
+ expensive.listen(v => logToAnalytics(v));
234
+ expensive.listen(v => saveToCache(v));
235
+ // heavyComputation() runs ONCE per value, not 3 times
665
236
  ```
666
237
 
667
- ## Documentation
668
-
669
- ### Transformer Guides
670
-
671
- - **[Filter Transformer →](src/transformers/filter.md)** - Concurrency strategies, type guards, stateful filtering, and stream termination
672
- - **[Map Transformer →](src/transformers/map.md)** - Concurrency strategies, type transformations, stateful mapping, and performance optimization
673
- - **[Merge Transformer →](src/transformers/merge.md)** - Stream orchestration and type-safe combination
674
- - **[Flat Transformer →](src/transformers/flat.md)** - Event multiplication and array flattening
238
+ ## Performance
675
239
 
676
- ## Contributing
240
+ - 1.6KB gzipped
241
+ - Zero dependencies
242
+ - Automatic memory management
243
+ - Tree-shakeable
677
244
 
678
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
245
+ ## Browser Support
679
246
 
680
- ### Development Setup
681
-
682
- ```bash
683
- git clone https://github.com/soffinal/stream.git
684
- cd stream
685
- bun install
686
- bun test
687
- ```
247
+ Chrome 84+, Firefox 79+, Safari 14.1+, Node.js 16+, Deno, Bun
688
248
 
689
249
  ## License
690
250
 
691
251
  MIT © [Soffinal](https://github.com/soffinal)
692
-
693
- Contact: <smari.sofiane@gmail.com>
694
-
695
- ---