@soffinal/stream 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -81
- package/dist/index.js +2 -2
- package/dist/index.js.map +8 -8
- package/dist/reactive/state.d.ts +3 -3
- package/dist/reactive/state.d.ts.map +1 -1
- package/dist/stream.d.ts +52 -19
- package/dist/stream.d.ts.map +1 -1
- package/dist/transformers/filter.d.ts +74 -13
- package/dist/transformers/filter.d.ts.map +1 -1
- package/dist/transformers/flat.d.ts.map +1 -1
- package/dist/transformers/map.d.ts +83 -13
- package/dist/transformers/map.d.ts.map +1 -1
- package/dist/transformers/merge.d.ts +1 -3
- package/dist/transformers/merge.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/transformers/filter.md +244 -120
- package/src/transformers/map.md +336 -122
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://bundlephobia.com/package/@soffinal/stream)
|
|
7
7
|
|
|
8
|
-
> **
|
|
8
|
+
> **Type-safe event emitters that scale**
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Stream is like EventEmitter, but better. Send events to multiple listeners, transform data with `filter` and `map`, and never worry about memory leaks. Works with DOM elements, WebSockets, user interactions, or any async data source. Fully typed, zero dependencies, 5.5KB.
|
|
11
11
|
|
|
12
12
|
## Table of Contents
|
|
13
13
|
|
|
@@ -25,34 +25,70 @@ A modern event emitter that's multicast, awaitable, async iterable, async genera
|
|
|
25
25
|
|
|
26
26
|
## Features
|
|
27
27
|
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
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
|
+
- **Automatic Cleanup** - WeakRef-based listener cleanup prevents memory leaks
|
|
34
|
+
- **Multicast Streams** - One stream, unlimited consumers
|
|
35
|
+
- **Awaitable** - `await stream` for next value
|
|
36
|
+
- **Async Iterable** - Native `for await` loop support
|
|
37
|
+
- **Pipe Composition** - Stream-to-stream functional composition
|
|
38
|
+
- **Type Guards** - Built-in TypeScript type narrowing support
|
|
39
|
+
- **Reactive State** - Stateful values with automatic change propagation
|
|
40
|
+
- **Reactive Collections** - Lists, Maps, Sets with fine-grained events
|
|
41
|
+
- **Stream Termination** - Declarative stream lifecycle control
|
|
42
|
+
- **Zero Dependencies** - Lightweight and tree-shakeable
|
|
43
|
+
- **Universal** - Node.js, browsers, Deno, Bun, Cloudflare Workers
|
|
44
|
+
- **Full TypeScript** - Complete type safety without the burden
|
|
42
45
|
|
|
43
46
|
## Quick Start
|
|
44
47
|
|
|
48
|
+
```typescript
|
|
49
|
+
import { Stream } from "@soffinal/stream";
|
|
50
|
+
|
|
51
|
+
const events = new Stream<string>();
|
|
52
|
+
|
|
53
|
+
events.listen(console.log);
|
|
54
|
+
|
|
55
|
+
events.push("Hello"); //log: Hello
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Examples
|
|
59
|
+
|
|
45
60
|
```typescript
|
|
46
61
|
import { Stream, State, filter, map, merge } from "@soffinal/stream";
|
|
47
62
|
|
|
48
|
-
// Create
|
|
63
|
+
// Create streams
|
|
49
64
|
const events = new Stream<string>();
|
|
50
65
|
const numbers = new Stream<number>();
|
|
51
66
|
|
|
52
|
-
//
|
|
67
|
+
// Pull-based stream from async generator
|
|
68
|
+
const websocketStream = new Stream(async function* () {
|
|
69
|
+
const ws = new WebSocket("ws://localhost:8080");
|
|
70
|
+
while (ws.readyState === WebSocket.OPEN) {
|
|
71
|
+
yield await new Promise((resolve) => {
|
|
72
|
+
ws.onmessage = (event) => resolve(JSON.parse(event.data));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Simple transformations
|
|
53
78
|
const processed = events
|
|
54
|
-
.pipe(
|
|
55
|
-
.pipe(
|
|
79
|
+
.pipe(filter((msg) => msg.length > 3)) // Simple filtering
|
|
80
|
+
.pipe(map((msg) => msg.toUpperCase())); // Transform to uppercase
|
|
81
|
+
|
|
82
|
+
// Async transformations with concurrency
|
|
83
|
+
const validated = events.pipe(
|
|
84
|
+
filter(
|
|
85
|
+
async (msg) => {
|
|
86
|
+
const isValid = await validateAsync(msg);
|
|
87
|
+
return isValid;
|
|
88
|
+
},
|
|
89
|
+
{ strategy: "concurrent-ordered" }
|
|
90
|
+
) // Parallel validation, ordered results
|
|
91
|
+
);
|
|
56
92
|
|
|
57
93
|
// Stateful transformers that learn and adapt
|
|
58
94
|
const runningAverage = numbers
|
|
@@ -75,6 +111,12 @@ const runningAverage = numbers
|
|
|
75
111
|
})
|
|
76
112
|
);
|
|
77
113
|
|
|
114
|
+
// Automatic cleanup with DOM elements
|
|
115
|
+
const element = document.createElement('div');
|
|
116
|
+
events.listen(value => {
|
|
117
|
+
element.textContent = value;
|
|
118
|
+
}, element); // Auto-removed when element is GC'd
|
|
119
|
+
|
|
78
120
|
// Copy-paste transformers from JSDoc
|
|
79
121
|
const limited = numbers.pipe(take(5)); // Limit to 5 items
|
|
80
122
|
const indexed = events.pipe(withIndex()); // Add indices
|
|
@@ -82,6 +124,7 @@ const delayed = processed.pipe(delay(100)); // Delay each value
|
|
|
82
124
|
|
|
83
125
|
// Multiple consumers
|
|
84
126
|
processed.listen((msg) => console.log("Processed:", msg));
|
|
127
|
+
validated.listen((msg) => console.log("Validated:", msg));
|
|
85
128
|
runningAverage.listen(({ value, average }) => console.log(`Value: ${value}, Running Average: ${average}`));
|
|
86
129
|
|
|
87
130
|
// Reactive state
|
|
@@ -150,20 +193,69 @@ for await (const event of userEvents) {
|
|
|
150
193
|
}
|
|
151
194
|
```
|
|
152
195
|
|
|
196
|
+
### Automatic Listener Cleanup
|
|
197
|
+
|
|
198
|
+
Stream provides three cleanup mechanisms to prevent memory leaks:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
const stream = new Stream<string>();
|
|
202
|
+
|
|
203
|
+
// 1. Manual cleanup
|
|
204
|
+
const cleanup = stream.listen(value => console.log(value));
|
|
205
|
+
cleanup(); // Remove listener
|
|
206
|
+
|
|
207
|
+
// 2. AbortSignal cleanup
|
|
208
|
+
const controller = new AbortController();
|
|
209
|
+
stream.listen(value => console.log(value), controller.signal);
|
|
210
|
+
controller.abort(); // Remove listener
|
|
211
|
+
|
|
212
|
+
// 3. WeakRef automatic cleanup (NEW!)
|
|
213
|
+
const element = document.createElement('div');
|
|
214
|
+
stream.listen(value => {
|
|
215
|
+
element.textContent = value;
|
|
216
|
+
}, element);
|
|
217
|
+
// Listener automatically removed when element is garbage collected
|
|
218
|
+
// Perfect for DOM elements, components, and temporary objects
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**WeakRef Benefits:**
|
|
222
|
+
- Zero memory leaks with DOM elements
|
|
223
|
+
- No manual cleanup needed
|
|
224
|
+
- Works with any object (components, instances, etc.)
|
|
225
|
+
- Leverages JavaScript's garbage collector
|
|
226
|
+
- Ideal for UI frameworks (React, Vue, Svelte, etc.)
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// Real-world example: Component lifecycle
|
|
230
|
+
function createComponent() {
|
|
231
|
+
const element = document.createElement('div');
|
|
232
|
+
const dataStream = new Stream<Data>();
|
|
233
|
+
|
|
234
|
+
// Auto-cleanup when component unmounts
|
|
235
|
+
dataStream.listen(data => {
|
|
236
|
+
element.innerHTML = renderTemplate(data);
|
|
237
|
+
}, element);
|
|
238
|
+
|
|
239
|
+
return element;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// When element is removed from DOM and GC'd, listener is automatically cleaned up
|
|
243
|
+
```
|
|
244
|
+
|
|
153
245
|
### Pipe: Stream-to-Stream Composition
|
|
154
246
|
|
|
155
|
-
The `pipe` method enforces
|
|
247
|
+
The `pipe` method enforces composition - it only accepts functions that return Stream instances, maintaining the infinite pipeline:
|
|
156
248
|
|
|
157
249
|
```typescript
|
|
158
250
|
// All transformers return Streams - infinite chaining
|
|
159
|
-
stream.pipe(filter(
|
|
160
|
-
stream.pipe(map(
|
|
251
|
+
stream.pipe(filter((v) => v > 0)); // → Stream<T>
|
|
252
|
+
stream.pipe(map((v) => v.toString())); // → Stream<string>
|
|
161
253
|
stream.pipe(toState("initial")); // → State<string> (extends Stream)
|
|
162
254
|
|
|
163
255
|
// Infinite chaining - every pipe returns a Stream
|
|
164
256
|
const result = stream
|
|
165
|
-
.pipe(filter(
|
|
166
|
-
.pipe(map(
|
|
257
|
+
.pipe(filter((v) => v > 0))
|
|
258
|
+
.pipe(map((v) => v * 2))
|
|
167
259
|
.pipe(take(5))
|
|
168
260
|
.pipe(delay(100))
|
|
169
261
|
.pipe(distinct()); // Always chainable
|
|
@@ -177,8 +269,8 @@ const result = stream
|
|
|
177
269
|
const numbers = new Stream<number>();
|
|
178
270
|
|
|
179
271
|
// TypeScript knows these are all Streams
|
|
180
|
-
const doubled = numbers.pipe(map(
|
|
181
|
-
const strings = numbers.pipe(map(
|
|
272
|
+
const doubled = numbers.pipe(map((n) => n * 2)); // Stream<number>
|
|
273
|
+
const strings = numbers.pipe(map((n) => n.toString())); // Stream<string>
|
|
182
274
|
const state = numbers.pipe(toState(0)); // State<number>
|
|
183
275
|
```
|
|
184
276
|
|
|
@@ -186,13 +278,27 @@ const state = numbers.pipe(toState(0)); // State<number>
|
|
|
186
278
|
|
|
187
279
|
All stream operations are built from four universal primitives with **Adaptive Constraints**:
|
|
188
280
|
|
|
189
|
-
#### 1. Filter
|
|
281
|
+
#### 1. Filter
|
|
190
282
|
|
|
191
283
|
```typescript
|
|
192
284
|
import { filter } from "@soffinal/stream";
|
|
193
285
|
|
|
194
286
|
// Simple filtering
|
|
195
|
-
stream.pipe(filter(
|
|
287
|
+
stream.pipe(filter((value) => value > 0));
|
|
288
|
+
|
|
289
|
+
// Type guard filtering
|
|
290
|
+
stream.pipe(filter((value): value is number => typeof value === "number"));
|
|
291
|
+
|
|
292
|
+
// Async filtering with concurrency strategies
|
|
293
|
+
stream.pipe(
|
|
294
|
+
filter(
|
|
295
|
+
async (value) => {
|
|
296
|
+
const isValid = await validateAsync(value);
|
|
297
|
+
return isValid;
|
|
298
|
+
},
|
|
299
|
+
{ strategy: "concurrent-ordered" }
|
|
300
|
+
) // Parallel validation, ordered results
|
|
301
|
+
);
|
|
196
302
|
|
|
197
303
|
// Stateful filtering with termination
|
|
198
304
|
stream.pipe(
|
|
@@ -201,14 +307,6 @@ stream.pipe(
|
|
|
201
307
|
return [value > 0, { count: state.count + 1 }];
|
|
202
308
|
})
|
|
203
309
|
);
|
|
204
|
-
|
|
205
|
-
// Async filtering
|
|
206
|
-
stream.pipe(
|
|
207
|
-
filter({}, async (_, value) => {
|
|
208
|
-
const isValid = await validateAsync(value);
|
|
209
|
-
return [isValid, {}];
|
|
210
|
-
})
|
|
211
|
-
);
|
|
212
310
|
```
|
|
213
311
|
|
|
214
312
|
**[📖 Complete Filter Documentation →](src/transformers/filter.md)**
|
|
@@ -219,7 +317,21 @@ stream.pipe(
|
|
|
219
317
|
import { map } from "@soffinal/stream";
|
|
220
318
|
|
|
221
319
|
// Simple transformation
|
|
222
|
-
stream.pipe(map(
|
|
320
|
+
stream.pipe(map((value) => value * 2));
|
|
321
|
+
|
|
322
|
+
// Type transformation
|
|
323
|
+
stream.pipe(map((value: number) => value.toString()));
|
|
324
|
+
|
|
325
|
+
// Async transformation with concurrency strategies
|
|
326
|
+
stream.pipe(
|
|
327
|
+
map(
|
|
328
|
+
async (value) => {
|
|
329
|
+
const enriched = await enrichWithAPI(value);
|
|
330
|
+
return enriched;
|
|
331
|
+
},
|
|
332
|
+
{ strategy: "concurrent-unordered" }
|
|
333
|
+
) // Parallel processing, results as completed
|
|
334
|
+
);
|
|
223
335
|
|
|
224
336
|
// Stateful transformation with context
|
|
225
337
|
stream.pipe(
|
|
@@ -228,14 +340,6 @@ stream.pipe(
|
|
|
228
340
|
return [{ value, runningSum: newSum }, { sum: newSum }];
|
|
229
341
|
})
|
|
230
342
|
);
|
|
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
|
-
);
|
|
239
343
|
```
|
|
240
344
|
|
|
241
345
|
**[📖 Complete Map Documentation →](src/transformers/map.md)**
|
|
@@ -273,10 +377,12 @@ const arrayStream = new Stream<number[]>();
|
|
|
273
377
|
const individualNumbers = arrayStream.pipe(flat());
|
|
274
378
|
|
|
275
379
|
arrayStream.push([1, 2, 3]); // Emits: 1, 2, 3 as separate events
|
|
380
|
+
// Type: Stream<number>
|
|
276
381
|
|
|
277
382
|
// Configurable depth flattening
|
|
278
383
|
const deepArrays = new Stream<number[][][]>();
|
|
279
384
|
const flattened = deepArrays.pipe(flat(2)); // Flatten 2 levels deep
|
|
385
|
+
// Type: Stream<number>
|
|
280
386
|
```
|
|
281
387
|
|
|
282
388
|
**[📖 Complete Flat Documentation →](src/transformers/flat.md)**
|
|
@@ -285,9 +391,7 @@ const flattened = deepArrays.pipe(flat(2)); // Flatten 2 levels deep
|
|
|
285
391
|
|
|
286
392
|
No separate repos, no CLI tools, no package management - just copy-paste ready transformers embedded in JSDoc!
|
|
287
393
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
#### The Educational Transparency Revolution
|
|
394
|
+
#### The Educational Transparency
|
|
291
395
|
|
|
292
396
|
Our approach makes **every implementation pattern visible and learnable**:
|
|
293
397
|
|
|
@@ -299,7 +403,6 @@ Our approach makes **every implementation pattern visible and learnable**:
|
|
|
299
403
|
const searchInput = new Stream<string>(); // ← Hover here for full library
|
|
300
404
|
const searchResults = searchInput
|
|
301
405
|
.pipe(distinct()) // Copy from Stream JSDoc - learn deduplication patterns
|
|
302
|
-
.pipe(simpleFilter((q) => q.length > 2)) // Copy from Stream JSDoc - learn filtering logic
|
|
303
406
|
.pipe(take(10)) // Copy from Stream JSDoc - learn termination patterns
|
|
304
407
|
.pipe(delay(300)) // Copy from Stream JSDoc - learn async transformation
|
|
305
408
|
.pipe(simpleMap((query) => searchAPI(query))); // Copy from Stream JSDoc - learn mapping patterns
|
|
@@ -383,8 +486,6 @@ Documentation-as-Distribution creates **multiplicative value**:
|
|
|
383
486
|
|
|
384
487
|
- `take(n)`, `skip(n)`, `distinct()`, `tap(fn)` - Essential filtering patterns
|
|
385
488
|
- `withIndex()`, `delay(ms)`, `pluck(key)`, `scan(fn, initial)` - Common transformation patterns
|
|
386
|
-
- `simpleFilter(predicate)` - Convenient filtering without state
|
|
387
|
-
- `simpleMap(fn)` - Convenient mapping without state
|
|
388
489
|
- `toState(initialValue)` - Convert streams to reactive state
|
|
389
490
|
- More transformers added with each release!
|
|
390
491
|
|
|
@@ -425,20 +526,20 @@ counter.value = 5;
|
|
|
425
526
|
|
|
426
527
|
// State from transformed streams
|
|
427
528
|
const source = new Stream<number>();
|
|
428
|
-
const derivedState = new State(0, source.pipe(map(
|
|
529
|
+
const derivedState = new State(0, source.pipe(map((v) => v * 2)));
|
|
429
530
|
|
|
430
531
|
// Derived state using transformers
|
|
431
|
-
const isLoggedIn = user.pipe(map(
|
|
532
|
+
const isLoggedIn = user.pipe(map((u) => u !== null));
|
|
432
533
|
|
|
433
534
|
const userDisplayName = user.pipe(
|
|
434
|
-
filter(
|
|
435
|
-
map(
|
|
535
|
+
filter((u) => u !== null),
|
|
536
|
+
map((u) => `${u.firstName} ${u.lastName}`)
|
|
436
537
|
);
|
|
437
538
|
|
|
438
539
|
// Convert streams to state with toState transformer
|
|
439
540
|
const processedState = source
|
|
440
|
-
.pipe(filter(
|
|
441
|
-
.pipe(map(
|
|
541
|
+
.pipe(filter((v) => v > 0))
|
|
542
|
+
.pipe(map((v) => v.toString()))
|
|
442
543
|
.pipe(toState("0")); // Explicit initial value
|
|
443
544
|
|
|
444
545
|
// Automatic UI updates
|
|
@@ -502,9 +603,10 @@ activeUsers.add("user1");
|
|
|
502
603
|
|
|
503
604
|
#### Core Methods
|
|
504
605
|
|
|
505
|
-
- `push(...values: T[]): void` - Emit values to all listeners
|
|
506
|
-
- `listen(callback: (value: T) => void,
|
|
507
|
-
- `pipe<OUTPUT
|
|
606
|
+
- `push(...values: T[]): void` - Emit values to all listeners (auto-removes GC'd listeners)
|
|
607
|
+
- `listen(callback: (value: T) => void, context?: AbortSignal | Stream<any> | object): () => void` - Add listener with optional cleanup
|
|
608
|
+
- `pipe<OUTPUT extends Stream<any>>(transformer: (stream: this) => OUTPUT): OUTPUT` - Apply any transformer
|
|
609
|
+
- `withContext(context: object): AsyncIterator<T>` - Async iterator bound to context lifetime
|
|
508
610
|
|
|
509
611
|
#### Async Interface
|
|
510
612
|
|
|
@@ -530,18 +632,21 @@ activeUsers.add("user1");
|
|
|
530
632
|
|
|
531
633
|
### Universal Transformers
|
|
532
634
|
|
|
533
|
-
#### filter(
|
|
635
|
+
#### filter(predicate, options?)
|
|
534
636
|
|
|
535
|
-
- **Simple**: `filter(
|
|
536
|
-
- **
|
|
537
|
-
- **Async**: `filter(
|
|
637
|
+
- **Simple**: `filter((value) => boolean)`
|
|
638
|
+
- **Type Guard**: `filter((value): value is Type => boolean)` (sync only)
|
|
639
|
+
- **Async**: `filter(async (value) => boolean, { strategy? })` with concurrency options
|
|
640
|
+
- **Stateful**: `filter(state, (state, value) => [boolean, newState])` (always sequential)
|
|
538
641
|
- **Termination**: Return `undefined` to terminate stream
|
|
642
|
+
- **Strategies**: `"sequential"` | `"concurrent-unordered"` | `"concurrent-ordered"`
|
|
539
643
|
|
|
540
|
-
#### map(
|
|
644
|
+
#### map(mapper, options?)
|
|
541
645
|
|
|
542
|
-
- **Simple**: `map(
|
|
543
|
-
- **
|
|
544
|
-
- **
|
|
646
|
+
- **Simple**: `map((value) => newValue)`
|
|
647
|
+
- **Async**: `map(async (value) => newValue, { strategy? })` with concurrency options
|
|
648
|
+
- **Stateful**: `map(state, (state, value) => [newValue, newState])` (always sequential)
|
|
649
|
+
- **Strategies**: `"sequential"` | `"concurrent-unordered"` | `"concurrent-ordered"`
|
|
545
650
|
|
|
546
651
|
#### merge(...streams)
|
|
547
652
|
|
|
@@ -589,10 +694,11 @@ activeUsers.add("user1");
|
|
|
589
694
|
- **Fast startup** - Zero dependencies, instant initialization
|
|
590
695
|
- **Efficient pipelines** - Optimized transformer composition
|
|
591
696
|
- **Memory bounded** - Built-in backpressure handling
|
|
697
|
+
- **Automatic cleanup** - WeakRef prevents memory leaks
|
|
592
698
|
|
|
593
699
|
## Runtime Support
|
|
594
700
|
|
|
595
|
-
- **Modern browsers** supporting ES2020+
|
|
701
|
+
- **Modern browsers** supporting ES2020+ (WeakRef support)
|
|
596
702
|
- **Node.js** 16+
|
|
597
703
|
- **Deno** 1.0+
|
|
598
704
|
- **Bun** 1.0+
|
|
@@ -620,20 +726,11 @@ stream.push("hello");
|
|
|
620
726
|
|
|
621
727
|
### Transformer Guides
|
|
622
728
|
|
|
623
|
-
- **[Filter Transformer →](src/transformers/filter.md)** -
|
|
624
|
-
- **[Map Transformer →](src/transformers/map.md)** -
|
|
729
|
+
- **[Filter Transformer →](src/transformers/filter.md)** - Concurrency strategies, type guards, stateful filtering, and stream termination
|
|
730
|
+
- **[Map Transformer →](src/transformers/map.md)** - Concurrency strategies, type transformations, stateful mapping, and performance optimization
|
|
625
731
|
- **[Merge Transformer →](src/transformers/merge.md)** - Stream orchestration and type-safe combination
|
|
626
732
|
- **[Flat Transformer →](src/transformers/flat.md)** - Event multiplication and array flattening
|
|
627
733
|
|
|
628
|
-
### Philosophy
|
|
629
|
-
|
|
630
|
-
**Adaptive Reactive Programming** - A new paradigm where transformers maintain state and evolve their behavior based on stream history. This enables:
|
|
631
|
-
|
|
632
|
-
- **Learning transformers** that adapt to data patterns
|
|
633
|
-
- **Stateful operations** with memory between events
|
|
634
|
-
- **Stream termination** for lifecycle control
|
|
635
|
-
- **Zero-overhead types** with perfect inference
|
|
636
|
-
|
|
637
734
|
## Contributing
|
|
638
735
|
|
|
639
736
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class z{_listeners=new Map;_generatorFn;_generator;_listenerAdded;_listenerRemoved;constructor(Z){this._generatorFn=Z instanceof z?()=>Z[Symbol.asyncIterator]():Z}get hasListeners(){return this._listeners.size>0}get listenerAdded(){if(!this._listenerAdded)this._listenerAdded=new z;return this._listenerAdded}get listenerRemoved(){if(!this._listenerRemoved)this._listenerRemoved=new z;return this._listenerRemoved}async*[Symbol.asyncIterator](){let Z=[],G,J=this.listen((Q)=>{Z.push(Q),G?.()});try{while(!0)if(Z.length)yield Z.shift();else await new Promise((Q)=>G=Q)}finally{J(),Z.length=0,G=void 0;return}}push(Z,...G){G.unshift(Z);let J=[];for(let Q of G)for(let[K,X]of this._listeners){if(X&&!X.deref()){J.push(K);continue}K(Q)}for(let Q of J)this._listeners.delete(Q)}async*withContext(Z){let G=new WeakRef(Z);try{for await(let J of this){if(!G.deref())break;yield J}}finally{return}}listen(Z,G){let J=this,Q,K;if(G instanceof AbortSignal){if(G?.aborted)return()=>{};G?.addEventListener("abort",X),Q=()=>G?.removeEventListener("abort",X)}else if(G instanceof z)Q=G?.listen(X);else if(G)K=new WeakRef(G);if(J._listeners.set(Z,K),J._listenerAdded?.push(),J._generatorFn&&J._listeners.size===1)J._generator=J._generatorFn(),(async()=>{for await(let Y of J._generator)J.push(Y)})();return X;function X(){if(J._listeners.delete(Z),J._listenerRemoved?.push(),J._listeners.size===0)J._generator?.return(),J._generator=void 0;Q?.(),Q=void 0,K=void 0}}then(Z){return new Promise((G)=>{let J=this.listen((Q)=>{G(Q),J()})}).then(Z)}pipe(Z){return Z(this)}}var V=(Z,G)=>{return(J)=>{if(!G||typeof G==="object"){let{strategy:K="sequential"}=G??{},X=Z;if(K==="sequential")return new z(async function*(){for await(let Y of J){let $=await X(Y);if($)yield Y;if($===void 0)return}});if(K==="concurrent-unordered")return new z(async function*(){let Y=Symbol.for("__abort"),$=[],_,H=J.listen(async(W)=>{let j=await X(W);if(j!==!1)j===void 0?$.push(Y):$.push(W),_?.(),_=void 0});try{while(!0)if($.length){let W=$.shift();if(W===Y)break;yield W}else await new Promise((W)=>_=W)}finally{$.length=0,H(),_=void 0}});if(K==="concurrent-ordered")return new z(async function*(){let Y=[],$,_=J.listen((H)=>{let W=X(H);Y.push({resultPromise:W,value:H}),(async()=>{await W,$?.(),$=void 0})()});try{while(!0)if(Y.length){let{resultPromise:H,value:W}=Y.shift(),j=await H;if(j)yield W;if(j===void 0)break}else await new Promise((H)=>$=H)}finally{Y.length=0,_(),$=void 0}})}let Q=G;return new z(async function*(){let K=Z;for await(let X of J){let Y=await Q(K,X);if(!Y)return;let[$,_]=Y;if(K=_,$)yield X}})}};var I=(Z,G)=>{return(J)=>{if(!G||typeof G==="object"){let{strategy:K="sequential"}=G??{},X=Z;if(K==="sequential")return new z(async function*(){for await(let Y of J)yield await X(Y)});if(K==="concurrent-unordered")return new z(async function*(){let Y=[],$,_=J.listen(async(H)=>{Y.push(await X(H)),$?.(),$=void 0});try{while(!0)if(Y.length)yield Y.shift();else await new Promise((H)=>$=H)}finally{Y.length=0,_(),$=void 0}});if(K==="concurrent-ordered")return new z(async function*(){let Y=[],$,_=J.listen((H)=>{let W=X(H);Y.push(W),(async()=>{await W,$?.(),$=void 0})()});try{while(!0)if(Y.length)yield await Y.shift();else await new Promise((H)=>$=H)}finally{Y.length=0,_(),$=void 0}})}let Q=G;return new z(async function*(){let K=Z;for await(let X of J){let[Y,$]=await Q(K,X);K=$,yield Y}})}};function q(...Z){return(G)=>new z(async function*(){let J=[G,...Z],Q=[],K,X=J.map((Y)=>Y.listen(($)=>{Q.push($),K?.()}));try{while(!0)if(Q.length)yield Q.shift();else await new Promise((Y)=>K=Y)}finally{X.forEach((Y)=>Y())}})}function A(Z=0){return(G)=>{return new z(async function*(){for await(let J of G)if(Array.isArray(J)){let Q=J.flat(Z);for(let K=0;K<Q.length;K++)yield Q[K]}else yield J})}}class B{_items=[];_insertStream;_deleteStream;_clearStream;constructor(Z){if(Z)this._items=[...Z];let G=this;function J(K,X){if(X===0)return 0;return K<0?(K%X+X)%X:K%X}this.insert=new Proxy((K,X)=>{let Y=K<0?Math.max(0,G._items.length+K+1):Math.min(K,G._items.length);return G._items.splice(Y,0,X),G._insertStream?.push([Y,X]),Q},{get(K,X){if(X in K)return K[X];if(!G._insertStream)G._insertStream=new z;return G._insertStream[X]}}),this.delete=new Proxy((K)=>{if(K<0||K>=G._items.length)return;let X=G._items.splice(K,1)[0];return G._deleteStream?.push([K,X]),X},{get(K,X){if(X in K)return K[X];if(!G._deleteStream)G._deleteStream=new z;return G._deleteStream[X]}}),this.clear=new Proxy(()=>{if(G._items.length>0)G._items.length=0,G._clearStream?.push()},{get(K,X){if(X in K)return K[X];if(!G._clearStream)G._clearStream=new z;return G._clearStream[X]}});let Q=new Proxy(this,{get(K,X){if(typeof X==="string"&&/^-?\d+$/.test(X)){let Y=parseInt(X);if(K._items.length===0)return;let $=J(Y,K._items.length);return K._items[$]}return K[X]},set(K,X,Y){if(typeof X==="string"&&/^-?\d+$/.test(X)){let $=parseInt(X);if(K._items.length===0)return K._items.push(Y),K._insertStream?.push([0,Y]),!0;let _=J($,K._items.length);if(K._items[_]!==Y)K._items[_]=Y,K._insertStream?.push([_,Y]);return!0}return K[X]=Y,!0}});return Q}get(Z){return this._items[Z]}get length(){return this._items.length}values(){return this._items[Symbol.iterator]()}[Symbol.iterator](){return this._items[Symbol.iterator]()}}class D extends globalThis.Map{_setStream;_deleteStream;_clearStream;constructor(Z){super(Z);let G=this;this.set=new Proxy((J,Q)=>{if(globalThis.Map.prototype.has.call(G,J)&&globalThis.Map.prototype.get.call(G,J)===Q)return G;return globalThis.Map.prototype.set.call(G,J,Q),G._setStream?.push([J,Q]),G},{get(J,Q){if(Q in J)return J[Q];if(!G._setStream)G._setStream=new z;return G._setStream[Q]}}),this.delete=new Proxy((J)=>{if(!globalThis.Map.prototype.has.call(G,J))return!1;let Q=globalThis.Map.prototype.get.call(G,J);return globalThis.Map.prototype.delete.call(G,J),G._deleteStream?.push([J,Q]),!0},{get(J,Q){if(Q in J)return J[Q];if(!G._deleteStream)G._deleteStream=new z;return G._deleteStream[Q]}}),this.clear=new Proxy(()=>{if(G.size>0)globalThis.Map.prototype.clear.call(G),G._clearStream?.push()},{get(J,Q){if(Q in J)return J[Q];if(!G._clearStream)G._clearStream=new z;return G._clearStream[Q]}})}}class F extends globalThis.Set{_addStream;_deleteStream;_clearStream;constructor(Z){super(Z);let G=this;this.add=new Proxy((J)=>{if(globalThis.Set.prototype.has.call(G,J))return G;return globalThis.Set.prototype.add.call(G,J),G._addStream?.push(J),G},{get(J,Q){if(Q in J)return J[Q];if(!G._addStream)G._addStream=new z;return G._addStream[Q]}}),this.delete=new Proxy((J)=>{if(!globalThis.Set.prototype.has.call(G,J))return!1;return globalThis.Set.prototype.delete.call(G,J),G._deleteStream?.push(J),!0},{get(J,Q){if(Q in J)return J[Q];if(!G._deleteStream)G._deleteStream=new z;return G._deleteStream[Q]}}),this.clear=new Proxy(()=>{if(G.size>0)globalThis.Set.prototype.clear.call(G),G._clearStream?.push()},{get(J,Q){if(Q in J)return J[Q];if(!G._clearStream)G._clearStream=new z;return G._clearStream[Q]}})}}class N extends z{_value;constructor(Z,G){super(G);this._value=Z}push(...Z){for(let G of Z)this.value=G}get value(){return this._value}set value(Z){this._value=Z,super.push(Z)}}export{q as merge,I as map,A as flat,V as filter,z as Stream,N as State,F as Set,D as Map,B as List};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=652D0058F0A0040264756E2164756E21
|
|
4
4
|
//# sourceMappingURL=index.js.map
|