@soffinal/stream 0.1.4 → 0.2.1

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 (39) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +439 -298
  3. package/dist/index.d.ts +2 -4
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2 -2
  6. package/dist/index.js.map +12 -8
  7. package/dist/reactive/index.d.ts +5 -0
  8. package/dist/reactive/index.d.ts.map +1 -0
  9. package/dist/{list.d.ts → reactive/list.d.ts} +19 -1
  10. package/dist/reactive/list.d.ts.map +1 -0
  11. package/dist/{map.d.ts → reactive/map.d.ts} +11 -1
  12. package/dist/reactive/map.d.ts.map +1 -0
  13. package/dist/{set.d.ts → reactive/set.d.ts} +11 -1
  14. package/dist/reactive/set.d.ts.map +1 -0
  15. package/dist/{state.d.ts → reactive/state.d.ts} +18 -18
  16. package/dist/reactive/state.d.ts.map +1 -0
  17. package/dist/stream.d.ts +94 -289
  18. package/dist/stream.d.ts.map +1 -1
  19. package/dist/transformers/filter.d.ts +35 -0
  20. package/dist/transformers/filter.d.ts.map +1 -0
  21. package/dist/transformers/flat.d.ts +31 -0
  22. package/dist/transformers/flat.d.ts.map +1 -0
  23. package/dist/transformers/index.d.ts +5 -0
  24. package/dist/transformers/index.d.ts.map +1 -0
  25. package/dist/transformers/map.d.ts +36 -0
  26. package/dist/transformers/map.d.ts.map +1 -0
  27. package/dist/transformers/merge.d.ts +35 -0
  28. package/dist/transformers/merge.d.ts.map +1 -0
  29. package/package.json +5 -8
  30. package/src/transformers/filter.md +202 -0
  31. package/src/transformers/flat.md +56 -0
  32. package/src/transformers/map.md +216 -0
  33. package/src/transformers/merge.md +79 -0
  34. package/dist/benchmark.d.ts +0 -16
  35. package/dist/benchmark.d.ts.map +0 -1
  36. package/dist/list.d.ts.map +0 -1
  37. package/dist/map.d.ts.map +0 -1
  38. package/dist/set.d.ts.map +0 -1
  39. package/dist/state.d.ts.map +0 -1
