@soffinal/stream 0.1.4 → 0.2.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 (39) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +318 -299
  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} +1 -1
  10. package/dist/reactive/list.d.ts.map +1 -0
  11. package/dist/{map.d.ts → reactive/map.d.ts} +1 -1
  12. package/dist/reactive/map.d.ts.map +1 -0
  13. package/dist/{set.d.ts → reactive/set.d.ts} +1 -1
  14. package/dist/reactive/set.d.ts.map +1 -0
  15. package/dist/{state.d.ts → reactive/state.d.ts} +1 -1
  16. package/dist/reactive/state.d.ts.map +1 -0
  17. package/dist/stream.d.ts +1 -279
  18. package/dist/stream.d.ts.map +1 -1
  19. package/dist/transformers/filter.d.ts +64 -0
  20. package/dist/transformers/filter.d.ts.map +1 -0
  21. package/dist/transformers/flat.d.ts +29 -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 +64 -0
  26. package/dist/transformers/map.d.ts.map +1 -0
  27. package/dist/transformers/merge.d.ts +33 -0
  28. package/dist/transformers/merge.d.ts.map +1 -0
  29. package/package.json +5 -8
  30. package/src/transformers/filter.md +161 -0
  31. package/src/transformers/flat.md +56 -0
  32. package/src/transformers/map.md +184 -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
 
