@yume-chan/stream-extra 0.0.17 → 0.0.19
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/CHANGELOG.json +33 -9
- package/CHANGELOG.md +15 -2
- package/LICENSE +1 -1
- package/README.md +35 -3
- package/esm/buffered-transform.d.ts +11 -10
- package/esm/buffered-transform.d.ts.map +1 -1
- package/esm/buffered-transform.js +55 -49
- package/esm/buffered-transform.js.map +1 -1
- package/esm/buffered.d.ts +29 -27
- package/esm/buffered.d.ts.map +1 -1
- package/esm/buffered.js +128 -130
- package/esm/buffered.js.map +1 -1
- package/esm/chunk.d.ts +7 -4
- package/esm/chunk.d.ts.map +1 -1
- package/esm/chunk.js +48 -7
- package/esm/chunk.js.map +1 -1
- package/esm/consumable.d.ts +53 -0
- package/esm/consumable.d.ts.map +1 -0
- package/esm/consumable.js +178 -0
- package/esm/consumable.js.map +1 -0
- package/esm/decode-utf8.d.ts +4 -4
- package/esm/decode-utf8.d.ts.map +1 -1
- package/esm/decode-utf8.js +11 -11
- package/esm/decode-utf8.js.map +1 -1
- package/esm/distribution.d.ts +24 -0
- package/esm/distribution.d.ts.map +1 -0
- package/esm/distribution.js +99 -0
- package/esm/distribution.js.map +1 -0
- package/esm/duplex.d.ts +48 -47
- package/esm/duplex.d.ts.map +1 -1
- package/esm/duplex.js +90 -88
- package/esm/duplex.js.map +1 -1
- package/esm/gather-string.d.ts +6 -6
- package/esm/gather-string.d.ts.map +1 -1
- package/esm/gather-string.js +15 -13
- package/esm/gather-string.js.map +1 -1
- package/esm/index.d.ts +16 -15
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +16 -15
- package/esm/index.js.map +1 -1
- package/esm/inspect.d.ts +4 -4
- package/esm/inspect.js +11 -11
- package/esm/pipe-from.d.ts +12 -12
- package/esm/pipe-from.d.ts.map +1 -1
- package/esm/pipe-from.js +23 -26
- package/esm/pipe-from.js.map +1 -1
- package/esm/push-readable.d.ts +19 -11
- package/esm/push-readable.d.ts.map +1 -1
- package/esm/push-readable.js +61 -46
- package/esm/push-readable.js.map +1 -1
- package/esm/split-string.d.ts +4 -4
- package/esm/split-string.d.ts.map +1 -1
- package/esm/split-string.js +24 -24
- package/esm/split-string.js.map +1 -1
- package/esm/stream.d.ts +25 -17
- package/esm/stream.d.ts.map +1 -1
- package/esm/stream.js +26 -3
- package/esm/stream.js.map +1 -1
- package/esm/struct-deserialize.d.ts +6 -6
- package/esm/struct-deserialize.d.ts.map +1 -1
- package/esm/struct-deserialize.js +8 -8
- package/esm/struct-deserialize.js.map +1 -1
- package/esm/struct-serialize.d.ts +5 -5
- package/esm/struct-serialize.d.ts.map +1 -1
- package/esm/struct-serialize.js +10 -10
- package/esm/struct-serialize.js.map +1 -1
- package/esm/trace.d.ts +16 -0
- package/esm/trace.d.ts.map +1 -0
- package/esm/trace.js +131 -0
- package/esm/trace.js.map +1 -0
- package/esm/wrap-readable.d.ts +21 -20
- package/esm/wrap-readable.d.ts.map +1 -1
- package/esm/wrap-readable.js +56 -56
- package/esm/wrap-readable.js.map +1 -1
- package/esm/wrap-writable.d.ts +14 -12
- package/esm/wrap-writable.d.ts.map +1 -1
- package/esm/wrap-writable.js +62 -52
- package/esm/wrap-writable.js.map +1 -1
- package/package.json +13 -9
- package/src/buffered-transform.ts +32 -17
- package/src/buffered.ts +14 -15
- package/src/consumable.ts +281 -0
- package/src/decode-utf8.ts +3 -2
- package/src/distribution.ts +113 -0
- package/src/duplex.ts +27 -10
- package/src/gather-string.ts +5 -3
- package/src/index.ts +16 -15
- package/src/inspect.ts +1 -1
- package/src/pipe-from.ts +11 -10
- package/src/push-readable.ts +77 -46
- package/src/split-string.ts +5 -2
- package/src/stream.ts +48 -6
- package/src/struct-deserialize.ts +6 -4
- package/src/struct-serialize.ts +4 -2
- package/src/wrap-readable.ts +30 -12
- package/src/wrap-writable.ts +34 -11
- package/tsconfig.build.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/native.d.ts +0 -240
- package/esm/native.d.ts.map +0 -1
- package/esm/native.js +0 -60
- package/esm/native.js.map +0 -1
- package/src/chunk.ts +0 -15
- package/src/native.ts +0 -362
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ConsumableTransformStream } from "./consumable.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Splits or combines buffers to specified size.
|
|
5
|
+
*/
|
|
6
|
+
export class BufferCombiner {
|
|
7
|
+
private _capacity: number;
|
|
8
|
+
private readonly _buffer: Uint8Array;
|
|
9
|
+
private _offset: number;
|
|
10
|
+
private _available: number;
|
|
11
|
+
|
|
12
|
+
public constructor(size: number) {
|
|
13
|
+
this._capacity = size;
|
|
14
|
+
this._buffer = new Uint8Array(size);
|
|
15
|
+
this._offset = 0;
|
|
16
|
+
this._available = size;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Pushes data to the combiner.
|
|
21
|
+
* @param data The input data to be split or combined.
|
|
22
|
+
* @returns
|
|
23
|
+
* A generator that yields buffers of specified size.
|
|
24
|
+
* It may yield the same buffer multiple times, consume the data before calling `next`.
|
|
25
|
+
*/
|
|
26
|
+
public *push(data: Uint8Array): Generator<Uint8Array, void, void> {
|
|
27
|
+
let offset = 0;
|
|
28
|
+
let available = data.byteLength;
|
|
29
|
+
|
|
30
|
+
if (this._offset !== 0) {
|
|
31
|
+
if (available >= this._available) {
|
|
32
|
+
this._buffer.set(
|
|
33
|
+
data.subarray(0, this._available),
|
|
34
|
+
this._offset
|
|
35
|
+
);
|
|
36
|
+
offset += this._available;
|
|
37
|
+
available -= this._available;
|
|
38
|
+
|
|
39
|
+
yield this._buffer;
|
|
40
|
+
this._offset = 0;
|
|
41
|
+
this._available = this._capacity;
|
|
42
|
+
|
|
43
|
+
if (available === 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
this._buffer.set(data, this._offset);
|
|
48
|
+
this._offset += available;
|
|
49
|
+
this._available -= available;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
while (available >= this._capacity) {
|
|
55
|
+
const end = offset + this._capacity;
|
|
56
|
+
yield data.subarray(offset, end);
|
|
57
|
+
offset = end;
|
|
58
|
+
available -= this._capacity;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (available > 0) {
|
|
62
|
+
this._buffer.set(data.subarray(offset), this._offset);
|
|
63
|
+
this._offset += available;
|
|
64
|
+
this._available -= available;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public flush(): Uint8Array | undefined {
|
|
69
|
+
if (this._offset === 0) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const output = this._buffer.subarray(0, this._offset);
|
|
74
|
+
this._offset = 0;
|
|
75
|
+
this._available = this._capacity;
|
|
76
|
+
return output;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class DistributionStream extends ConsumableTransformStream<
|
|
81
|
+
Uint8Array,
|
|
82
|
+
Uint8Array
|
|
83
|
+
> {
|
|
84
|
+
public constructor(size: number, combine = false) {
|
|
85
|
+
const combiner = combine ? new BufferCombiner(size) : undefined;
|
|
86
|
+
super({
|
|
87
|
+
async transform(chunk, controller) {
|
|
88
|
+
if (combiner) {
|
|
89
|
+
for (const buffer of combiner.push(chunk)) {
|
|
90
|
+
await controller.enqueue(buffer);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
let offset = 0;
|
|
94
|
+
let available = chunk.byteLength;
|
|
95
|
+
while (available > 0) {
|
|
96
|
+
const end = offset + size;
|
|
97
|
+
await controller.enqueue(chunk.subarray(offset, end));
|
|
98
|
+
offset = end;
|
|
99
|
+
available -= size;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
async flush(controller) {
|
|
104
|
+
if (combiner) {
|
|
105
|
+
const data = combiner.flush();
|
|
106
|
+
if (data) {
|
|
107
|
+
await controller.enqueue(data);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
package/src/duplex.ts
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
import { PromiseResolver } from "@yume-chan/async";
|
|
2
2
|
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
ReadableStream,
|
|
6
|
+
ReadableStreamDefaultController,
|
|
7
|
+
WritableStreamDefaultWriter,
|
|
8
|
+
} from "./stream.js";
|
|
9
|
+
import { WritableStream } from "./stream.js";
|
|
4
10
|
import { WrapReadableStream } from "./wrap-readable.js";
|
|
5
11
|
|
|
12
|
+
const NOOP = () => {
|
|
13
|
+
// no-op
|
|
14
|
+
};
|
|
15
|
+
|
|
6
16
|
export interface DuplexStreamFactoryOptions {
|
|
7
17
|
/**
|
|
8
18
|
* Callback when any `ReadableStream` is cancelled (the user doesn't need any more data),
|
|
9
19
|
* or `WritableStream` is ended (the user won't produce any more data),
|
|
10
20
|
* or `DuplexStreamFactory#close` is called.
|
|
11
21
|
*
|
|
12
|
-
* Usually you want to let the other peer know that the duplex stream should be
|
|
22
|
+
* Usually you want to let the other peer know that the duplex stream should be closed.
|
|
13
23
|
*
|
|
14
24
|
* `dispose` will automatically be called after `close` completes,
|
|
15
25
|
* but if you want to wait another peer for a close confirmation and call
|
|
@@ -40,10 +50,14 @@ export class DuplexStreamFactory<R, W> {
|
|
|
40
50
|
private writers: WritableStreamDefaultWriter<W>[] = [];
|
|
41
51
|
|
|
42
52
|
private _writableClosed = false;
|
|
43
|
-
public get writableClosed() {
|
|
53
|
+
public get writableClosed() {
|
|
54
|
+
return this._writableClosed;
|
|
55
|
+
}
|
|
44
56
|
|
|
45
57
|
private _closed = new PromiseResolver<void>();
|
|
46
|
-
public get closed() {
|
|
58
|
+
public get closed() {
|
|
59
|
+
return this._closed.promise;
|
|
60
|
+
}
|
|
47
61
|
|
|
48
62
|
private options: DuplexStreamFactoryOptions;
|
|
49
63
|
|
|
@@ -58,7 +72,7 @@ export class DuplexStreamFactory<R, W> {
|
|
|
58
72
|
return readable;
|
|
59
73
|
},
|
|
60
74
|
cancel: async () => {
|
|
61
|
-
// cancel means the local peer
|
|
75
|
+
// cancel means the local peer wants to close the connection.
|
|
62
76
|
await this.close();
|
|
63
77
|
},
|
|
64
78
|
close: async () => {
|
|
@@ -76,7 +90,6 @@ export class DuplexStreamFactory<R, W> {
|
|
|
76
90
|
// So it only triggers `close`.
|
|
77
91
|
return new WritableStream<W>({
|
|
78
92
|
write: async (chunk) => {
|
|
79
|
-
await writer.ready;
|
|
80
93
|
await writer.write(chunk);
|
|
81
94
|
},
|
|
82
95
|
abort: async (reason) => {
|
|
@@ -84,7 +97,7 @@ export class DuplexStreamFactory<R, W> {
|
|
|
84
97
|
await this.close();
|
|
85
98
|
},
|
|
86
99
|
close: async () => {
|
|
87
|
-
|
|
100
|
+
await writer.close().catch(NOOP);
|
|
88
101
|
await this.close();
|
|
89
102
|
},
|
|
90
103
|
});
|
|
@@ -97,13 +110,13 @@ export class DuplexStreamFactory<R, W> {
|
|
|
97
110
|
this._writableClosed = true;
|
|
98
111
|
|
|
99
112
|
// Call `close` first, so it can still write data to `WritableStream`s.
|
|
100
|
-
if (await this.options.close?.() !== false) {
|
|
113
|
+
if ((await this.options.close?.()) !== false) {
|
|
101
114
|
// `close` can return `false` to disable automatic `dispose`.
|
|
102
115
|
await this.dispose();
|
|
103
116
|
}
|
|
104
117
|
|
|
105
118
|
for (const writer of this.writers) {
|
|
106
|
-
|
|
119
|
+
await writer.close().catch(NOOP);
|
|
107
120
|
}
|
|
108
121
|
}
|
|
109
122
|
|
|
@@ -112,7 +125,11 @@ export class DuplexStreamFactory<R, W> {
|
|
|
112
125
|
this._closed.resolve();
|
|
113
126
|
|
|
114
127
|
for (const controller of this.readableControllers) {
|
|
115
|
-
try {
|
|
128
|
+
try {
|
|
129
|
+
controller.close();
|
|
130
|
+
} catch {
|
|
131
|
+
// ignore
|
|
132
|
+
}
|
|
116
133
|
}
|
|
117
134
|
|
|
118
135
|
await this.options.dispose?.();
|
package/src/gather-string.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { WritableStream } from "./stream.js";
|
|
2
2
|
|
|
3
|
-
export class GatherStringStream extends WritableStream<string>{
|
|
3
|
+
export class GatherStringStream extends WritableStream<string> {
|
|
4
4
|
// PERF: rope (concat strings) is faster than `[].join('')`
|
|
5
|
-
private _result =
|
|
6
|
-
public get result() {
|
|
5
|
+
private _result = "";
|
|
6
|
+
public get result() {
|
|
7
|
+
return this._result;
|
|
8
|
+
}
|
|
7
9
|
|
|
8
10
|
public constructor() {
|
|
9
11
|
super({
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
11
|
-
export * from
|
|
12
|
-
export * from
|
|
13
|
-
export * from
|
|
14
|
-
export * from
|
|
15
|
-
export * from
|
|
1
|
+
export * from "./buffered-transform.js";
|
|
2
|
+
export * from "./buffered.js";
|
|
3
|
+
export * from "./consumable.js";
|
|
4
|
+
export * from "./decode-utf8.js";
|
|
5
|
+
export * from "./distribution.js";
|
|
6
|
+
export * from "./duplex.js";
|
|
7
|
+
export * from "./gather-string.js";
|
|
8
|
+
export * from "./inspect.js";
|
|
9
|
+
export * from "./pipe-from.js";
|
|
10
|
+
export * from "./push-readable.js";
|
|
11
|
+
export * from "./split-string.js";
|
|
12
|
+
export * from "./stream.js";
|
|
13
|
+
export * from "./struct-deserialize.js";
|
|
14
|
+
export * from "./struct-serialize.js";
|
|
15
|
+
export * from "./wrap-readable.js";
|
|
16
|
+
export * from "./wrap-writable.js";
|
package/src/inspect.ts
CHANGED
package/src/pipe-from.ts
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ReadableWritablePair } from "./stream.js";
|
|
2
|
+
import { WritableStream } from "./stream.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
* `pair.writable`, when pipe `pair.readable` to `writable`.
|
|
5
|
+
* Pipe `pair.readable` to `writable`, then returns `pair.writable`.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* This is the opposite of `ReadableStream#pipeThrough()`.
|
|
8
8
|
*
|
|
9
9
|
* @param writable The `WritableStream` to write to.
|
|
10
10
|
* @param pair A `TransformStream` that converts chunks.
|
|
11
|
-
* @returns
|
|
11
|
+
* @returns `pair`'s `writable` stream.
|
|
12
12
|
*/
|
|
13
|
-
export function pipeFrom<W, T>(
|
|
13
|
+
export function pipeFrom<W, T>(
|
|
14
|
+
writable: WritableStream<W>,
|
|
15
|
+
pair: ReadableWritablePair<W, T>
|
|
16
|
+
) {
|
|
14
17
|
const writer = pair.writable.getWriter();
|
|
15
|
-
const pipe = pair.readable
|
|
16
|
-
.pipeTo(writable);
|
|
18
|
+
const pipe = pair.readable.pipeTo(writable);
|
|
17
19
|
return new WritableStream<T>({
|
|
18
20
|
async write(chunk) {
|
|
19
|
-
await writer.ready;
|
|
20
21
|
await writer.write(chunk);
|
|
21
22
|
},
|
|
22
23
|
async close() {
|
|
23
24
|
await writer.close();
|
|
24
25
|
await pipe;
|
|
25
|
-
}
|
|
26
|
+
},
|
|
26
27
|
});
|
|
27
28
|
}
|
package/src/push-readable.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { PromiseResolver } from
|
|
2
|
-
|
|
1
|
+
import { PromiseResolver } from "@yume-chan/async";
|
|
2
|
+
|
|
3
|
+
import type { AbortSignal, QueuingStrategy } from "./stream.js";
|
|
4
|
+
import { AbortController, ReadableStream } from "./stream.js";
|
|
3
5
|
|
|
4
6
|
export interface PushReadableStreamController<T> {
|
|
5
7
|
abortSignal: AbortSignal;
|
|
@@ -8,55 +10,84 @@ export interface PushReadableStreamController<T> {
|
|
|
8
10
|
|
|
9
11
|
close(): void;
|
|
10
12
|
|
|
11
|
-
error(e?:
|
|
13
|
+
error(e?: unknown): void;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
export type PushReadableStreamSource<T> = (
|
|
16
|
+
export type PushReadableStreamSource<T> = (
|
|
17
|
+
controller: PushReadableStreamController<T>
|
|
18
|
+
) => void | Promise<void>;
|
|
15
19
|
|
|
16
20
|
export class PushReadableStream<T> extends ReadableStream<T> {
|
|
17
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Create a new `PushReadableStream` from a source.
|
|
23
|
+
*
|
|
24
|
+
* @param source If `source` returns a `Promise`, the stream will be closed
|
|
25
|
+
* when the `Promise` is resolved, and be errored when the `Promise` is rejected.
|
|
26
|
+
* @param strategy
|
|
27
|
+
*/
|
|
28
|
+
public constructor(
|
|
29
|
+
source: PushReadableStreamSource<T>,
|
|
30
|
+
strategy?: QueuingStrategy<T>
|
|
31
|
+
) {
|
|
18
32
|
let waterMarkLow: PromiseResolver<void> | undefined;
|
|
19
|
-
const canceled
|
|
20
|
-
|
|
21
|
-
super(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
33
|
+
const canceled = new AbortController();
|
|
34
|
+
|
|
35
|
+
super(
|
|
36
|
+
{
|
|
37
|
+
start: (controller) => {
|
|
38
|
+
const result = source({
|
|
39
|
+
abortSignal: canceled.signal,
|
|
40
|
+
async enqueue(chunk) {
|
|
41
|
+
if (canceled.signal.aborted) {
|
|
42
|
+
// If the stream is already cancelled,
|
|
43
|
+
// throw immediately.
|
|
44
|
+
throw (
|
|
45
|
+
canceled.signal.reason ??
|
|
46
|
+
new Error("Aborted")
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Only when the stream is errored, `desiredSize` will be `null`.
|
|
51
|
+
// But since `null <= 0` is `true`
|
|
52
|
+
// (`null <= 0` is evaluated as `!(null > 0)` => `!false` => `true`),
|
|
53
|
+
// not handling it will cause a deadlock.
|
|
54
|
+
if ((controller.desiredSize ?? 1) <= 0) {
|
|
55
|
+
waterMarkLow = new PromiseResolver<void>();
|
|
56
|
+
await waterMarkLow.promise;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// `controller.enqueue` will throw error for us
|
|
60
|
+
// if the stream is already errored.
|
|
61
|
+
controller.enqueue(chunk);
|
|
62
|
+
},
|
|
63
|
+
close() {
|
|
64
|
+
controller.close();
|
|
65
|
+
},
|
|
66
|
+
error(e) {
|
|
67
|
+
controller.error(e);
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (result && "then" in result) {
|
|
72
|
+
result.then(
|
|
73
|
+
() => {
|
|
74
|
+
controller.close();
|
|
75
|
+
},
|
|
76
|
+
(e) => {
|
|
77
|
+
controller.error(e);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
pull: () => {
|
|
83
|
+
waterMarkLow?.resolve();
|
|
84
|
+
},
|
|
85
|
+
cancel: (reason) => {
|
|
86
|
+
canceled.abort(reason);
|
|
87
|
+
waterMarkLow?.reject(reason);
|
|
88
|
+
},
|
|
59
89
|
},
|
|
60
|
-
|
|
90
|
+
strategy
|
|
91
|
+
);
|
|
61
92
|
}
|
|
62
93
|
}
|
package/src/split-string.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { TransformStream } from "./stream.js";
|
|
2
2
|
|
|
3
|
-
function* split(
|
|
3
|
+
function* split(
|
|
4
|
+
input: string,
|
|
5
|
+
separator: string
|
|
6
|
+
): Generator<string, void, void> {
|
|
4
7
|
let start = 0;
|
|
5
8
|
|
|
6
9
|
while (true) {
|
|
@@ -23,7 +26,7 @@ export class SplitStringStream extends TransformStream<string, string> {
|
|
|
23
26
|
for (const part of split(chunk, separator)) {
|
|
24
27
|
controller.enqueue(part);
|
|
25
28
|
}
|
|
26
|
-
}
|
|
29
|
+
},
|
|
27
30
|
});
|
|
28
31
|
}
|
|
29
32
|
}
|
package/src/stream.ts
CHANGED
|
@@ -1,22 +1,64 @@
|
|
|
1
1
|
import type { AbortSignal } from "web-streams-polyfill";
|
|
2
|
-
|
|
2
|
+
import {
|
|
3
|
+
ReadableStream as ReadableStreamPolyfill,
|
|
4
|
+
TransformStream as TransformStreamPolyfill,
|
|
5
|
+
WritableStream as WritableStreamPolyfill,
|
|
6
|
+
} from "web-streams-polyfill";
|
|
7
|
+
export * from "web-streams-polyfill";
|
|
3
8
|
|
|
4
9
|
/** A controller object that allows you to abort one or more DOM requests as and when desired. */
|
|
5
10
|
export interface AbortController {
|
|
6
11
|
/**
|
|
7
12
|
* Returns the AbortSignal object associated with this object.
|
|
8
13
|
*/
|
|
9
|
-
|
|
10
14
|
readonly signal: AbortSignal;
|
|
15
|
+
|
|
11
16
|
/**
|
|
12
17
|
* Invoking this method will set this object's AbortSignal's aborted flag and signal to any observers that the associated activity is to be aborted.
|
|
13
18
|
*/
|
|
14
19
|
abort(reason?: any): void;
|
|
15
20
|
}
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
interface AbortControllerConstructor {
|
|
18
23
|
prototype: AbortController;
|
|
19
|
-
new(): AbortController;
|
|
20
|
-
}
|
|
24
|
+
new (): AbortController;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface GlobalExtension {
|
|
28
|
+
AbortController: AbortControllerConstructor;
|
|
29
|
+
ReadableStream: typeof ReadableStreamPolyfill;
|
|
30
|
+
WritableStream: typeof WritableStreamPolyfill;
|
|
31
|
+
TransformStream: typeof TransformStreamPolyfill;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const GLOBAL = globalThis as unknown as GlobalExtension;
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
export const AbortController = GLOBAL.AbortController;
|
|
37
|
+
|
|
38
|
+
export type ReadableStream<R = any> = ReadableStreamPolyfill<R>;
|
|
39
|
+
export let ReadableStream = ReadableStreamPolyfill;
|
|
40
|
+
|
|
41
|
+
export type WritableStream<W = any> = WritableStreamPolyfill<W>;
|
|
42
|
+
export let WritableStream = WritableStreamPolyfill;
|
|
43
|
+
|
|
44
|
+
export type TransformStream<I = any, O = any> = TransformStreamPolyfill<I, O>;
|
|
45
|
+
export let TransformStream = TransformStreamPolyfill;
|
|
46
|
+
|
|
47
|
+
if (GLOBAL.ReadableStream && GLOBAL.WritableStream && GLOBAL.TransformStream) {
|
|
48
|
+
// Use browser native implementation
|
|
49
|
+
ReadableStream = GLOBAL.ReadableStream;
|
|
50
|
+
WritableStream = GLOBAL.WritableStream;
|
|
51
|
+
TransformStream = GLOBAL.TransformStream;
|
|
52
|
+
} else {
|
|
53
|
+
// TODO: enable loading Node.js stream implementation when bundler supports Top Level Await
|
|
54
|
+
// try {
|
|
55
|
+
// // Use Node.js native implementation
|
|
56
|
+
// const MODULE_NAME = "node:stream/web";
|
|
57
|
+
// const StreamWeb = (await import(MODULE_NAME)) as GlobalExtension;
|
|
58
|
+
// ReadableStream = StreamWeb.ReadableStream;
|
|
59
|
+
// WritableStream = StreamWeb.WritableStream;
|
|
60
|
+
// TransformStream = StreamWeb.TransformStream;
|
|
61
|
+
// } catch {
|
|
62
|
+
// // ignore
|
|
63
|
+
// }
|
|
64
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type Struct from "@yume-chan/struct";
|
|
2
2
|
import type { StructValueType } from "@yume-chan/struct";
|
|
3
|
-
import { BufferedTransformStream } from './buffered-transform.js';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import { BufferedTransformStream } from "./buffered-transform.js";
|
|
5
|
+
|
|
6
|
+
export class StructDeserializeStream<
|
|
7
|
+
T extends Struct<any, any, any, any>
|
|
8
|
+
> extends BufferedTransformStream<StructValueType<T>> {
|
|
7
9
|
public constructor(struct: T) {
|
|
8
10
|
super((stream) => {
|
|
9
|
-
return struct.deserialize(stream)
|
|
11
|
+
return struct.deserialize(stream);
|
|
10
12
|
});
|
|
11
13
|
}
|
|
12
14
|
}
|
package/src/struct-serialize.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type Struct from "@yume-chan/struct";
|
|
2
|
+
|
|
2
3
|
import { TransformStream } from "./stream.js";
|
|
3
4
|
|
|
4
|
-
export class StructSerializeStream<
|
|
5
|
-
extends
|
|
5
|
+
export class StructSerializeStream<
|
|
6
|
+
T extends Struct<any, any, any, any>
|
|
7
|
+
> extends TransformStream<T["TInit"], Uint8Array> {
|
|
6
8
|
constructor(struct: T) {
|
|
7
9
|
super({
|
|
8
10
|
transform(chunk, controller) {
|
package/src/wrap-readable.ts
CHANGED
|
@@ -1,21 +1,31 @@
|
|
|
1
1
|
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
2
|
-
import { ReadableStream, ReadableStreamDefaultController, ReadableStreamDefaultReader } from "./stream.js";
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
import type {
|
|
4
|
+
ReadableStreamDefaultController,
|
|
5
|
+
ReadableStreamDefaultReader,
|
|
6
|
+
} from "./stream.js";
|
|
7
|
+
import { ReadableStream } from "./stream.js";
|
|
8
|
+
|
|
9
|
+
export type WrapReadableStreamStart<T> = (
|
|
10
|
+
controller: ReadableStreamDefaultController<T>
|
|
11
|
+
) => ValueOrPromise<ReadableStream<T>>;
|
|
5
12
|
|
|
6
13
|
export interface ReadableStreamWrapper<T> {
|
|
7
14
|
start: WrapReadableStreamStart<T>;
|
|
8
|
-
cancel?(reason?:
|
|
15
|
+
cancel?(reason?: unknown): ValueOrPromise<void>;
|
|
9
16
|
close?(): ValueOrPromise<void>;
|
|
10
17
|
}
|
|
11
18
|
|
|
12
19
|
function getWrappedReadableStream<T>(
|
|
13
|
-
wrapper:
|
|
20
|
+
wrapper:
|
|
21
|
+
| ReadableStream<T>
|
|
22
|
+
| WrapReadableStreamStart<T>
|
|
23
|
+
| ReadableStreamWrapper<T>,
|
|
14
24
|
controller: ReadableStreamDefaultController<T>
|
|
15
25
|
) {
|
|
16
|
-
if (
|
|
26
|
+
if ("start" in wrapper) {
|
|
17
27
|
return wrapper.start(controller);
|
|
18
|
-
} else if (typeof wrapper ===
|
|
28
|
+
} else if (typeof wrapper === "function") {
|
|
19
29
|
return wrapper(controller);
|
|
20
30
|
} else {
|
|
21
31
|
// Can't use `wrapper instanceof ReadableStream`
|
|
@@ -31,12 +41,17 @@ function getWrappedReadableStream<T>(
|
|
|
31
41
|
* 2. Synchronously create a `ReadableStream` by asynchronously return another `ReadableStream`.
|
|
32
42
|
* 3. Convert native `ReadableStream`s to polyfilled ones so they can `pipe` between.
|
|
33
43
|
*/
|
|
34
|
-
export class WrapReadableStream<T> extends ReadableStream<T>{
|
|
44
|
+
export class WrapReadableStream<T> extends ReadableStream<T> {
|
|
35
45
|
public readable!: ReadableStream<T>;
|
|
36
46
|
|
|
37
47
|
private reader!: ReadableStreamDefaultReader<T>;
|
|
38
48
|
|
|
39
|
-
public constructor(
|
|
49
|
+
public constructor(
|
|
50
|
+
wrapper:
|
|
51
|
+
| ReadableStream<T>
|
|
52
|
+
| WrapReadableStreamStart<T>
|
|
53
|
+
| ReadableStreamWrapper<T>
|
|
54
|
+
) {
|
|
40
55
|
super({
|
|
41
56
|
start: async (controller) => {
|
|
42
57
|
// `start` is invoked before `ReadableStream`'s constructor finish,
|
|
@@ -45,12 +60,15 @@ export class WrapReadableStream<T> extends ReadableStream<T>{
|
|
|
45
60
|
// Queue a microtask to avoid this.
|
|
46
61
|
await Promise.resolve();
|
|
47
62
|
|
|
48
|
-
this.readable = await getWrappedReadableStream(
|
|
63
|
+
this.readable = await getWrappedReadableStream(
|
|
64
|
+
wrapper,
|
|
65
|
+
controller
|
|
66
|
+
);
|
|
49
67
|
this.reader = this.readable.getReader();
|
|
50
68
|
},
|
|
51
69
|
cancel: async (reason) => {
|
|
52
70
|
await this.reader.cancel(reason);
|
|
53
|
-
if (
|
|
71
|
+
if ("cancel" in wrapper) {
|
|
54
72
|
await wrapper.cancel?.(reason);
|
|
55
73
|
}
|
|
56
74
|
},
|
|
@@ -58,13 +76,13 @@ export class WrapReadableStream<T> extends ReadableStream<T>{
|
|
|
58
76
|
const result = await this.reader.read();
|
|
59
77
|
if (result.done) {
|
|
60
78
|
controller.close();
|
|
61
|
-
if (
|
|
79
|
+
if ("close" in wrapper) {
|
|
62
80
|
await wrapper.close?.();
|
|
63
81
|
}
|
|
64
82
|
} else {
|
|
65
83
|
controller.enqueue(result.value);
|
|
66
84
|
}
|
|
67
|
-
}
|
|
85
|
+
},
|
|
68
86
|
});
|
|
69
87
|
}
|
|
70
88
|
}
|