reactor-core-ts 2.0.0 → 2.1.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.
package/README.md CHANGED
@@ -12,7 +12,9 @@ A TypeScript implementation of [Reactive Streams](https://www.reactive-streams.o
12
12
  - [Filtering](#flux-filtering)
13
13
  - [Aggregation](#flux-aggregation)
14
14
  - [Combining](#flux-combining)
15
+ - [Backpressure](#flux-backpressure)
15
16
  - [Side effects](#flux-side-effects)
17
+ - [Error handling](#flux-error-handling)
16
18
  - [Scheduling](#flux-scheduling)
17
19
  - [Utilities](#flux-utilities)
18
20
  - [Subscribe](#flux-subscribe)
@@ -22,6 +24,7 @@ A TypeScript implementation of [Reactive Streams](https://www.reactive-streams.o
22
24
  - [Filtering](#mono-filtering)
23
25
  - [Combining](#mono-combining)
24
26
  - [Side effects](#mono-side-effects)
27
+ - [Error handling](#mono-error-handling)
25
28
  - [Scheduling](#mono-scheduling)
26
29
  - [Utilities](#mono-utilities)
27
30
  - [Subscribe](#mono-subscribe)
@@ -77,11 +80,18 @@ This library implements [Reactive Streams specification](https://www.reactive-st
77
80
  | `Flux.range(start, count)` | Emit integers `[start, start+count)`. |
78
81
  | `Flux.fromIterable(iterable)` | Emit all items from any `Iterable<T>`. |
79
82
  | `Flux.generate(sink => …)` | Imperative generator. Call `sink.next()` / `sink.complete()` / `sink.error()`. Delivery is deferred until downstream requests. |
83
+ | `Flux.create(emitter => …)` | Push-based bridge via [`FluxSink`](#fluxsink) — supports `onRequest`, `onCancel`, `onDispose` callbacks. Suitable for event listeners and callback APIs. |
80
84
  | `Flux.from(publisher)` | Wrap any `Publisher<T>` as a `Flux<T>`. |
81
85
  | `Flux.defer(factory)` | Lazily create a new Flux per subscription via the factory function. |
82
86
  | `Flux.empty()` | Complete immediately without emitting. |
83
87
  | `Flux.never()` | Never emit any signal. |
84
88
  | `Flux.error(err)` | Signal `onError` immediately. |
89
+ | `Flux.interval(ms)` | Emit an incrementing counter (0, 1, 2, …) at a fixed `ms` rate. Items are dropped when downstream has no demand. |
90
+ | `Flux.merge(...sources)` | Subscribe to all sources concurrently; items interleave as they arrive. |
91
+ | `Flux.zip(sources, combiner)` | Combine items from 2–4 sources positionally via `combiner`. Overloads for 2, 3, and 4 typed sources. |
92
+ | `Flux.combineLatest(sources, combiner)` | Emit a new value whenever any source emits, combining the **latest** values from each. Overloads for 2, 3, and 4 sources. |
93
+ | `Flux.firstWithValue(...sources)` | Race: emit the first value to arrive from any source, then cancel the rest. |
94
+ | `Flux.using(resourceSupplier, sourceFactory, cleanup)` | Manage a resource lifecycle: acquire, stream, release on termination. |
85
95
 
86
96
  ```typescript
87
97
  import { Flux } from 'reactor-core-ts';
@@ -117,6 +127,17 @@ Flux.generate<number>(sink => {
117
127
  | `.scanWith(seedFactory, reducer)` | Emit running accumulation starting from an explicit seed value. |
118
128
  | `.cast<R>()` | Unsafe type cast — changes the declared element type without any runtime conversion. |
119
129
  | `.indexed()` | Pair each item with its zero-based index: `Flux<[number, T]>`. |
130
+ | `.groupBy(keyFn)` | Route items into `GroupedFlux<K, T>` streams keyed by `keyFn(item)`. Each group is a self-contained `Flux`. |
131
+ | `.expand(fn)` | Breadth-first recursive expansion — each item is expanded via `fn`, which returns a `Publisher<T>` of child items. |
132
+ | `.sample(trigger)` | Emit the latest item seen whenever `trigger` emits. |
133
+ | `.bufferTimeout(maxSize, ms)` | Collect items into arrays; flush when the buffer reaches `maxSize` or `ms` elapses. |
134
+ | `.elapsed()` | Pair each item with the milliseconds elapsed since the previous item: `Flux<[number, T]>`. |
135
+ | `.timestamp()` | Pair each item with the current timestamp in ms: `Flux<[number, T]>`. |
136
+ | `.materialize()` | Wrap each signal (`next`, `error`, `complete`) as a `Signal<T>` item. |
137
+ | `.dematerialize()` | Restore a `Flux<Signal<R>>` back into a live reactive stream. |
138
+ | `.transformDeferred(fn)` | Apply a reusable operator function lazily per subscription. |
139
+ | `.delayUntil(triggerFn)` | Hold each item until the trigger publisher returned by `triggerFn(item)` emits or completes. |
140
+ | `.ofType(constructor)` | Keep only items that are `instanceof` the given constructor, with a typed cast to `R`. |
120
141
 
121
142
  ```typescript
122
143
  Flux.just(1, 2, 3)
@@ -220,6 +241,10 @@ Flux.just(3, 1, 4, 1, 5)
220
241
  | `.concatWith(other)` | Append `other` after `this` completes (sequential). |
221
242
  | `.mergeWith(other)` | Merge `other` with `this` concurrently; items interleave as they arrive. |
222
243
  | `.zipWith(other, combiner)` | Pair items from `this` and `other` one-by-one using `combiner(a, b) → V`. |
244
+ | `Flux.merge(...sources)` | Static N-source merge — subscribe to all and forward items as they arrive. |
245
+ | `Flux.zip(sources, combiner)` | Static positional combine: waits for one item from each source, then calls `combiner`. |
246
+ | `Flux.combineLatest(sources, combiner)` | Emit a combined value whenever any source emits; uses the latest from all others. |
247
+ | `Flux.firstWithValue(...sources)` | Race: emits the first value from any source, cancelling all others. |
223
248
 
224
249
  ```typescript
225
250
  Flux.just(1, 2).concatWith(Flux.just(3, 4)).subscribe(v => console.log(v));
@@ -228,8 +253,25 @@ Flux.just(1, 2).concatWith(Flux.just(3, 4)).subscribe(v => console.log(v));
228
253
  Flux.just(1, 2, 3).zipWith(Flux.just('a', 'b', 'c'), (n, s) => `${n}${s}`)
229
254
  .subscribe(v => console.log(v));
230
255
  // '1a' '2b' '3c'
256
+
257
+ Flux.zip([Flux.just(1, 2), Flux.just('a', 'b')], (n, s) => `${n}${s}`)
258
+ .subscribe(v => console.log(v));
259
+ // '1a' '2b'
260
+
261
+ Flux.combineLatest([Flux.just(1), Flux.just(2)], (a, b) => a + b)
262
+ .subscribe(v => console.log(v));
263
+ // 3
231
264
  ```
232
265
 
266
+ ### Flux Backpressure
267
+
268
+ | Method | Description |
269
+ |---|---|
270
+ | `.onBackpressureBuffer(maxSize?)` | Buffer all items when downstream has no demand. Signals error when `maxSize` is exceeded. |
271
+ | `.onBackpressureDrop(onDrop?)` | Silently drop items when downstream has no demand. Optional `onDrop(item)` callback. |
272
+ | `.onBackpressureLatest()` | Keep only the latest item when downstream has no demand; older items are discarded. |
273
+ | `.limitRate(n)` | Prefetch items from upstream in batches of `n`, replenishing at 75% consumption. |
274
+
233
275
  ### Flux Side Effects
234
276
 
235
277
  These operators observe signals without modifying the stream.
@@ -244,6 +286,22 @@ These operators observe signals without modifying the stream.
244
286
  | `.doOnCancel(fn)` | Run `fn` when the subscription is cancelled. |
245
287
  | `.doFinally(fn)` | Run `fn` after the stream terminates for any reason (complete, error, or cancel). |
246
288
  | `.doOnSubscribe(fn)` | Run `fn` when `onSubscribe` is received, receiving the `Subscription` object. |
289
+ | `.doOnRequest(fn)` | Run `fn(n)` each time downstream calls `request(n)`. |
290
+ | `.doOnEach(fn)` | Run `fn(signal)` for every signal — next, error, and complete — as a `Signal<T>`. |
291
+ | `.log(label?)` | Console-log every signal with an optional label prefix. |
292
+
293
+ ### Flux Error Handling
294
+
295
+ | Method | Description |
296
+ |---|---|
297
+ | `.retry(maxRetries?)` | Re-subscribe on error up to `maxRetries` times (default: unbounded). |
298
+ | `.retryWhen(fn)` | Controlled retry — each error is pushed to a `Flux<Error>` returned by `fn`; re-subscribe when the control emits. |
299
+ | `.repeatWhen(fn)` | Controlled repeat — each completion is pushed to a `Flux<void>`; re-subscribe when the control emits. |
300
+ | `.onErrorReturn(replacement)` | On error, switch to `replacement` publisher. |
301
+ | `.onErrorResume(fn)` | On error, switch to the publisher returned by `fn(error)`. |
302
+ | `.onErrorMap(fn)` | Transform the error without changing the stream type. |
303
+ | `.onErrorContinue(predicate)` | If `predicate(err)` is `true`, complete; otherwise re-throw. |
304
+ | `.timeout(ms, fallback?)` | Error with `TimeoutError` if no item arrives within `ms`; optionally switch to `fallback`. |
247
305
 
248
306
  ```typescript
249
307
  Flux.just(1, 2, 3)
@@ -277,13 +335,34 @@ Flux.just('a', 'b', 'c')
277
335
 
278
336
  | Method | Description |
279
337
  |---|---|
280
- | `.retry(maxRetries?)` | Re-subscribe on error up to `maxRetries` times (default: unbounded). |
281
338
  | `.cache()` | Subscribe to the source once; replay all items to subsequent subscribers. |
282
339
  | `.switchIfEmpty(alternative)` | Switch to `alternative` if source completes without emitting. |
283
- | `.onErrorReturn(replacement)` | On error, switch to `replacement` publisher. |
284
- | `.onErrorContinue(predicate)` | If `predicate(err)` is `true`, complete; otherwise re-throw. |
340
+ | `.delaySubscription(ms)` | Delay the actual subscription to the source by `ms` milliseconds. |
285
341
  | `.pipe(producer, onRequest, onUnsubscribe)` | Low-level escape hatch for building custom downstream operators. |
286
342
 
343
+ ### FluxSink
344
+
345
+ `Flux.create(emitter => …)` hands the `emitter` callback a `FluxSink<T>` with:
346
+
347
+ | Member | Description |
348
+ |---|---|
349
+ | `.next(value)` | Push a value. Buffered when downstream has no demand. |
350
+ | `.error(err)` | Terminate with an error. |
351
+ | `.complete()` | Complete normally. |
352
+ | `.requested` | Current downstream demand count. |
353
+ | `.onRequest(fn)` | Register a callback invoked each time downstream requests more items. |
354
+ | `.onCancel(fn)` | Register a cancellation callback. |
355
+ | `.onDispose(fn)` | Register a callback invoked on any terminal event (cancel, error, complete). |
356
+
357
+ ```typescript
358
+ Flux.create<number>(sink => {
359
+ const id = setInterval(() => {
360
+ if (sink.requested > 0) sink.next(Math.random());
361
+ }, 100);
362
+ sink.onDispose(() => clearInterval(id));
363
+ }).take(5).subscribe(v => console.log(v));
364
+ ```
365
+
287
366
  ### Flux Subscribe
288
367
 
289
368
  ```typescript
@@ -326,10 +405,14 @@ sub.unsubscribe(); // cancel at any time
326
405
  | `Mono.justOrEmpty(value)` | Emit `value` if not `null`/`undefined`, otherwise complete empty. |
327
406
  | `Mono.fromPromise(promise)` | Wrap a `Promise<T>` — resolves to `onNext` + `onComplete`, rejects to `onError`. |
328
407
  | `Mono.generate(sink => …)` | Imperative generator. Call `sink.next()` exactly once (or `sink.error()`/`sink.complete()`). |
408
+ | `Mono.create(sink => …)` | Alias for `Mono.generate`. |
409
+ | `Mono.fromCallable(fn)` | Lazy synchronous factory — calls `fn()` at subscribe time; propagates throws as `onError`. |
410
+ | `Mono.delay(ms)` | Emit `0` after `ms` milliseconds. |
329
411
  | `Mono.from(publisher)` | Wrap any `Publisher<T>` as a `Mono<T>` (takes only the first item). |
330
412
  | `Mono.defer(factory)` | Lazily create a new Mono per subscription via the factory function. |
331
413
  | `Mono.empty()` | Complete immediately without emitting. |
332
414
  | `Mono.error(err)` | Signal `onError` immediately. |
415
+ | `Mono.firstWithValue(...sources)` | Race: emit the first value from any of the given `Mono` sources. |
333
416
 
334
417
  ```typescript
335
418
  import { Mono } from 'reactor-core-ts';
@@ -356,6 +439,9 @@ Mono.justOrEmpty(null).subscribe(
356
439
  | `.mapNotNull(fn)` | Transform `T → R | null | undefined`; if result is null/undefined, complete empty. |
357
440
  | `.flatMap(fn)` | Map the value to a `Mono<R>`, then subscribe to that inner Mono. |
358
441
  | `.flatMapMany(fn)` | Map the value to a `Flux<R>` or `Mono<R>`, returning a `Flux<R>`. |
442
+ | `.thenReturn(value)` | Ignore the emitted value; emit `value` after source completes. |
443
+ | `.delayElement(ms)` | Delay the emitted value by `ms` milliseconds. |
444
+ | `.delayUntil(triggerFn)` | Hold the emitted value until the trigger publisher fires, then forward it. |
359
445
  | `.cast<R>()` | Unsafe type cast. |
360
446
 
361
447
  ```typescript
@@ -396,7 +482,29 @@ Mono.just(42)
396
482
 
397
483
  ### Mono Side Effects
398
484
 
399
- Same as Flux: `.doOnNext`, `.doOnError`, `.doOnSubscribe`, `.doFirst`, `.doFinally`.
485
+ | Method | Description |
486
+ |---|---|
487
+ | `.doOnSuccess(fn)` | Run `fn(value)` when the Mono emits a value. Exceptions propagate as `onError`. |
488
+ | `.doFirst(fn)` | Run `fn` before the first item is emitted. |
489
+ | `.doOnNext(fn)` | Run `fn` for the emitted item. |
490
+ | `.doOnError(fn)` | Run `fn` when `onError` is received. |
491
+ | `.doOnSubscribe(fn)` | Run `fn` when `onSubscribe` is received. |
492
+ | `.doFinally(fn)` | Run `fn` after the stream terminates (complete or error). |
493
+ | `.doOnRequest(fn)` | Run `fn(n)` when downstream calls `request(n)`. |
494
+ | `.doOnEach(fn)` | Run `fn(signal)` for every signal as a `Signal<T>`. |
495
+ | `.log(label?)` | Console-log every signal. |
496
+
497
+ ### Mono Error Handling
498
+
499
+ | Method | Description |
500
+ |---|---|
501
+ | `.retry(maxRetries?)` | Re-subscribe on error up to `maxRetries` times. |
502
+ | `.retryWhen(fn)` | Controlled retry — each error is pushed to a `Flux<Error>`; re-subscribe when the control emits. |
503
+ | `.onErrorReturn(replacement)` | On error, switch to `replacement` publisher. |
504
+ | `.onErrorResume(fn)` | On error, switch to the publisher returned by `fn(error)`. |
505
+ | `.onErrorMap(fn)` | Transform the error. |
506
+ | `.onErrorComplete(predicate?)` | Convert an error to a normal completion (optionally filtered by `predicate`). |
507
+ | `.timeout(ms, fallback?)` | Error with `TimeoutError` if no item arrives within `ms`. |
400
508
 
401
509
  ### Mono Scheduling
402
510
 
@@ -408,9 +516,9 @@ Same as Flux: `.publishOn(scheduler)`, `.subscribeOn(scheduler)`.
408
516
  |---|---|
409
517
  | `.hasElement()` | `Mono<boolean>` — `true` if a value is emitted, `false` if it completes empty. |
410
518
  | `.toPromise()` | `Promise<T | null>` — resolves with the emitted value or `null` if empty. |
519
+ | `.toFuture()` | Alias for `.toPromise()`. |
520
+ | `.or(other)` | Fall back to `other` Mono if this completes empty. |
411
521
  | `.switchIfEmpty(alternative)` | Switch to `alternative` Mono if this completes empty. |
412
- | `.onErrorReturn(replacement)` | On error, switch to `replacement` publisher. |
413
- | `.retry(maxRetries?)` | Re-subscribe on error. |
414
522
  | `.pipe(producer, onRequest, onUnsubscribe)` | Low-level custom operator builder. |
415
523
 
416
524
  ### Mono Subscribe
@@ -598,10 +706,10 @@ The convenience callback overloads issue `request(Number.MAX_SAFE_INTEGER)` for
598
706
  ```bash
599
707
  git clone https://github.com/CKATEPTb/reactor-core-ts.git
600
708
  cd reactor-core-ts
601
- pnpm install
709
+ npm install
602
710
 
603
- pnpm run build # compile
604
- pnpm test # run all tests (Jest + TCK)
711
+ npm run build # compile
712
+ npm run test # run all tests (Jest + TCK)
605
713
  ```
606
714
 
607
715
  Feel free to open issues and submit pull requests.
@@ -612,4 +720,4 @@ Feel free to open issues and submit pull requests.
612
720
 
613
721
  LGPL-3.0-only. See [LICENSE.md](LICENSE.md) for details.
614
722
 
615
- **Author**: CKATEPTb
723
+ **Author**: [CKATEPTb](https://github.com/CKATEPTb)