@peerbit/stream 3.0.11 → 3.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.
@@ -0,0 +1,385 @@
1
+ import defer from "p-defer";
2
+ import GenericFIFO from "fast-fifo";
3
+
4
+ export class AbortError extends Error {
5
+ type: string;
6
+ code: string;
7
+
8
+ constructor(message?: string, code?: string) {
9
+ super(message ?? "The operation was aborted");
10
+ this.type = "aborted";
11
+ this.code = code ?? "ABORT_ERR";
12
+ }
13
+ }
14
+
15
+ export interface AbortOptions {
16
+ signal?: AbortSignal;
17
+ }
18
+
19
+ /**
20
+ * An iterable that you can push values into.
21
+ */
22
+ export interface PushableLanes<T, R = void, N = unknown>
23
+ extends AsyncGenerator<T, R, N> {
24
+ /**
25
+ * End the iterable after all values in the buffer (if any) have been yielded. If an
26
+ * error is passed the buffer is cleared immediately and the next iteration will
27
+ * throw the passed error
28
+ */
29
+ end(err?: Error): this;
30
+
31
+ /**
32
+ * Push a value into the iterable. Values are yielded from the iterable in the order
33
+ * they are pushed. Values not yet consumed from the iterable are buffered.
34
+ */
35
+ push(value: T, lane?: number): this;
36
+
37
+ /**
38
+ * Returns a promise that resolves when the underlying queue becomes empty (e.g.
39
+ * this.readableLength === 0).
40
+ *
41
+ * If an AbortSignal is passed as an option and that signal aborts, it only
42
+ * causes the returned promise to reject - it does not end the pushable.
43
+ */
44
+ onEmpty(options?: AbortOptions): Promise<void>;
45
+
46
+ /**
47
+ * This property contains the total number of bytes in the queue ready to be read.
48
+ *
49
+ */
50
+
51
+ get readableLength(): number;
52
+
53
+ /**
54
+ * Get readable length for specific lane
55
+ * @param lane
56
+ */
57
+ getReadableLength(lane?: number): number;
58
+ }
59
+
60
+ export interface Options {
61
+ /**
62
+ * A function called after *all* values have been yielded from the iterator (including
63
+ * buffered values). In the case when the iterator is ended with an error it will be
64
+ * passed the error as a parameter.
65
+ */
66
+ onEnd?(err?: Error): void;
67
+
68
+ /**
69
+ * How many lanes, lane 0 is fastest and will drain before lane 1 is consumed
70
+ */
71
+ lanes?: number;
72
+ }
73
+
74
+ export interface DoneResult {
75
+ done: true;
76
+ }
77
+ export interface ValueResult<T> {
78
+ done: false;
79
+ value: T;
80
+ }
81
+ export type NextResult<T> = ValueResult<T> | DoneResult;
82
+
83
+ export interface Next<T> {
84
+ done?: boolean;
85
+ error?: Error;
86
+ value?: T;
87
+ }
88
+
89
+ /**
90
+ * Fifo but with total readableLength counter
91
+ */
92
+ class Uint8ArrayFifo<T extends { byteLength: number }> extends GenericFIFO<
93
+ Next<T>
94
+ > {
95
+ size: number = 0;
96
+ push(val: Next<T>): void {
97
+ if (val.value) {
98
+ this.size += val.value.byteLength;
99
+ }
100
+ return super.push(val);
101
+ }
102
+
103
+ shift(): Next<T> | undefined {
104
+ const shifted = super.shift();
105
+ if (shifted?.value) {
106
+ this.size -= shifted.value.byteLength;
107
+ }
108
+ return shifted;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * A queue consisting of multiple 'lanes' with different priority to be emptied.
114
+ * The lane with index 0 will empty before lane with index 1 etc..
115
+ * TODO add an additional proprty to control whether we we pick objects from slower lanes
116
+ * so no lane get really "stuck"
117
+ */
118
+ class Uint8arrayPriorityQueue<T extends { byteLength: number }> {
119
+ lanes: Uint8ArrayFifo<T>[];
120
+ constructor(options: { lanes: number } = { lanes: 1 }) {
121
+ this.lanes = new Array(options.lanes);
122
+ for (let i = 0; i < this.lanes.length; i++) {
123
+ this.lanes[i] = new Uint8ArrayFifo();
124
+ }
125
+ }
126
+
127
+ get size() {
128
+ let sum = 0;
129
+ for (const lane of this.lanes) {
130
+ sum += lane.size;
131
+ }
132
+ return sum;
133
+ }
134
+ push(val: Next<T>, lane: number) {
135
+ return this.lanes[lane].push(val);
136
+ }
137
+ shift(): Next<T> | undefined {
138
+ // fetch the first non undefined item.
139
+ // by iterating from index 0 up we define that lanes with lower index have higher prioirity
140
+ for (const lane of this.lanes) {
141
+ const element = lane.shift();
142
+ if (element) {
143
+ return element;
144
+ }
145
+ }
146
+ return undefined;
147
+ }
148
+ isEmpty(): boolean {
149
+ for (const lane of this.lanes) {
150
+ if (!lane.isEmpty()) {
151
+ return false;
152
+ }
153
+ }
154
+ return true;
155
+ }
156
+ }
157
+
158
+ export function pushableLanes<T extends { byteLength: number } = Uint8Array>(
159
+ options: Options = {}
160
+ ): PushableLanes<T> {
161
+ return _pushable<Uint8Array, T, PushableLanes<T>>(options);
162
+ }
163
+
164
+ // Modified from https://github.com/alanshaw/it-pushable
165
+ function _pushable<PushType extends Uint8Array, ValueType, ReturnType>(
166
+ options?: Options
167
+ ): ReturnType {
168
+ options = options ?? {};
169
+ let onEnd = options.onEnd;
170
+ let buffer: Uint8arrayPriorityQueue<PushType> | Uint8ArrayFifo<PushType> =
171
+ new Uint8arrayPriorityQueue<PushType>(
172
+ options.lanes ? { lanes: options.lanes } : undefined
173
+ );
174
+ let pushable: any;
175
+ let onNext: ((next: Next<PushType>, lane: number) => ReturnType) | null;
176
+ let ended: boolean;
177
+ let drain = defer();
178
+
179
+ const getNext = (): NextResult<ValueType> => {
180
+ const next: Next<PushType> | undefined = buffer.shift();
181
+
182
+ if (next == null) {
183
+ return { done: true };
184
+ }
185
+
186
+ if (next.error != null) {
187
+ throw next.error;
188
+ }
189
+
190
+ return {
191
+ done: next.done === true,
192
+ // @ts-expect-error if done is false, value will be present
193
+ value: next.value
194
+ };
195
+ };
196
+
197
+ const waitNext = async (): Promise<NextResult<ValueType>> => {
198
+ try {
199
+ if (!buffer.isEmpty()) {
200
+ return getNext();
201
+ }
202
+
203
+ if (ended) {
204
+ return { done: true };
205
+ }
206
+
207
+ return await new Promise<NextResult<ValueType>>((resolve, reject) => {
208
+ onNext = (next: Next<PushType>, lane: number) => {
209
+ onNext = null;
210
+ buffer.push(next, lane);
211
+
212
+ try {
213
+ resolve(getNext());
214
+ } catch (err) {
215
+ reject(err);
216
+ }
217
+
218
+ return pushable;
219
+ };
220
+ });
221
+ } finally {
222
+ if (buffer.isEmpty()) {
223
+ // settle promise in the microtask queue to give consumers a chance to
224
+ // await after calling .push
225
+ queueMicrotask(() => {
226
+ drain.resolve();
227
+ drain = defer();
228
+ });
229
+ }
230
+ }
231
+ };
232
+
233
+ const bufferNext = (next: Next<PushType>, lane: number): ReturnType => {
234
+ if (onNext != null) {
235
+ return onNext(next, lane);
236
+ }
237
+
238
+ buffer.push(next, lane);
239
+ return pushable;
240
+ };
241
+
242
+ const bufferError = (err: Error): ReturnType => {
243
+ buffer = new Uint8ArrayFifo();
244
+
245
+ if (onNext != null) {
246
+ return onNext({ error: err }, 0);
247
+ }
248
+
249
+ buffer.push({ error: err });
250
+ return pushable;
251
+ };
252
+
253
+ const push = (value: PushType, lane: number = 0): ReturnType => {
254
+ if (ended) {
255
+ return pushable;
256
+ }
257
+
258
+ return bufferNext({ done: false, value }, lane);
259
+ };
260
+ const end = (err?: Error): ReturnType => {
261
+ if (ended) return pushable;
262
+ ended = true;
263
+
264
+ return err != null ? bufferError(err) : bufferNext({ done: true }, 0);
265
+ };
266
+ const _return = (): DoneResult => {
267
+ buffer = new Uint8ArrayFifo();
268
+ end();
269
+
270
+ return { done: true };
271
+ };
272
+ const _throw = (err: Error): DoneResult => {
273
+ end(err);
274
+
275
+ return { done: true };
276
+ };
277
+
278
+ pushable = {
279
+ [Symbol.asyncIterator]() {
280
+ return this;
281
+ },
282
+ next: waitNext,
283
+ return: _return,
284
+ throw: _throw,
285
+ push,
286
+ end,
287
+ get readableLength(): number {
288
+ return buffer.size;
289
+ },
290
+
291
+ getReadableLength(lane?: number): number {
292
+ if (lane == null) {
293
+ return buffer.size;
294
+ }
295
+
296
+ if (buffer instanceof Uint8arrayPriorityQueue) {
297
+ return buffer.lanes[lane].size;
298
+ }
299
+ throw new Error("Missing lane info");
300
+ },
301
+ onEmpty: async (options?: AbortOptions) => {
302
+ const signal = options?.signal;
303
+ signal?.throwIfAborted();
304
+
305
+ if (buffer.isEmpty()) {
306
+ return;
307
+ }
308
+
309
+ let cancel: Promise<void> | undefined;
310
+ let listener: (() => void) | undefined;
311
+
312
+ if (signal != null) {
313
+ cancel = new Promise((resolve, reject) => {
314
+ listener = () => {
315
+ reject(new AbortError());
316
+ };
317
+
318
+ signal.addEventListener("abort", listener);
319
+ });
320
+ }
321
+
322
+ try {
323
+ await Promise.race([drain.promise, cancel]);
324
+ } finally {
325
+ if (listener != null && signal != null) {
326
+ signal?.removeEventListener("abort", listener);
327
+ }
328
+ }
329
+ }
330
+ };
331
+
332
+ if (onEnd == null) {
333
+ return pushable;
334
+ }
335
+
336
+ const _pushable = pushable;
337
+
338
+ pushable = {
339
+ [Symbol.asyncIterator]() {
340
+ return this;
341
+ },
342
+ next() {
343
+ return _pushable.next();
344
+ },
345
+ throw(err: Error) {
346
+ _pushable.throw(err);
347
+
348
+ if (onEnd != null) {
349
+ onEnd(err);
350
+ onEnd = undefined;
351
+ }
352
+
353
+ return { done: true };
354
+ },
355
+ return() {
356
+ _pushable.return();
357
+
358
+ if (onEnd != null) {
359
+ onEnd();
360
+ onEnd = undefined;
361
+ }
362
+
363
+ return { done: true };
364
+ },
365
+ push,
366
+ end(err: Error) {
367
+ _pushable.end(err);
368
+
369
+ if (onEnd != null) {
370
+ onEnd(err);
371
+ onEnd = undefined;
372
+ }
373
+
374
+ return pushable;
375
+ },
376
+ get readableLength() {
377
+ return _pushable.readableLength;
378
+ },
379
+ onEmpty: (opts?: AbortOptions) => {
380
+ return _pushable.onEmpty(opts);
381
+ }
382
+ };
383
+
384
+ return pushable;
385
+ }