package/README.md CHANGED
@@ -5,9 +5,9 @@
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@soffinal/stream)](https://bundlephobia.com/package/@soffinal/stream)
7
7
 
8
- > **High-performance reactive streaming library for TypeScript/JavaScript**
8
+ > **A reactive streaming library with Adaptive Constraints**
9
9
 
10
- A modern, async-first streaming library that treats asynchronous data flow as a first-class citizen. Built for real-time applications, event-driven architectures, and reactive programming patterns.
10
+ A groundbreaking streaming library that introduces **Adaptive Reactive Programming** - where transformers maintain state and evolve their behavior based on stream history. Built with four universal primitives that compose into infinite possibilities.
11
11
 
12
12
  ## Table of Contents
13
13
 
@@ -18,48 +18,74 @@ A modern, async-first streaming library that treats asynchronous data flow as a
18
18
  - [API Reference](#api-reference)
19
19
  - [Examples](#examples)
20
20
  - [Performance](#performance)
21
- - [Browser Support](#browser-support)
22
21
  - [Migration Guide](#migration-guide)
22
+ - [Documentation](#documentation)
23
23
  - [Contributing](#contributing)
24
24
  - [License](#license)
25
25
 
26
26
  ## Features
27
27
 
28
- - 🚀 **High Performance** - Optimized for speed and efficiency
29
- - 🔄 **Multicast by Default** - One stream, many consumers
30
- - **Awaitable Streams** - `await stream` for next value
31
- - 🔁 **Async Iterable** - Use `for await` loops naturally
32
- - **Async Operations** - All transformers support async functions while maintaining order
33
- - 🧠 **Smart Caching** - Operations run once, results shared
34
- - 🗑️ **Auto Cleanup** - Memory management handled automatically
35
- - 🔧 **Dual Programming Paradigms** - Method chaining or functional pipe composition
36
- - 🛠️ **Custom Transformers** - Create reusable transformers in both OOP and functional styles
37
- - 📊 **Reactive Collections** - Lists, Maps, Sets with fine-grained change events
38
- - 🔀 **Stateful Operations** - Built-in support for stateful filtering, mapping, and grouping
39
- - 📦 **Zero Dependencies** - Lightweight (~6KB minified, ~2KB gzipped) and tree-shakeable
28
+ - 🧠 **Adaptive Constraints** - Transformers that learn and evolve based on stream history
29
+ - 🔧 **Universal Primitives** - Four algebraic primitives: `filter`, `map`, `merge`, `flat`
30
+ - 📚 **Documentation-as-Distribution** - Copy-paste transformers embedded in JSDoc, no separate packages needed
31
+ - **Async-First** - All operations support async with order preservation
32
+ - 🔄 **Multicast Streams** - One stream, unlimited consumers
33
+ - **Awaitable** - `await stream` for next value
34
+ - 🔁 **Async Iterable** - Native `for await` loop support
35
+ - 🛠️ **Pipe Composition** - Functional transformer composition
36
+ - 📊 **Reactive State** - Stateful values with automatic change propagation
37
+ - 📋 **Reactive Collections** - Lists, Maps, Sets with fine-grained events
38
+ - 🗑️ **Stream Termination** - Declarative stream lifecycle control
39
+ - 📦 **Zero Dependencies** - Lightweight and tree-shakeable
40
40
  - 🌐 **Universal** - Node.js, browsers, Deno, Bun, Cloudflare Workers
41
- - 📘 **Full TypeScript** - Complete type safety and inference
41
+ - 📘 **Full TypeScript** - Complete type safety without the burden
42
42
 
43
43
  ## Quick Start
44
44
 
45
45
  ```typescript
46
- import { Stream, State } from "@soffinal/stream";
46
+ import { Stream, State, filter, map, merge } from "@soffinal/stream";
47
47
 
48
- // Create reactive data streams
48
+ // Create reactive streams
49
49
  const events = new Stream<string>();
50
- const counter = new State(0);
50
+ const numbers = new Stream<number>();
51
51
 
52
- // Transform and filter data
53
- const processed = events.filter((msg) => msg.length > 3).map((msg) => msg.toUpperCase());
52
+ // Pipe-based transformation with Adaptive Constraints
53
+ const processed = events
54
+ .pipe(simpleFilter((msg) => msg.length > 3)) // Simple filtering
55
+ .pipe(simpleMap((msg) => msg.toUpperCase())); // Transform to uppercase
54
56
 
55
- // Multiple consumers
56
- processed.listen((msg) => console.log("Logger:", msg));
57
- processed.listen((msg) => updateUI(msg));
57
+ // Stateful transformers that learn and adapt
58
+ const runningAverage = numbers
59
+ .pipe(
60
+ filter({ count: 0 }, (state, value) => {
61
+ // Only pass every 3rd number, terminate after 10
62
+ if (state.count >= 10) return; // Stream termination
63
+ return [(state.count + 1) % 3 === 0, { count: state.count + 1 }];
64
+ })
65
+ )
66
+ .pipe(
67
+ map({ sum: 0, count: 0 }, (state, value) => {
68
+ const newSum = state.sum + value;
69
+ const newCount = state.count + 1;
70
+ const average = newSum / newCount;
71
+ return [
72
+ { value, average },
73
+ { sum: newSum, count: newCount },
74
+ ];
75
+ })
76
+ );
77
+
78
+ // Copy-paste transformers from JSDoc
79
+ const limited = numbers.pipe(take(5)); // Limit to 5 items
80
+ const indexed = events.pipe(withIndex()); // Add indices
81
+ const delayed = processed.pipe(delay(100)); // Delay each value
58
82
 
59
- // Push data through the pipeline
60
- events.push("hello", "hi", "world"); // Only 'HELLO' and 'WORLD' processed
83
+ // Multiple consumers
84
+ processed.listen((msg) => console.log("Processed:", msg));
85
+ runningAverage.listen(({ value, average }) => console.log(`Value: ${value}, Running Average: ${average}`));
61
86
 
62
- // Reactive state management
87
+ // Reactive state
88
+ const counter = new State(0);
63
89
  counter.listen((count) => (document.title = `Count: ${count}`));
64
90
  counter.value++; // UI updates automatically
65
91
  ```
@@ -102,234 +128,442 @@ deno add jsr:@soffinal/stream
102
128
 
103
129
  ## Core Concepts
104
130
 
105
- ### Streams: Async Data Pipelines
131
+ ### Streams: Multicast Event Pipelines
106
132
 
107
- A `Stream` is an async iterable that can push values to multiple listeners while also being awaitable for the next value.
133
+ A `Stream` is a multicast, async iterable that pushes values to multiple listeners while being awaitable for the next value.
108
134
 
109
135
  ```typescript
110
136
  const userEvents = new Stream<UserEvent>();
111
137
 
112
- // Multiple consumers
138
+ // Multiple consumers automatically share the same data
113
139
  userEvents.listen((event) => analytics.track(event));
114
140
  userEvents.listen((event) => notifications.send(event));
115
141
  userEvents.listen((event) => database.save(event));
116
142
 
117
- // Or await the next event
143
+ // Await the next event
118
144
  const nextEvent = await userEvents;
145
+
146
+ // Async iteration
147
+ for await (const event of userEvents) {
148
+ if (event.type === "critical") break;
149
+ processEvent(event);
150
+ }
119
151
  ```
120
152
 
121
- ### Transformers: Fluent & Functional Styles
153
+ ### Pipe: Stream-to-Stream Composition
122
154
 
123
- #### Method Chaining (Fluent)
155
+ The `pipe` method enforces reactive composition - it only accepts functions that return Stream instances, maintaining the infinite reactive pipeline:
124
156
 
125
157
  ```typescript
126
- const stream = new Stream<number>();
158
+ // All transformers return Streams - infinite chaining
159
+ stream.pipe(filter({}, (_, v) => [v > 0, {}])); // → Stream<T>
160
+ stream.pipe(map({}, (_, v) => [v.toString(), {}])); // → Stream<string>
161
+ stream.pipe(toState("initial")); // → State<string> (extends Stream)
127
162
 
128
- // Simple transformations
163
+ // Infinite chaining - every pipe returns a Stream
129
164
  const result = stream
130
- .filter((x) => x > 0)
131
- .map((x) => x * 2)
132
- .group((batch) => batch.length >= 5);
165
+ .pipe(filter({}, (_, v) => [v > 0, {}]))
166
+ .pipe(map({}, (_, v) => [v * 2, {}]))
167
+ .pipe(take(5))
168
+ .pipe(delay(100))
169
+ .pipe(distinct()); // Always chainable
170
+ ```
171
+
172
+ **Streams are infinite** - Like event emitters, they don't terminate naturally. The `pipe` constraint ensures you maintain the reactive paradigm throughout your entire pipeline.
173
+
174
+ **Perfect TypeScript inference** - no annotations needed:
175
+
176
+ ```typescript
177
+ const numbers = new Stream<number>();
178
+
179
+ // TypeScript knows these are all Streams
180
+ const doubled = numbers.pipe(map({}, (_, n) => [n * 2, {}])); // Stream<number>
181
+ const strings = numbers.pipe(map({}, (_, n) => [n.toString(), {}])); // Stream<string>
182
+ const state = numbers.pipe(toState(0)); // State<number>
183
+ ```
184
+
185
+ ### Universal Primitives: The Four Algebraic Operations
186
+
187
+ All stream operations are built from four universal primitives with **Adaptive Constraints**:
133
188
 
134
- // Async operations with order preservation
135
- const asyncProcessed = stream
136
- .filter(async (value) => {
189
+ #### 1. Filter - Adaptive Gatekeeper
190
+
191
+ ```typescript
192
+ import { filter } from "@soffinal/stream";
193
+
194
+ // Simple filtering
195
+ stream.pipe(filter({}, (_, value) => [value > 0, {}]));
196
+
197
+ // Stateful filtering with termination
198
+ stream.pipe(
199
+ filter({ count: 0 }, (state, value) => {
200
+ if (state.count >= 10) return; // Terminate after 10 items
201
+ return [value > 0, { count: state.count + 1 }];
202
+ })
203
+ );
204
+
205
+ // Async filtering
206
+ stream.pipe(
207
+ filter({}, async (_, value) => {
137
208
  const isValid = await validateAsync(value);
138
- return isValid;
209
+ return [isValid, {}];
139
210
  })
140
- .map(async (value) => {
141
- const enriched = await enrichWithExternalData(value);
142
- return { original: value, enriched };
143
- });
211
+ );
212
+ ```
213
+
214
+ **[📖 Complete Filter Documentation →](src/transformers/filter.md)**
144
215
 
145
- // Stateful operations with initial state
146
- const stateful = stream
147
- .filter(0, (prev, curr) => [curr > prev, Math.max(prev, curr)]) // Only increasing values
148
- .map([], (history, value) => {
149
- const newHistory = [...history, value].slice(-3); // Keep last 3
150
- const average = newHistory.reduce((a, b) => a + b, 0) / newHistory.length;
151
- return [{ value, average, history: newHistory }, newHistory];
216
+ #### 2. Map - Adaptive Transformer
217
+
218
+ ```typescript
219
+ import { map } from "@soffinal/stream";
220
+
221
+ // Simple transformation
222
+ stream.pipe(map({}, (_, value) => [value * 2, {}]));
223
+
224
+ // Stateful transformation with context
225
+ stream.pipe(
226
+ map({ sum: 0 }, (state, value) => {
227
+ const newSum = state.sum + value;
228
+ return [{ value, runningSum: newSum }, { sum: newSum }];
152
229
  })
153
- .group(5, (count, item) => [count >= 5, count >= 5 ? 0 : count + 1]); // Batch every 5
230
+ );
231
+
232
+ // Async transformation with order preservation
233
+ stream.pipe(
234
+ map({}, async (_, value) => {
235
+ const enriched = await enrichWithAPI(value);
236
+ return [{ original: value, enriched }, {}];
237
+ })
238
+ );
154
239
  ```
155
240
 
156
- #### Functional Composition (Pipe)
241
+ **[📖 Complete Map Documentation →](src/transformers/map.md)**
242
+
243
+ #### 3. Merge - Stream Orchestration
157
244
 
158
245
  ```typescript
159
- import { filter, map, group, merge } from "@soffinal/stream";
246
+ import { merge } from "@soffinal/stream";
160
247
 
161
- // Simple functional composition
162
- const result = stream
163
- .pipe(filter((x) => x > 0))
164
- .pipe(map((x) => x * 2))
165
- .pipe(group((batch) => batch.length >= 5));
166
-
167
- // Async functional transformers (order preserved)
168
- const asyncPipeline = stream
169
- .pipe(filter(async (x) => await isValidAsync(x)))
170
- .pipe(map(async (x) => await processAsync(x)))
171
- .pipe(group(batch => batch.length >= 5));
172
-
173
- // Stateful functional transformers
174
- const advanced = stream
175
- .pipe(filter(0, (prev, curr) => [curr > prev, Math.max(prev, curr)]))
176
- .pipe(
177
- map([], (acc, value) => {
178
- const newAcc = [...acc, value].slice(-10);
179
- const sum = newAcc.reduce((a, b) => a + b, 0);
180
- return [{ value, sum, count: newAcc.length }, newAcc];
181
- })
182
- )
183
- .pipe(group(0, (count, item) => [count >= 3, count >= 3 ? 0 : count + 1]));
184
-
185
- // Combining multiple streams
186
- const stream2 = new Stream<number>();
187
- const merged = stream
188
- .pipe(filter((x) => x % 2 === 0))
189
- .pipe(merge(stream2))
190
- .pipe(map((x) => x.toString()));
248
+ const stream1 = new Stream<number>();
249
+ const stream2 = new Stream<string>();
250
+
251
+ // Combine multiple streams with type safety
252
+ const combined = stream1.pipe(merge(stream2));
253
+ // Type: Stream<number | string>
254
+
255
+ combined.listen((value) => {
256
+ if (typeof value === "number") {
257
+ console.log("Number:", value);
258
+ } else {
259
+ console.log("String:", value);
260
+ }
261
+ });
191
262
  ```
192
263
 
193
- ### Custom Transformers
264
+ **[📖 Complete Merge Documentation →](src/transformers/merge.md)**
194
265
 
195
- Create reusable transformers for both paradigms:
266
+ #### 4. Flat - Event Multiplication
196
267
 
197
268
  ```typescript
198
- // Custom transformer: distinctUntilChanged
199
- const distinctUntilChanged = <T>(stream: Stream<T>): Stream<T> => {
200
- return new Stream<T>(async function* () {
201
- let prev: T;
202
- let first = true;
203
-
204
- for await (const value of stream) {
205
- if (first || value !== prev) {
206
- yield value;
207
- prev = value;
208
- first = false;
209
- }
210
- }
211
- });
212
- };
213
-
214
- // Custom transformer: throttle
215
- const throttle =
216
- <T>(ms: number) =>
217
- (stream: Stream<T>): Stream<T> => {
218
- return new Stream<T>(async function* () {
219
- let lastEmit = 0;
220
-
221
- for await (const value of stream) {
222
- const now = Date.now();
223
- if (now - lastEmit >= ms) {
224
- yield value;
225
- lastEmit = now;
226
- }
227
- }
228
- });
229
- };
230
-
231
- // Usage with perfect type inference
232
- const searchInput = new Stream<string>();
269
+ import { flat } from "@soffinal/stream";
270
+
271
+ // Transform 1 array event → N individual events
272
+ const arrayStream = new Stream<number[]>();
273
+ const individualNumbers = arrayStream.pipe(flat());
274
+
275
+ arrayStream.push([1, 2, 3]); // Emits: 1, 2, 3 as separate events
276
+
277
+ // Configurable depth flattening
278
+ const deepArrays = new Stream<number[][][]>();
279
+ const flattened = deepArrays.pipe(flat(2)); // Flatten 2 levels deep
280
+ ```
281
+
282
+ **[📖 Complete Flat Documentation →](src/transformers/flat.md)**
283
+
284
+ ### Documentation-as-Distribution: Copy-Paste Transformers
285
+
286
+ No separate repos, no CLI tools, no package management - just copy-paste ready transformers embedded in JSDoc!
287
+
288
+ But more importantly: **Documentation-as-Distribution is actually Education-as-Distribution.**
289
+
290
+ #### The Educational Transparency Revolution
291
+
292
+ Unlike traditional libraries where transformers are minified black boxes, our approach makes **every implementation pattern visible and learnable**:
293
+
294
+ ```typescript
295
+ // 📦 All transformers are copy-pastable from IntelliSense!
296
+ // Hover over 'Stream' to see the complete transformers library
297
+
298
+ // Example: Users don't just get functions - they get implementation education
299
+ const searchInput = new Stream<string>(); // ← Hover here for full library
233
300
  const searchResults = searchInput
234
- .pipe(distinctUntilChanged)
235
- .pipe(throttle(300))
236
- .pipe(map((query) => searchAPI(query)));
301
+ .pipe(distinct()) // Copy from Stream JSDoc - learn deduplication patterns
302
+ .pipe(simpleFilter((q) => q.length > 2)) // Copy from Stream JSDoc - learn filtering logic
303
+ .pipe(take(10)) // Copy from Stream JSDoc - learn termination patterns
304
+ .pipe(delay(300)) // Copy from Stream JSDoc - learn async transformation
305
+ .pipe(simpleMap((query) => searchAPI(query))); // Copy from Stream JSDoc - learn mapping patterns
306
+ ```
307
+
308
+ #### What Users Actually Learn
309
+
310
+ When users hover over any function in JSDoc, they see **complete implementation patterns**:
311
+
312
+ ```typescript
313
+ // Users see EXACTLY how to build transformers
314
+ const take = <T>(n: number) =>
315
+ filter<T, { count: number }>({ count: 0 }, (state, value) => {
316
+ if (state.count >= n) return; // ← Learn termination patterns
317
+ return [true, { count: state.count + 1 }]; // ← Learn state evolution
318
+ });
319
+
320
+ const distinct = <T>() =>
321
+ filter<T, { seen: Set<T> }>({ seen: new Set() }, (state, value) => {
322
+ if (state.seen.has(value)) return [false, state]; // ← Learn deduplication logic
323
+ state.seen.add(value); // ← Learn state mutation patterns
324
+ return [true, state];
325
+ });
326
+ ```
327
+
328
+ #### From Consumers to Creators
329
+
330
+ This transparency empowers users to become **transformer architects**:
331
+
332
+ ```typescript
333
+ // After learning from JSDoc examples, users create their own:
334
+ const withTimestamp = <T>() =>
335
+ map<T, {}, { value: T; timestamp: number }>(
336
+ {}, // ← Learned: empty state when no memory needed
337
+ (_, value) => [
338
+ { value, timestamp: Date.now() }, // ← Learned: transformation pattern
339
+ {}, // ← Learned: state management
340
+ ]
341
+ );
342
+
343
+ const rateLimited = <T>(maxPerSecond: number) =>
344
+ filter<T, { timestamps: number[] }>({ timestamps: [] }, (state, value) => {
345
+ const now = Date.now();
346
+ const recent = state.timestamps.filter((t) => now - t < 1000);
347
+ if (recent.length >= maxPerSecond) return [false, { timestamps: recent }];
348
+ return [true, { timestamps: [...recent, now] }];
349
+ });
350
+ ```
351
+
352
+ #### Benefits Beyond Bundle Size
353
+
354
+ **Traditional Libraries (Code-as-Distribution):**
355
+
356
+ - ❌ **Black box implementations** - Minified, unreadable code
357
+ - ❌ **Separate documentation** - Often outdated, disconnected from code
358
+ - ❌ **Limited extensibility** - Users can only use what's provided
359
+ - ❌ **Learning barrier** - No insight into implementation patterns
360
+ - ❌ **Bundle bloat** - Every transformer adds runtime cost
361
+
362
+ **Documentation-as-Distribution:**
363
+
364
+ - ✅ **Zero friction** - Copy-paste ready transformers
365
+ - ✅ **Perfect discoverability** - IntelliSense shows all available transformers
366
+ - ✅ **Always up-to-date** - Examples match current API version
367
+ - ✅ **No ecosystem fragmentation** - Everything in one place
368
+ - ✅ **Educational transparency** - Users learn implementation patterns
369
+ - ✅ **Infinite extensibility** - Users become transformer creators
370
+ - ✅ **Self-documenting** - Usage examples included with working code
371
+ - ✅ **Zero bundle cost** - JSDoc stripped at compile time
372
+
373
+ #### The Network Effect
374
+
375
+ Documentation-as-Distribution creates **multiplicative value**:
376
+
377
+ 1. **User discovers** transformer in JSDoc
378
+ 2. **User learns** implementation pattern
379
+ 3. **User creates** custom transformers for their domain
380
+ 4. **User shares** patterns with their team
381
+ 5. **Team creates** hundreds of variations
382
+ 6. **Knowledge multiplies** exponentially across the community
383
+
384
+ **How it works:**
385
+
386
+ 1. Hover over `Stream` in your IDE to see the complete transformers library
387
+ 2. Or hover over individual functions for quick references
388
+ 3. Copy the transformer you need
389
+ 4. Use immediately - perfect TypeScript inference included!
390
+ 5. **Learn the patterns** and create your own infinite variations
391
+
392
+ **Available Transformers (via JSDoc):**
393
+
394
+ - `take(n)`, `skip(n)`, `distinct()`, `tap(fn)` - Essential filtering patterns
395
+ - `withIndex()`, `delay(ms)`, `pluck(key)`, `scan(fn, initial)` - Common transformation patterns
396
+ - `simpleFilter(predicate)` - Convenient filtering without state
397
+ - `simpleMap(fn)` - Convenient mapping without state
398
+ - `toState(initialValue)` - Convert streams to reactive state
399
+ - More transformers added with each release!
400
+
401
+ **📊 Bundle Size Impact:**
402
+
403
+ - **Package size**: Currently ~15KB, grows with JSDoc transformer examples over time
404
+ - **Your app bundle**: Always only 5.5KB (runtime code only, zero JSDoc overhead)
405
+ - **Tree-shaking**: Only imported functions included in final bundle
406
+ - **JSDoc transformers**: "Free" - rich transformer library without production cost
407
+
408
+ **You're not just building applications - you're learning a paradigm that scales infinitely.**
409
+
410
+ ### Manual Composition
411
+
412
+ ```typescript
413
+ // You can still build transformers manually
414
+ const customTransformer = <T>(count: number) =>
415
+ filter<T, { taken: number }>({ taken: 0 }, (state, value) => {
416
+ if (state.taken >= count) return; // Terminate after N items
417
+ return [true, { taken: state.taken + 1 }];
418
+ });
237
419
  ```
238
420
 
239
- ### Reactive State
421
+ ### Reactive State: Stateful Values
240
422
 
241
- State objects hold current values and notify dependents of changes:
423
+ `State` extends `Stream` with a current value that can be read and written:
242
424
 
243
425
  ```typescript
244
426
  const user = new State<User | null>(null);
245
427
  const theme = new State<"light" | "dark">("light");
428
+ const counter = new State(0);
429
+
430
+ // Read current value
431
+ console.log(counter.value); // 0
432
+
433
+ // Write triggers all listeners
434
+ counter.value = 5;
246
435
 
247
- // Derived state with transformations
248
- const isLoggedIn = user.map((u) => u !== null);
249
- const userProfile = user
250
- .filter((u): u is User => u !== null)
251
- .map((u) => ({ ...u, displayName: u.firstName + " " + u.lastName }));
436
+ // State from transformed streams
437
+ const source = new Stream<number>();
438
+ const derivedState = new State(0, source.pipe(map({}, (_, v) => [v * 2, {}])));
439
+
440
+ // Derived state using transformers
441
+ const isLoggedIn = user.pipe(map({}, (_, u) => [u !== null, {}]));
442
+
443
+ const userDisplayName = user.pipe(
444
+ filter({}, (_, u) => [u !== null, {}]),
445
+ map({}, (_, u) => [`${u.firstName} ${u.lastName}`, {}])
446
+ );
447
+
448
+ // Convert streams to state with toState transformer
449
+ const processedState = source
450
+ .pipe(filter({}, (_, v) => [v > 0, {}]))
451
+ .pipe(map({}, (_, v) => [v.toString(), {}]))
452
+ .pipe(toState("0")); // Explicit initial value
252
453
 
253
454
  // Automatic UI updates
254
455
  isLoggedIn.listen((loggedIn) => {
255
456
  document.body.classList.toggle("authenticated", loggedIn);
256
457
  });
458
+
459
+ // State changes propagate through the pipeline
460
+ user.value = { firstName: "John", lastName: "Doe" };
461
+ // Triggers: isLoggedIn → true, userDisplayName → 'John Doe'
257
462
  ```
258
463
 
259
- ### Reactive Collections
464
+ ### Reactive Collections: Fine-Grained Change Events
260
465
 
261
- Collections that emit fine-grained change events:
466
+ Collections that emit specific change events for efficient UI updates:
262
467
 
263
468
  ```typescript
469
+ import { List, Map, Set } from "@soffinal/stream";
470
+
264
471
  const todos = new List<Todo>();
265
- const cache = new Map<string, any>();
472
+ const userCache = new Map<string, User>();
266
473
  const activeUsers = new Set<string>();
267
474
 
268
- // React to changes
269
- todos.insert.listen(([index, todo]) => renderTodo(todo, index));
270
- cache.set.listen(([key, value]) => console.log(`Cached: ${key}`));
271
- activeUsers.add.listen((userId) => showOnlineStatus(userId));
475
+ // React to specific operations
476
+ todos.insert.listen(([index, todo]) => {
477
+ console.log(`Todo inserted at ${index}:`, todo);
478
+ renderTodoAtIndex(index, todo);
479
+ });
480
+
481
+ todos.delete.listen(([index, todo]) => {
482
+ console.log(`Todo removed from ${index}:`, todo);
483
+ removeTodoFromDOM(index);
484
+ });
485
+
486
+ // Map changes
487
+ userCache.set.listen(([key, user]) => {
488
+ console.log(`User cached: ${key}`, user);
489
+ updateUserInUI(key, user);
490
+ });
491
+
492
+ // Set changes
493
+ activeUsers.add.listen((userId) => {
494
+ console.log(`User ${userId} came online`);
495
+ showOnlineIndicator(userId);
496
+ });
497
+
498
+ activeUsers.delete.listen((userId) => {
499
+ console.log(`User ${userId} went offline`);
500
+ hideOnlineIndicator(userId);
501
+ });
502
+
503
+ // Use like normal collections
504
+ todos.push({ id: 1, text: "Learn streams", done: false });
505
+ userCache.set("user1", { name: "Alice", email: "alice@example.com" });
506
+ activeUsers.add("user1");
272
507
  ```
273
508
 
274
509
  ## API Reference
275
510
 
276
511
  ### Stream\<T>
277
512
 
513
+ #### Core Methods
514
+
515
+ - `push(...values: T[]): void` - Emit values to all listeners
516
+ - `listen(callback: (value: T) => void, signal?: AbortSignal | Stream<any>): () => void` - Add listener, returns cleanup
517
+ - `pipe<OUTPUT>(transformer: (stream: this) => OUTPUT): OUTPUT` - Apply any transformer (flexible return type)
518
+
519
+ #### Async Interface
520
+
521
+ - `then<U>(callback?: (value: T) => U): Promise<U>` - Promise interface for next value
522
+ - `[Symbol.asyncIterator](): AsyncIterator<T>` - Async iteration support
523
+
278
524
  #### Properties
279
525
 
280
526
  - `hasListeners: boolean` - Whether stream has active listeners
281
527
  - `listenerAdded: Stream<void>` - Emits when listener is added
282
528
  - `listenerRemoved: Stream<void>` - Emits when listener is removed
283
529
 
284
- #### Methods
530
+ ### State\<T> extends Stream\<T>
285
531
 
286
- - `push(...values: T[]): void` - Emit values to all listeners
287
- - `listen(callback: (value: T) => void, signal?: AbortSignal): () => void` - Add listener, returns cleanup function
288
- - `filter<U extends T>(predicate: (value: T) => value is U): Stream<U>` - Filter with type guard
289
- - `filter(predicate: (value: T) => boolean | Promise<boolean>): Stream<T>` - Filter with async predicate
290
- - `filter<S>(initialState: S, accumulator: (state: S, value: T) => [boolean, S] | Promise<[boolean, S]>): Stream<T>` - Stateful filtering
291
- - `map<U>(mapper: (value: T) => U | Promise<U>): Stream<U>` - Transform with async mapper
292
- - `map<S, U>(initialState: S, accumulator: (state: S, value: T) => [U, S] | Promise<[U, S]>): Stream<U>` - Stateful mapping
293
- - `group(predicate: (batch: T[]) => boolean | Promise<boolean>): Stream<T[]>` - Group into batches
294
- - `group<S>(initialState: S, accumulator: (state: S, value: T) => [boolean, S] | Promise<[boolean, S]>): Stream<S>` - Stateful grouping
295
- - `merge<STREAMS extends [Stream<any>, ...Stream<any>[]]>(...streams: STREAMS): Stream<T | ValueOf<STREAMS[number]>>` - Merge multiple streams
296
- - `flat<DEPTH extends number = 0>(depth?: DEPTH): Stream<FlatArray<T, DEPTH>>` - Flatten arrays with configurable depth
297
- - `pipe<U>(transformer: (stream: Stream<T>) => Stream<U>): Stream<U>` - Apply functional transformer
532
+ #### Constructor
298
533
 
299
- #### Async Interface
534
+ - `new State(initialValue: T)` - Create state with initial value
535
+ - `new State(initialValue: T, stream: Stream<T>)` - Create state from stream
300
536
 
301
- - `then<U>(callback?: (value: T) => U): Promise<U>` - Promise interface
302
- - `[Symbol.asyncIterator](): AsyncIterator<T>` - Async iteration
537
+ #### Additional Properties
303
538
 
304
- ### State\<T> extends Stream\<T>
539
+ - `value: T` - Current state value (get/set)
305
540
 
306
- #### Properties
541
+ ### Universal Transformers
307
542
 
308
- - `value: T` - Current state value (get/set)
543
+ #### filter(initialState, accumulator)
309
544
 
310
- ### Transformer Functions
545
+ - **Simple**: `filter({}, (_, value) => [boolean, {}])`
546
+ - **Stateful**: `filter(state, (state, value) => [boolean, newState])`
547
+ - **Async**: `filter({}, async (_, value) => [boolean, {}])`
548
+ - **Termination**: Return `undefined` to terminate stream
311
549
 
312
- Functional transformers for use with `pipe()` - all support async operations:
550
+ #### map(initialState, accumulator)
313
551
 
314
- #### filter
315
- - `filter<T, U extends T>(predicate: (value: T) => value is U): (stream: Stream<T>) => Stream<U>`
316
- - `filter<T>(predicate: (value: T) => boolean | Promise<boolean>): (stream: Stream<T>) => Stream<T>`
317
- - `filter<T, S>(initialState: S, accumulator: (state: S, value: T) => [boolean, S] | Promise<[boolean, S]>): (stream: Stream<T>) => Stream<T>`
552
+ - **Simple**: `map({}, (_, value) => [newValue, {}])`
553
+ - **Stateful**: `map(state, (state, value) => [newValue, newState])`
554
+ - **Async**: `map({}, async (_, value) => [newValue, {}])`
318
555
 
319
- #### map
320
- - `map<T, U>(mapper: (value: T) => U | Promise<U>): (stream: Stream<T>) => Stream<U>`
321
- - `map<T, S, U>(initialState: S, accumulator: (state: S, value: T) => [U, S] | Promise<[U, S]>): (stream: Stream<T>) => Stream<U>`
556
+ #### merge(...streams)
322
557
 
323
- #### group
324
- - `group<T>(predicate: (batch: T[]) => boolean | Promise<boolean>): (stream: Stream<T>) => Stream<T[]>`
325
- - `group<T, S>(initialState: S, accumulator: (state: S, value: T) => [boolean, S] | Promise<[boolean, S]>): (stream: Stream<T>) => Stream<S>`
558
+ - **Basic**: `stream.pipe(merge(stream2, stream3))`
559
+ - **Type-Safe**: Automatically creates union types
560
+ - **Temporal Order**: Maintains chronological sequence
326
561
 
327
- #### merge
328
- - `merge<STREAMS extends [Stream<any>, ...Stream<any>[]]>(...streams: STREAMS): <T>(stream: Stream<T>) => Stream<T | ValueOf<STREAMS[number]>>`
562
+ #### flat(depth?)
329
563
 
330
- #### flat
331
- - `flat(): <T>(stream: Stream<T>) => Stream<FlatArray<T, 0>>`
332
- - `flat<DEPTH extends number>(depth: DEPTH): <T>(stream: Stream<T>) => Stream<FlatArray<T, DEPTH>>`
564
+ - **Basic**: `stream.pipe(flat())` - Flatten one level
565
+ - **Deep**: `stream.pipe(flat(2))` - Flatten N levels
566
+ - **Event Multiplication**: 1 array event N individual events
333
567
 
334
568
  ### Reactive Collections
335
569
 
@@ -351,111 +585,22 @@ Functional transformers for use with `pipe()` - all support async operations:
351
585
  - `delete: Stream<T>` - Delete events
352
586
  - `clear: Stream<void>` - Clear events
353
587
 
354
- ## Examples
355
-
356
- ### Real-time Data Processing
357
-
358
- ```typescript
359
- const sensorData = new Stream<SensorReading>();
360
-
361
- // Multi-stage processing pipeline
362
- const alerts = sensorData
363
- .filter((reading) => reading.temperature > 30)
364
- .map((reading) => ({ ...reading, timestamp: Date.now() }))
365
- .group((batch) => batch.length >= 5);
366
-
367
- alerts.listen((batch) => sendAlertNotification(batch));
368
- ```
369
-
370
- ### WebSocket Integration
371
-
372
- ```typescript
373
- const wsStream = new Stream<MessageEvent>(async function* () {
374
- const ws = new WebSocket("wss://api.example.com");
375
-
376
- while (ws.readyState !== WebSocket.CLOSED) {
377
- yield await new Promise((resolve) => {
378
- ws.onmessage = resolve;
379
- });
380
- }
381
- });
382
-
383
- const messages = wsStream.map((event) => JSON.parse(event.data)).filter((data) => data.type === "update");
384
-
385
- messages.listen((update) => handleUpdate(update));
386
- ```
387
-
388
- ### State Management
389
-
390
- ```typescript
391
- interface AppState {
392
- user: User | null;
393
- theme: "light" | "dark";
394
- notifications: Notification[];
395
- }
396
-
397
- const appState = new State<AppState>({
398
- user: null,
399
- theme: "light",
400
- notifications: [],
401
- });
402
-
403
- // Derived state
404
- const unreadCount = appState.map((state) => state.notifications.filter((n) => !n.read).length);
405
-
406
- // Reactive UI
407
- unreadCount.listen((count) => {
408
- document.title = count > 0 ? `(${count}) App` : "App";
409
- });
410
- ```
411
-
412
- ### Browser Counter Example
413
-
414
- ```html
415
- <!DOCTYPE html>
416
- <html>
417
- <head>
418
- <title>Reactive Counter</title>
419
- </head>
420
- <body>
421
- <div id="counter">0</div>
422
- <button id="increment">+</button>
423
- <button id="decrement">-</button>
424
-
425
- <script type="module">
426
- import { State } from "https://esm.sh/@soffinal/stream";
427
-
428
- const count = new State(0);
429
- const display = document.getElementById("counter");
430
-
431
- // Reactive UI updates
432
- count.listen((value) => (display.textContent = value));
433
-
434
- // User interactions
435
- document.getElementById("increment").onclick = () => count.value++;
436
- document.getElementById("decrement").onclick = () => count.value--;
437
- </script>
438
- </body>
439
- </html>
440
- ```
441
-
442
588
  ## Performance
443
589
 
444
- @soffinal/stream delivers exceptional performance:
590
+ ### Bundle Size
445
591
 
446
- - **High throughput** for basic operations
447
- - **Efficient processing** for complex pipelines
448
- - **Memory efficient** with automatic cleanup
449
- - **Zero overhead** for unused features
592
+ - **Runtime bundle** - 5.5KB minified, 1.6KB gzipped
593
+ - **Package size** - Starts small, grows with JSDoc transformer library
594
+ - **Your production app** - Always gets only the 5.5KB runtime code
595
+ - **Tree-shakeable** - Import only what you use
450
596
 
451
- ### Performance Characteristics
597
+ ### Benchmarks
452
598
 
453
- - **Optimized data structures** for minimal memory allocation
454
- - **Efficient event dispatch** with smart caching
455
- - **Lazy evaluation** reduces unnecessary computations
456
- - **Automatic cleanup** prevents memory leaks
599
+ - **Fast startup** - Zero dependencies, instant initialization
600
+ - **Efficient pipelines** - Optimized transformer composition
601
+ - **Memory bounded** - Built-in backpressure handling
457
602
 
458
- ## Browser Support
603
+ ## Runtime Support
459
604
 
460
605
  - **Modern browsers** supporting ES2020+
461
606
  - **Node.js** 16+
@@ -465,28 +610,6 @@ unreadCount.listen((count) => {
465
610
 
466
611
  ## Migration Guide
467
612
 
468
- ### From RxJS
469
-
470
- ```typescript
471
- // RxJS
472
- import { Subject, map, filter } from "rxjs";
473
- const subject = new Subject();
474
- subject
475
- .pipe(
476
- filter((x) => x > 0),
477
- map((x) => x * 2)
478
- )
479
- .subscribe(console.log);
480
-
481
- // @soffinal/stream
482
- import { Stream } from "@soffinal/stream";
483
- const stream = new Stream();
484
- stream
485
- .filter((x) => x > 0)
486
- .map((x) => x * 2)
487
- .listen(console.log);
488
- ```
489
-
490
613
  ### From EventEmitter
491
614
 
492
615
  ```typescript
@@ -503,6 +626,24 @@ stream.listen(console.log);
503
626
  stream.push("hello");
504
627
  ```
505
628
 
629
+ ## Documentation
630
+
631
+ ### Transformer Guides
632
+
633
+ - **[Filter Transformer →](src/transformers/filter.md)** - Adaptive constraints and stream termination
634
+ - **[Map Transformer →](src/transformers/map.md)** - Stateful transformations and async processing
635
+ - **[Merge Transformer →](src/transformers/merge.md)** - Stream orchestration and type-safe combination
636
+ - **[Flat Transformer →](src/transformers/flat.md)** - Event multiplication and array flattening
637
+
638
+ ### Philosophy
639
+
640
+ **Adaptive Reactive Programming** - A new paradigm where transformers maintain state and evolve their behavior based on stream history. This enables:
641
+
642
+ - **Learning transformers** that adapt to data patterns
643
+ - **Stateful operations** with memory between events
644
+ - **Stream termination** for lifecycle control
645
+ - **Zero-overhead types** with perfect inference
646
+
506
647
  ## Contributing
507
648
 
508
649
  We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
@@ -525,5 +666,5 @@ Contact: <smari.sofiane@gmail.com>
525
666
  ---
526
667
 
527
668
  <div align="center">
528
- <strong>Built with ❤️ for the JavaScript community</strong>
669
+ <strong>Pioneering Adaptive Reactive Programming</strong>
529
670
  </div>