@peerbit/stream 5.0.0 → 5.0.1-3dcfc85
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/dist/src/index.d.ts +46 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +248 -46
- package/dist/src/index.js.map +1 -1
- package/dist/src/pushable-lanes.d.ts +11 -0
- package/dist/src/pushable-lanes.d.ts.map +1 -1
- package/dist/src/pushable-lanes.js +73 -3
- package/dist/src/pushable-lanes.js.map +1 -1
- package/package.json +10 -9
- package/src/index.ts +414 -82
- package/src/pushable-lanes.ts +98 -3
package/src/pushable-lanes.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// - Lane indices are 0..(lanes-1). Defaults to lane 0.
|
|
22
22
|
import { AbortError } from "@peerbit/time";
|
|
23
23
|
import GenericFIFO from "fast-fifo";
|
|
24
|
-
import defer from "p-defer";
|
|
24
|
+
import defer, { type DeferredPromise } from "p-defer";
|
|
25
25
|
|
|
26
26
|
export interface AbortOptions {
|
|
27
27
|
signal?: AbortSignal;
|
|
@@ -56,6 +56,13 @@ export interface PushableLanes<T, R = void, N = unknown>
|
|
|
56
56
|
*/
|
|
57
57
|
onEmpty(options?: AbortOptions): Promise<void>;
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Resolves when the total buffered bytes are at or below `limitBytes`.
|
|
61
|
+
* If an AbortSignal is given and it aborts, only this promise rejects;
|
|
62
|
+
* the pushable itself is not ended.
|
|
63
|
+
*/
|
|
64
|
+
onBufferedBelow(limitBytes: number, options?: AbortOptions): Promise<void>;
|
|
65
|
+
|
|
59
66
|
/** Total number of bytes buffered (across all lanes). */
|
|
60
67
|
get readableLength(): number;
|
|
61
68
|
|
|
@@ -90,6 +97,12 @@ export interface Options {
|
|
|
90
97
|
*/
|
|
91
98
|
onPush?(value: { byteLength: number }, lane: number): void;
|
|
92
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Optional hook invoked whenever the total buffered bytes change.
|
|
102
|
+
* Useful for external backpressure coordination.
|
|
103
|
+
*/
|
|
104
|
+
onBufferSize?(bufferedBytes: number): void;
|
|
105
|
+
|
|
93
106
|
/**
|
|
94
107
|
* Fairness mode:
|
|
95
108
|
* - 'priority': strict priority (original behavior).
|
|
@@ -320,9 +333,34 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
320
333
|
let onNext: ((next: Next<PushType>, lane: number) => ReturnType) | null;
|
|
321
334
|
let ended = false;
|
|
322
335
|
let drain = defer<void>();
|
|
336
|
+
const bufferedBelowWaiters = new Set<{
|
|
337
|
+
limitBytes: number;
|
|
338
|
+
deferred: DeferredPromise<void>;
|
|
339
|
+
}>();
|
|
323
340
|
|
|
324
341
|
const maxBytes = options.maxBufferedBytes;
|
|
325
342
|
const overflow: OverflowPolicy = options.overflow ?? "throw";
|
|
343
|
+
const notifyBufferSize = () => {
|
|
344
|
+
options?.onBufferSize?.(totalBufferedBytes());
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const notifyBufferedBelowWaiters = () => {
|
|
348
|
+
if (bufferedBelowWaiters.size === 0) return;
|
|
349
|
+
const size = totalBufferedBytes();
|
|
350
|
+
for (const waiter of [...bufferedBelowWaiters]) {
|
|
351
|
+
if (size <= waiter.limitBytes) {
|
|
352
|
+
bufferedBelowWaiters.delete(waiter);
|
|
353
|
+
waiter.deferred.resolve();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const resolveBufferedBelowWaiters = () => {
|
|
359
|
+
for (const waiter of [...bufferedBelowWaiters]) {
|
|
360
|
+
bufferedBelowWaiters.delete(waiter);
|
|
361
|
+
waiter.deferred.resolve();
|
|
362
|
+
}
|
|
363
|
+
};
|
|
326
364
|
|
|
327
365
|
const getNext = (): NextResult<ValueType> => {
|
|
328
366
|
const next: Next<PushType> | undefined = buffer.shift();
|
|
@@ -362,6 +400,8 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
362
400
|
};
|
|
363
401
|
});
|
|
364
402
|
} finally {
|
|
403
|
+
notifyBufferSize();
|
|
404
|
+
notifyBufferedBelowWaiters();
|
|
365
405
|
// If buffer is empty after this turn, resolve the drain promise (in a microtask)
|
|
366
406
|
if (buffer.isEmpty()) {
|
|
367
407
|
queueMicrotask(() => {
|
|
@@ -383,6 +423,8 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
383
423
|
const bufferError = (err: Error): ReturnType => {
|
|
384
424
|
// swap to ByteFifo to deliver a single terminal error
|
|
385
425
|
buffer = new ByteFifo<PushType>();
|
|
426
|
+
notifyBufferSize();
|
|
427
|
+
notifyBufferedBelowWaiters();
|
|
386
428
|
if (onNext != null) {
|
|
387
429
|
return onNext({ error: err }, 0);
|
|
388
430
|
}
|
|
@@ -421,18 +463,26 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
421
463
|
value,
|
|
422
464
|
clampLane(lane, isLaneQueue(buffer) ? buffer.lanes.length : 1),
|
|
423
465
|
);
|
|
466
|
+
notifyBufferSize();
|
|
424
467
|
return out;
|
|
425
468
|
};
|
|
426
469
|
|
|
427
470
|
const end = (err?: Error): ReturnType => {
|
|
428
471
|
if (ended) return pushable;
|
|
429
472
|
ended = true;
|
|
473
|
+
queueMicrotask(() => {
|
|
474
|
+
drain.resolve();
|
|
475
|
+
drain = defer<void>();
|
|
476
|
+
resolveBufferedBelowWaiters();
|
|
477
|
+
});
|
|
430
478
|
return err != null ? bufferError(err) : bufferNext({ done: true }, 0);
|
|
431
479
|
};
|
|
432
480
|
|
|
433
481
|
const _return = (): DoneResult => {
|
|
434
482
|
// Ensure prompt termination
|
|
435
483
|
buffer = new ByteFifo<PushType>();
|
|
484
|
+
notifyBufferSize();
|
|
485
|
+
notifyBufferedBelowWaiters();
|
|
436
486
|
end();
|
|
437
487
|
return { done: true };
|
|
438
488
|
};
|
|
@@ -470,7 +520,7 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
470
520
|
const signal = opts?.signal;
|
|
471
521
|
signal?.throwIfAborted?.();
|
|
472
522
|
|
|
473
|
-
if (buffer.isEmpty()) return;
|
|
523
|
+
if (buffer.isEmpty() || ended) return;
|
|
474
524
|
|
|
475
525
|
let cancel: Promise<void> | undefined;
|
|
476
526
|
let listener: (() => void) | undefined;
|
|
@@ -483,8 +533,50 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
483
533
|
}
|
|
484
534
|
|
|
485
535
|
try {
|
|
486
|
-
await Promise.race(
|
|
536
|
+
await Promise.race(
|
|
537
|
+
cancel != null ? [drain.promise, cancel] : [drain.promise],
|
|
538
|
+
);
|
|
539
|
+
} finally {
|
|
540
|
+
if (listener != null) {
|
|
541
|
+
signal?.removeEventListener("abort", listener);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
onBufferedBelow: async (limitBytes: number, opts?: AbortOptions) => {
|
|
547
|
+
const signal = opts?.signal;
|
|
548
|
+
signal?.throwIfAborted?.();
|
|
549
|
+
const normalizedLimit = Math.max(0, Math.floor(limitBytes));
|
|
550
|
+
|
|
551
|
+
if (totalBufferedBytes() <= normalizedLimit || ended) return;
|
|
552
|
+
|
|
553
|
+
const waiter = {
|
|
554
|
+
limitBytes: normalizedLimit,
|
|
555
|
+
deferred: defer<void>(),
|
|
556
|
+
};
|
|
557
|
+
bufferedBelowWaiters.add(waiter);
|
|
558
|
+
|
|
559
|
+
let cancel: Promise<void> | undefined;
|
|
560
|
+
let listener: (() => void) | undefined;
|
|
561
|
+
|
|
562
|
+
if (signal != null) {
|
|
563
|
+
cancel = new Promise<void>((_resolve, reject) => {
|
|
564
|
+
listener = () => {
|
|
565
|
+
bufferedBelowWaiters.delete(waiter);
|
|
566
|
+
reject(new AbortError());
|
|
567
|
+
};
|
|
568
|
+
signal.addEventListener("abort", listener!);
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
try {
|
|
573
|
+
await Promise.race(
|
|
574
|
+
cancel != null
|
|
575
|
+
? [waiter.deferred.promise, cancel]
|
|
576
|
+
: [waiter.deferred.promise],
|
|
577
|
+
);
|
|
487
578
|
} finally {
|
|
579
|
+
bufferedBelowWaiters.delete(waiter);
|
|
488
580
|
if (listener != null) {
|
|
489
581
|
signal?.removeEventListener("abort", listener);
|
|
490
582
|
}
|
|
@@ -540,6 +632,9 @@ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
|
|
|
540
632
|
onEmpty(opts?: AbortOptions) {
|
|
541
633
|
return _pushable.onEmpty(opts);
|
|
542
634
|
},
|
|
635
|
+
onBufferedBelow(limitBytes: number, opts?: AbortOptions) {
|
|
636
|
+
return _pushable.onBufferedBelow(limitBytes, opts);
|
|
637
|
+
},
|
|
543
638
|
};
|
|
544
639
|
|
|
545
640
|
return pushable;
|