@@ -20,46 +20,73 @@ A modern, async-first streaming library that treats asynchronous data flow as a
20
20
  - [Performance](#performance)
21
21
  - [Browser Support](#browser-support)
22
22
  - [Migration Guide](#migration-guide)
23
+ - [Documentation](#documentation)
23
24
  - [Contributing](#contributing)
24
25
  - [License](#license)
25
26
 
26
27
  ## Features
27
28
 
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
29
+ - 🧠 **Adaptive Constraints** - Transformers that learn and evolve based on stream history
30
+ - 🔧 **Universal Primitives** - Four algebraic primitives: `filter`, `map`, `merge`, `flat`
31
+ - 📚 **Documentation-as-Distribution** - Copy-paste transformers embedded in JSDoc, no separate packages needed
32
+ - **Async-First** - All operations support async with order preservation
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** - Functional transformer composition
37
+ - 📊 **Reactive State** - Stateful values with automatic change propagation
38
+ - 📋 **Reactive Collections** - Lists, Maps, Sets with fine-grained events
39
+ - 🗑️ **Stream Termination** - Declarative stream lifecycle control
40
+ - 📦 **Zero Dependencies** - Lightweight and tree-shakeable
40
41
  - 🌐 **Universal** - Node.js, browsers, Deno, Bun, Cloudflare Workers
41
- - 📘 **Full TypeScript** - Complete type safety and inference
42
+ - 📘 **Full TypeScript** - Complete type safety without the burden
42
43
 
43
44
  ## Quick Start
44
45
 
45
46
  ```typescript
46
- import { Stream, State } from "@soffinal/stream";
47
+ import { Stream, State, filter, map, merge } from "@soffinal/stream";
47
48
 
48
- // Create reactive data streams
49
+ // Create reactive streams
49
50
  const events = new Stream<string>();
50
- const counter = new State(0);
51
+ const numbers = new Stream<number>();
51
52
 
52
- // Transform and filter data
53
- const processed = events.filter((msg) => msg.length > 3).map((msg) => msg.toUpperCase());
53
+ // Pipe-based transformation with Adaptive Constraints
54
+ const processed = events
55
+ .pipe(simpleFilter((msg) => msg.length > 3)) // Simple filtering
56
+ .pipe(simpleMap((msg) => msg.toUpperCase())); // Transform to uppercase
54
57
 
55
- // Multiple consumers
56
- processed.listen((msg) => console.log("Logger:", msg));
57
- processed.listen((msg) => updateUI(msg));
58
+ // Stateful transformers that learn and adapt
59
+ const runningAverage = numbers
60
+ .pipe(
61
+ filter({ count: 0 }, (state, value) => {
62
+ // Only pass every 3rd number, terminate after 10
63
+ if (state.count >= 10) return; // Stream termination
64
+ return [(state.count + 1) % 3 === 0, { count: state.count + 1 }];
65
+ })
66
+ )
67
+ .pipe(
68
+ map({ sum: 0, count: 0 }, (state, value) => {
69
+ const newSum = state.sum + value;
70
+ const newCount = state.count + 1;
71
+ const average = newSum / newCount;
72
+ return [
73
+ { value, average },
74
+ { sum: newSum, count: newCount },
75
+ ];
76
+ })
77
+ );
58
78
 
59
- // Push data through the pipeline
60
- events.push("hello", "hi", "world"); // Only 'HELLO' and 'WORLD' processed
79
+ // Copy-paste transformers from JSDoc
80
+ const limited = numbers.pipe(take(5)); // Limit to 5 items
81
+ const indexed = events.pipe(withIndex()); // Add indices
82
+ const delayed = processed.pipe(delay(100)); // Delay each value
83
+
84
+ // Multiple consumers
85
+ processed.listen((msg) => console.log("Processed:", msg));
86
+ runningAverage.listen(({ value, average }) => console.log(`Value: ${value}, Running Average: ${average}`));
61
87
 
62
- // Reactive state management
88
+ // Reactive state
89
+ const counter = new State(0);
63
90
  counter.listen((count) => (document.title = `Count: ${count}`));
64
91
  counter.value++; // UI updates automatically
65
92
  ```
@@ -102,234 +129,319 @@ deno add jsr:@soffinal/stream
102
129
 
103
130
  ## Core Concepts
104
131
 
105
- ### Streams: Async Data Pipelines
132
+ ### Streams: Multicast Event Pipelines
106
133
 
107
- A `Stream` is an async iterable that can push values to multiple listeners while also being awaitable for the next value.
134
+ A `Stream` is a multicast, async iterable that pushes values to multiple listeners while being awaitable for the next value.
108
135
 
109
136
  ```typescript
110
137
  const userEvents = new Stream<UserEvent>();
111
138
 
112
- // Multiple consumers
139
+ // Multiple consumers automatically share the same data
113
140
  userEvents.listen((event) => analytics.track(event));
114
141
  userEvents.listen((event) => notifications.send(event));
115
142
  userEvents.listen((event) => database.save(event));
116
143
 
117
- // Or await the next event
144
+ // Await the next event
118
145
  const nextEvent = await userEvents;
146
+
147
+ // Async iteration
148
+ for await (const event of userEvents) {
149
+ if (event.type === "critical") break;
150
+ processEvent(event);
151
+ }
119
152
  ```
120
153
 
121
- ### Transformers: Fluent & Functional Styles
154
+ ### Universal Primitives: The Four Algebraic Operations
155
+
156
+ All stream operations are built from four universal primitives with **Adaptive Constraints**:
122
157
 
123
- #### Method Chaining (Fluent)
158
+ #### 1. Filter - Adaptive Gatekeeper
124
159
 
125
160
  ```typescript
126
- const stream = new Stream<number>();
161
+ import { filter } from "@soffinal/stream";
127
162
 
128
- // Simple transformations
129
- const result = stream
130
- .filter((x) => x > 0)
131
- .map((x) => x * 2)
132
- .group((batch) => batch.length >= 5);
163
+ // Simple filtering
164
+ stream.pipe(filter({}, (_, value) => [value > 0, {}]));
133
165
 
134
- // Async operations with order preservation
135
- const asyncProcessed = stream
136
- .filter(async (value) => {
166
+ // Stateful filtering with termination
167
+ stream.pipe(
168
+ filter({ count: 0 }, (state, value) => {
169
+ if (state.count >= 10) return; // Terminate after 10 items
170
+ return [value > 0, { count: state.count + 1 }];
171
+ })
172
+ );
173
+
174
+ // Async filtering
175
+ stream.pipe(
176
+ filter({}, async (_, value) => {
137
177
  const isValid = await validateAsync(value);
138
- return isValid;
178
+ return [isValid, {}];
139
179
  })
140
- .map(async (value) => {
141
- const enriched = await enrichWithExternalData(value);
142
- return { original: value, enriched };
143
- });
180
+ );
181
+ ```
144
182
 
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];
183
+ **[📖 Complete Filter Documentation →](src/transformers/filter.md)**
184
+
185
+ #### 2. Map - Adaptive Transformer
186
+
187
+ ```typescript
188
+ import { map } from "@soffinal/stream";
189
+
190
+ // Simple transformation
191
+ stream.pipe(map({}, (_, value) => [value * 2, {}]));
192
+
193
+ // Stateful transformation with context
194
+ stream.pipe(
195
+ map({ sum: 0 }, (state, value) => {
196
+ const newSum = state.sum + value;
197
+ return [{ value, runningSum: newSum }, { sum: newSum }];
152
198
  })
153
- .group(5, (count, item) => [count >= 5, count >= 5 ? 0 : count + 1]); // Batch every 5
199
+ );
200
+
201
+ // Async transformation with order preservation
202
+ stream.pipe(
203
+ map({}, async (_, value) => {
204
+ const enriched = await enrichWithAPI(value);
205
+ return [{ original: value, enriched }, {}];
206
+ })
207
+ );
154
208
  ```
155
209
 
156
- #### Functional Composition (Pipe)
210
+ **[📖 Complete Map Documentation →](src/transformers/map.md)**
211
+
212
+ #### 3. Merge - Stream Orchestration
157
213
 
158
214
  ```typescript
159
- import { filter, map, group, merge } from "@soffinal/stream";
160
-
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()));
215
+ import { merge } from "@soffinal/stream";
216
+
217
+ const stream1 = new Stream<number>();
218
+ const stream2 = new Stream<string>();
219
+
220
+ // Combine multiple streams with type safety
221
+ const combined = stream1.pipe(merge(stream2));
222
+ // Type: Stream<number | string>
223
+
224
+ combined.listen((value) => {
225
+ if (typeof value === "number") {
226
+ console.log("Number:", value);
227
+ } else {
228
+ console.log("String:", value);
229
+ }
230
+ });
191
231
  ```
192
232
 
193
- ### Custom Transformers
233
+ **[📖 Complete Merge Documentation →](src/transformers/merge.md)**
194
234
 
195
- Create reusable transformers for both paradigms:
235
+ #### 4. Flat - Event Multiplication
196
236
 
197
237
  ```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
238
+ import { flat } from "@soffinal/stream";
239
+
240
+ // Transform 1 array event → N individual events
241
+ const arrayStream = new Stream<number[]>();
242
+ const individualNumbers = arrayStream.pipe(flat());
243
+
244
+ arrayStream.push([1, 2, 3]); // Emits: 1, 2, 3 as separate events
245
+
246
+ // Configurable depth flattening
247
+ const deepArrays = new Stream<number[][][]>();
248
+ const flattened = deepArrays.pipe(flat(2)); // Flatten 2 levels deep
249
+ ```
250
+
251
+ **[📖 Complete Flat Documentation →](src/transformers/flat.md)**
252
+
253
+ ### Documentation-as-Distribution: Revolutionary Copy-Paste Transformers
254
+
255
+ **🚀 World's First Documentation-as-Distribution System**
256
+
257
+ No separate repos, no CLI tools, no package management - just copy-paste ready transformers embedded in JSDoc!
258
+
259
+ ```typescript
260
+ // 📦 All transformers are copy-pastable from IntelliSense!
261
+ // Just hover over any primitive in your IDE to see the transformer library
262
+
263
+ // Example: Essential transformers available via autocomplete
232
264
  const searchInput = new Stream<string>();
233
265
  const searchResults = searchInput
234
- .pipe(distinctUntilChanged)
235
- .pipe(throttle(300))
236
- .pipe(map((query) => searchAPI(query)));
266
+ .pipe(distinct()) // Copy from filter() JSDoc
267
+ .pipe(simpleFilter((q) => q.length > 2)) // Copy from filter() JSDoc
268
+ .pipe(take(10)) // Copy from filter() JSDoc
269
+ .pipe(delay(300)) // Copy from map() JSDoc
270
+ .pipe(simpleMap((query) => searchAPI(query))); // Copy from map() JSDoc
271
+ ```
272
+
273
+ **Benefits:**
274
+
275
+ - ✅ **Zero friction** - Copy-paste ready transformers
276
+ - ✅ **Perfect discoverability** - IntelliSense shows all available transformers
277
+ - ✅ **Always up-to-date** - Examples match current API version
278
+ - ✅ **No ecosystem fragmentation** - Everything in one place
279
+ - ✅ **Self-documenting** - Usage examples included
280
+
281
+ **How it works:**
282
+
283
+ 1. Hover over `filter()`, `map()`, `merge()`, or `flat()` in your IDE
284
+ 2. Browse the 📦 COPY-PASTE TRANSFORMER examples
285
+ 3. Copy the transformer you need
286
+ 4. Use immediately - perfect TypeScript inference included!
287
+
288
+ **Available Transformers (via JSDoc):**
289
+
290
+ - `take(n)`, `skip(n)`, `distinct()` - Essential filtering
291
+ - `withIndex()`, `delay(ms)`, `pluck(key)` - Common transformations
292
+ - `simpleFilter(predicate)` - Convenient filtering without state
293
+ - `simpleMap(fn)` - Convenient mapping without state
294
+ - More transformers added with each release!
295
+
296
+ **📊 Bundle Size Impact:**
297
+
298
+ - **Package size**: Currently ~15KB, grows with JSDoc transformer examples over time
299
+ - **Your app bundle**: Always only 5.5KB (runtime code only, zero JSDoc overhead)
300
+ - **Tree-shaking**: Only imported functions included in final bundle
301
+ - **JSDoc transformers**: "Free" - rich transformer library without production cost
302
+
303
+ ### Manual Composition
304
+
305
+ ```typescript
306
+ // You can still build transformers manually
307
+ const customTransformer = <T>(count: number) =>
308
+ filter<T, { taken: number }>({ taken: 0 }, (state, value) => {
309
+ if (state.taken >= count) return; // Terminate after N items
310
+ return [true, { taken: state.taken + 1 }];
311
+ });
237
312
  ```
238
313
 
239
- ### Reactive State
314
+ ### Reactive State: Stateful Values
240
315
 
241
- State objects hold current values and notify dependents of changes:
316
+ `State` extends `Stream` with a current value that can be read and written:
242
317
 
243
318
  ```typescript
244
319
  const user = new State<User | null>(null);
245
320
  const theme = new State<"light" | "dark">("light");
321
+ const counter = new State(0);
322
+
323
+ // Read current value
324
+ console.log(counter.value); // 0
246
325
 
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 }));
326
+ // Write triggers all listeners
327
+ counter.value = 5;
328
+
329
+ // Derived state using transformers
330
+ const isLoggedIn = user.pipe(map({}, (_, u) => [u !== null, {}]));
331
+
332
+ const userDisplayName = user.pipe(
333
+ filter({}, (_, u) => [u !== null, {}]),
334
+ map({}, (_, u) => [`${u.firstName} ${u.lastName}`, {}])
335
+ );
252
336
 
253
337
  // Automatic UI updates
254
338
  isLoggedIn.listen((loggedIn) => {
255
339
  document.body.classList.toggle("authenticated", loggedIn);
256
340
  });
341
+
342
+ // State changes propagate through the pipeline
343
+ user.value = { firstName: "John", lastName: "Doe" };
344
+ // Triggers: isLoggedIn → true, userDisplayName → 'John Doe'
257
345
  ```
258
346
 
259
- ### Reactive Collections
347
+ ### Reactive Collections: Fine-Grained Change Events
260
348
 
261
- Collections that emit fine-grained change events:
349
+ Collections that emit specific change events for efficient UI updates:
262
350
 
263
351
  ```typescript
352
+ import { List, Map, Set } from "@soffinal/stream";
353
+
264
354
  const todos = new List<Todo>();
265
- const cache = new Map<string, any>();
355
+ const userCache = new Map<string, User>();
266
356
  const activeUsers = new Set<string>();
267
357
 
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));
358
+ // React to specific operations
359
+ todos.insert.listen(([index, todo]) => {
360
+ console.log(`Todo inserted at ${index}:`, todo);
361
+ renderTodoAtIndex(index, todo);
362
+ });
363
+
364
+ todos.delete.listen(([index, todo]) => {
365
+ console.log(`Todo removed from ${index}:`, todo);
366
+ removeTodoFromDOM(index);
367
+ });
368
+
369
+ // Map changes
370
+ userCache.set.listen(([key, user]) => {
371
+ console.log(`User cached: ${key}`, user);
372
+ updateUserInUI(key, user);
373
+ });
374
+
375
+ // Set changes
376
+ activeUsers.add.listen((userId) => {
377
+ console.log(`User ${userId} came online`);
378
+ showOnlineIndicator(userId);
379
+ });
380
+
381
+ activeUsers.delete.listen((userId) => {
382
+ console.log(`User ${userId} went offline`);
383
+ hideOnlineIndicator(userId);
384
+ });
385
+
386
+ // Use like normal collections
387
+ todos.push({ id: 1, text: "Learn streams", done: false });
388
+ userCache.set("user1", { name: "Alice", email: "alice@example.com" });
389
+ activeUsers.add("user1");
272
390
  ```
273
391
 
274
392
  ## API Reference
275
393
 
276
394
  ### Stream\<T>
277
395
 
278
- #### Properties
279
-
280
- - `hasListeners: boolean` - Whether stream has active listeners
281
- - `listenerAdded: Stream<void>` - Emits when listener is added
282
- - `listenerRemoved: Stream<void>` - Emits when listener is removed
283
-
284
- #### Methods
396
+ #### Core Methods
285
397
 
286
398
  - `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
399
+ - `listen(callback: (value: T) => void, signal?: AbortSignal | Stream<any>): () => void` - Add listener, returns cleanup
297
400
  - `pipe<U>(transformer: (stream: Stream<T>) => Stream<U>): Stream<U>` - Apply functional transformer
298
401
 
299
402
  #### Async Interface
300
403
 
301
- - `then<U>(callback?: (value: T) => U): Promise<U>` - Promise interface
302
- - `[Symbol.asyncIterator](): AsyncIterator<T>` - Async iteration
404
+ - `then<U>(callback?: (value: T) => U): Promise<U>` - Promise interface for next value
405
+ - `[Symbol.asyncIterator](): AsyncIterator<T>` - Async iteration support
406
+
407
+ #### Properties
408
+
409
+ - `hasListeners: boolean` - Whether stream has active listeners
410
+ - `listenerAdded: Stream<void>` - Emits when listener is added
411
+ - `listenerRemoved: Stream<void>` - Emits when listener is removed
303
412
 
304
413
  ### State\<T> extends Stream\<T>
305
414
 
306
- #### Properties
415
+ #### Additional Properties
307
416
 
308
417
  - `value: T` - Current state value (get/set)
309
418
 
310
- ### Transformer Functions
419
+ ### Universal Transformers
420
+
421
+ #### filter(initialState, accumulator)
422
+
423
+ - **Simple**: `filter({}, (_, value) => [boolean, {}])`
424
+ - **Stateful**: `filter(state, (state, value) => [boolean, newState])`
425
+ - **Async**: `filter({}, async (_, value) => [boolean, {}])`
426
+ - **Termination**: Return `undefined` to terminate stream
311
427
 
312
- Functional transformers for use with `pipe()` - all support async operations:
428
+ #### map(initialState, accumulator)
313
429
 
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>`
430
+ - **Simple**: `map({}, (_, value) => [newValue, {}])`
431
+ - **Stateful**: `map(state, (state, value) => [newValue, newState])`
432
+ - **Async**: `map({}, async (_, value) => [newValue, {}])`
318
433
 
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>`
434
+ #### merge(...streams)
322
435
 
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>`
436
+ - **Basic**: `stream.pipe(merge(stream2, stream3))`
437
+ - **Type-Safe**: Automatically creates union types
438
+ - **Temporal Order**: Maintains chronological sequence
326
439
 
327
- #### merge
328
- - `merge<STREAMS extends [Stream<any>, ...Stream<any>[]]>(...streams: STREAMS): <T>(stream: Stream<T>) => Stream<T | ValueOf<STREAMS[number]>>`
440
+ #### flat(depth?)
329
441
 
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>>`
442
+ - **Basic**: `stream.pipe(flat())` - Flatten one level
443
+ - **Deep**: `stream.pipe(flat(2))` - Flatten N levels
444
+ - **Event Multiplication**: 1 array event N individual events
333
445
 
334
446
  ### Reactive Collections
335
447
 
@@ -351,111 +463,22 @@ Functional transformers for use with `pipe()` - all support async operations:
351
463
  - `delete: Stream<T>` - Delete events
352
464
  - `clear: Stream<void>` - Clear events
353
465
 
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
466
  ## Performance
443
467
 
444
- @soffinal/stream delivers exceptional performance:
468
+ ### Bundle Size
445
469
 
446
- - **High throughput** for basic operations
447
- - **Efficient processing** for complex pipelines
448
- - **Memory efficient** with automatic cleanup
449
- - **Zero overhead** for unused features
470
+ - **Runtime bundle** - 5.5KB minified, 1.6KB gzipped
471
+ - **Package size** - Starts small, grows with JSDoc transformer library
472
+ - **Your production app** - Always gets only the 5.5KB runtime code
473
+ - **Tree-shakeable** - Import only what you use
450
474
 
451
- ### Performance Characteristics
475
+ ### Benchmarks
452
476
 
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
477
+ - **Fast startup** - Zero dependencies, instant initialization
478
+ - **Efficient pipelines** - Optimized transformer composition
479
+ - **Memory bounded** - Built-in backpressure handling
457
480
 
458
- ## Browser Support
481
+ ## Runtime Support
459
482
 
460
483
  - **Modern browsers** supporting ES2020+
461
484
  - **Node.js** 16+
@@ -465,28 +488,6 @@ unreadCount.listen((count) => {
465
488
 
466
489
  ## Migration Guide
467
490
 
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
491
  ### From EventEmitter
491
492
 
492
493
  ```typescript
@@ -503,6 +504,24 @@ stream.listen(console.log);
503
504
  stream.push("hello");
504
505
  ```
505
506
 
507
+ ## Documentation
508
+
509
+ ### Transformer Guides
510
+
511
+ - **[Filter Transformer →](src/transformers/filter.md)** - Adaptive constraints and stream termination
512
+ - **[Map Transformer →](src/transformers/map.md)** - Stateful transformations and async processing
513
+ - **[Merge Transformer →](src/transformers/merge.md)** - Stream orchestration and type-safe combination
514
+ - **[Flat Transformer →](src/transformers/flat.md)** - Event multiplication and array flattening
515
+
516
+ ### Philosophy
517
+
518
+ **Adaptive Reactive Programming** - A new paradigm where transformers maintain state and evolve their behavior based on stream history. This enables:
519
+
520
+ - **Learning transformers** that adapt to data patterns
521
+ - **Stateful operations** with memory between events
522
+ - **Stream termination** for lifecycle control
523
+ - **Zero-overhead types** with perfect inference
524
+
506
525
  ## Contributing
507
526
 
508
527
  We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
@@ -525,5 +544,5 @@ Contact: <smari.sofiane@gmail.com>
525
544
  ---
526
545
 
527
546
  <div align="center">
528
- <strong>Built with ❤️ for the JavaScript community</strong>
547
+ <strong>Pioneering Adaptive Reactive Programming</strong>
529
548
  </div>