@yume-chan/stream-extra 0.0.20 → 0.0.22
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 +18 -0
- package/CHANGELOG.md +13 -1
- package/esm/buffered-transform.d.ts.map +1 -1
- package/esm/buffered-transform.js.map +1 -1
- package/esm/buffered.d.ts +0 -2
- package/esm/buffered.d.ts.map +1 -1
- package/esm/buffered.js +28 -21
- package/esm/buffered.js.map +1 -1
- package/esm/concat.d.ts +35 -0
- package/esm/concat.d.ts.map +1 -0
- package/esm/concat.js +128 -0
- package/esm/concat.js.map +1 -0
- package/esm/consumable.d.ts +3 -3
- package/esm/consumable.d.ts.map +1 -1
- package/esm/consumable.js +4 -4
- package/esm/consumable.js.map +1 -1
- package/esm/distribution.d.ts.map +1 -1
- package/esm/distribution.js.map +1 -1
- package/esm/duplex.d.ts +2 -2
- package/esm/duplex.d.ts.map +1 -1
- package/esm/duplex.js +2 -2
- package/esm/duplex.js.map +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -1
- package/esm/index.js.map +1 -1
- package/esm/push-readable.d.ts +1 -0
- package/esm/push-readable.d.ts.map +1 -1
- package/esm/push-readable.js +24 -10
- package/esm/push-readable.js.map +1 -1
- package/esm/split-string.d.ts.map +1 -1
- package/esm/split-string.js.map +1 -1
- package/esm/stream.js +0 -14
- package/esm/stream.js.map +1 -1
- package/esm/struct-deserialize.d.ts.map +1 -1
- package/esm/struct-deserialize.js.map +1 -1
- package/esm/wrap-readable.d.ts +2 -2
- package/esm/wrap-readable.d.ts.map +1 -1
- package/esm/wrap-readable.js +8 -8
- package/esm/wrap-readable.js.map +1 -1
- package/esm/wrap-writable.d.ts.map +1 -1
- package/esm/wrap-writable.js.map +1 -1
- package/package.json +9 -9
- package/src/buffered-transform.ts +4 -4
- package/src/buffered.ts +32 -25
- package/src/concat.ts +160 -0
- package/src/consumable.ts +45 -33
- package/src/decode-utf8.ts +1 -1
- package/src/distribution.ts +5 -5
- package/src/duplex.ts +27 -20
- package/src/index.ts +1 -1
- package/src/pipe-from.ts +1 -1
- package/src/push-readable.ts +33 -17
- package/src/split-string.ts +2 -2
- package/src/stream.ts +0 -13
- package/src/struct-deserialize.ts +2 -2
- package/src/struct-serialize.ts +1 -1
- package/src/wrap-readable.ts +39 -34
- package/src/wrap-writable.ts +5 -5
- package/tsconfig.build.tsbuildinfo +1 -1
- package/esm/gather-string.d.ts +0 -7
- package/esm/gather-string.d.ts.map +0 -1
- package/esm/gather-string.js +0 -16
- package/esm/gather-string.js.map +0 -1
- package/src/gather-string.ts +0 -17
package/src/consumable.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { PromiseResolver } from "@yume-chan/async";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
QueuingStrategy,
|
|
5
|
+
WritableStreamDefaultController,
|
|
6
|
+
WritableStreamDefaultWriter,
|
|
7
|
+
} from "./stream.js";
|
|
4
8
|
import { ReadableStream, TransformStream, WritableStream } from "./stream.js";
|
|
5
9
|
|
|
6
10
|
interface Task {
|
|
@@ -29,25 +33,25 @@ export class Consumable<T> {
|
|
|
29
33
|
readonly #task: Task;
|
|
30
34
|
readonly #resolver: PromiseResolver<void>;
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
readonly value: T;
|
|
37
|
+
readonly consumed: Promise<void>;
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
constructor(value: T) {
|
|
36
40
|
this.#task = createTask("Consumable");
|
|
37
41
|
this.value = value;
|
|
38
42
|
this.#resolver = new PromiseResolver<void>();
|
|
39
43
|
this.consumed = this.#resolver.promise;
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
consume() {
|
|
43
47
|
this.#resolver.resolve();
|
|
44
48
|
}
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
error(error: any) {
|
|
47
51
|
this.#resolver.reject(error);
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
async tryConsume<U>(callback: (value: T) => U) {
|
|
51
55
|
try {
|
|
52
56
|
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
53
57
|
const result = await this.#task.run(() => callback(this.value));
|
|
@@ -62,7 +66,7 @@ export class Consumable<T> {
|
|
|
62
66
|
|
|
63
67
|
async function enqueue<T>(
|
|
64
68
|
controller: { enqueue: (chunk: Consumable<T>) => void },
|
|
65
|
-
chunk: T
|
|
69
|
+
chunk: T,
|
|
66
70
|
) {
|
|
67
71
|
const output = new Consumable(chunk);
|
|
68
72
|
controller.enqueue(output);
|
|
@@ -70,7 +74,7 @@ async function enqueue<T>(
|
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
export class WrapConsumableStream<T> extends TransformStream<T, Consumable<T>> {
|
|
73
|
-
|
|
77
|
+
constructor() {
|
|
74
78
|
super({
|
|
75
79
|
async transform(chunk, controller) {
|
|
76
80
|
await enqueue(controller, chunk);
|
|
@@ -83,7 +87,7 @@ export class UnwrapConsumableStream<T> extends TransformStream<
|
|
|
83
87
|
Consumable<T>,
|
|
84
88
|
T
|
|
85
89
|
> {
|
|
86
|
-
|
|
90
|
+
constructor() {
|
|
87
91
|
super({
|
|
88
92
|
transform(chunk, controller) {
|
|
89
93
|
controller.enqueue(chunk.value);
|
|
@@ -101,18 +105,18 @@ export interface ConsumableReadableStreamController<T> {
|
|
|
101
105
|
|
|
102
106
|
export interface ConsumableReadableStreamSource<T> {
|
|
103
107
|
start?(
|
|
104
|
-
controller: ConsumableReadableStreamController<T
|
|
108
|
+
controller: ConsumableReadableStreamController<T>,
|
|
105
109
|
): void | PromiseLike<void>;
|
|
106
110
|
pull?(
|
|
107
|
-
controller: ConsumableReadableStreamController<T
|
|
111
|
+
controller: ConsumableReadableStreamController<T>,
|
|
108
112
|
): void | PromiseLike<void>;
|
|
109
113
|
cancel?(reason: any): void | PromiseLike<void>;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
export class ConsumableReadableStream<T> extends ReadableStream<Consumable<T>> {
|
|
113
|
-
|
|
117
|
+
constructor(
|
|
114
118
|
source: ConsumableReadableStreamSource<T>,
|
|
115
|
-
strategy?: QueuingStrategy<T
|
|
119
|
+
strategy?: QueuingStrategy<T>,
|
|
116
120
|
) {
|
|
117
121
|
let wrappedController:
|
|
118
122
|
| ConsumableReadableStreamController<T>
|
|
@@ -155,31 +159,36 @@ export class ConsumableReadableStream<T> extends ReadableStream<Consumable<T>> {
|
|
|
155
159
|
await source.cancel?.(reason);
|
|
156
160
|
},
|
|
157
161
|
},
|
|
158
|
-
wrappedStrategy
|
|
162
|
+
wrappedStrategy,
|
|
159
163
|
);
|
|
160
164
|
}
|
|
161
165
|
}
|
|
162
166
|
|
|
163
167
|
export interface ConsumableWritableStreamSink<T> {
|
|
164
|
-
start?(
|
|
165
|
-
|
|
168
|
+
start?(
|
|
169
|
+
controller: WritableStreamDefaultController,
|
|
170
|
+
): void | PromiseLike<void>;
|
|
171
|
+
write?(
|
|
172
|
+
chunk: T,
|
|
173
|
+
controller: WritableStreamDefaultController,
|
|
174
|
+
): void | PromiseLike<void>;
|
|
166
175
|
abort?(reason: any): void | PromiseLike<void>;
|
|
167
176
|
close?(): void | PromiseLike<void>;
|
|
168
177
|
}
|
|
169
178
|
|
|
170
179
|
export class ConsumableWritableStream<T> extends WritableStream<Consumable<T>> {
|
|
171
|
-
|
|
180
|
+
static async write<T>(
|
|
172
181
|
writer: WritableStreamDefaultWriter<Consumable<T>>,
|
|
173
|
-
value: T
|
|
182
|
+
value: T,
|
|
174
183
|
) {
|
|
175
184
|
const consumable = new Consumable(value);
|
|
176
185
|
await writer.write(consumable);
|
|
177
186
|
await consumable.consumed;
|
|
178
187
|
}
|
|
179
188
|
|
|
180
|
-
|
|
189
|
+
constructor(
|
|
181
190
|
sink: ConsumableWritableStreamSink<T>,
|
|
182
|
-
strategy?: QueuingStrategy<T
|
|
191
|
+
strategy?: QueuingStrategy<T>,
|
|
183
192
|
) {
|
|
184
193
|
let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
|
|
185
194
|
if (strategy) {
|
|
@@ -196,11 +205,13 @@ export class ConsumableWritableStream<T> extends WritableStream<Consumable<T>> {
|
|
|
196
205
|
|
|
197
206
|
super(
|
|
198
207
|
{
|
|
199
|
-
start() {
|
|
200
|
-
return sink.start?.();
|
|
208
|
+
start(controller) {
|
|
209
|
+
return sink.start?.(controller);
|
|
201
210
|
},
|
|
202
|
-
async write(chunk) {
|
|
203
|
-
await chunk.tryConsume(
|
|
211
|
+
async write(chunk, controller) {
|
|
212
|
+
await chunk.tryConsume(
|
|
213
|
+
(value) => sink.write?.(value, controller),
|
|
214
|
+
);
|
|
204
215
|
chunk.consume();
|
|
205
216
|
},
|
|
206
217
|
abort(reason) {
|
|
@@ -210,21 +221,21 @@ export class ConsumableWritableStream<T> extends WritableStream<Consumable<T>> {
|
|
|
210
221
|
return sink.close?.();
|
|
211
222
|
},
|
|
212
223
|
},
|
|
213
|
-
wrappedStrategy
|
|
224
|
+
wrappedStrategy,
|
|
214
225
|
);
|
|
215
226
|
}
|
|
216
227
|
}
|
|
217
228
|
|
|
218
229
|
export interface ConsumableTransformer<I, O> {
|
|
219
230
|
start?(
|
|
220
|
-
controller: ConsumableReadableStreamController<O
|
|
231
|
+
controller: ConsumableReadableStreamController<O>,
|
|
221
232
|
): void | PromiseLike<void>;
|
|
222
233
|
transform?(
|
|
223
234
|
chunk: I,
|
|
224
|
-
controller: ConsumableReadableStreamController<O
|
|
235
|
+
controller: ConsumableReadableStreamController<O>,
|
|
225
236
|
): void | PromiseLike<void>;
|
|
226
237
|
flush?(
|
|
227
|
-
controller: ConsumableReadableStreamController<O
|
|
238
|
+
controller: ConsumableReadableStreamController<O>,
|
|
228
239
|
): void | PromiseLike<void>;
|
|
229
240
|
}
|
|
230
241
|
|
|
@@ -232,7 +243,7 @@ export class ConsumableTransformStream<I, O> extends TransformStream<
|
|
|
232
243
|
Consumable<I>,
|
|
233
244
|
Consumable<O>
|
|
234
245
|
> {
|
|
235
|
-
|
|
246
|
+
constructor(transformer: ConsumableTransformer<I, O>) {
|
|
236
247
|
let wrappedController:
|
|
237
248
|
| ConsumableReadableStreamController<O>
|
|
238
249
|
| undefined;
|
|
@@ -254,8 +265,9 @@ export class ConsumableTransformStream<I, O> extends TransformStream<
|
|
|
254
265
|
await transformer.start?.(wrappedController);
|
|
255
266
|
},
|
|
256
267
|
async transform(chunk) {
|
|
257
|
-
await chunk.tryConsume(
|
|
258
|
-
|
|
268
|
+
await chunk.tryConsume(
|
|
269
|
+
(value) =>
|
|
270
|
+
transformer.transform?.(value, wrappedController!),
|
|
259
271
|
);
|
|
260
272
|
chunk.consume();
|
|
261
273
|
},
|
|
@@ -270,7 +282,7 @@ export class ConsumableInspectStream<T> extends TransformStream<
|
|
|
270
282
|
Consumable<T>,
|
|
271
283
|
Consumable<T>
|
|
272
284
|
> {
|
|
273
|
-
|
|
285
|
+
constructor(callback: (value: T) => void) {
|
|
274
286
|
super({
|
|
275
287
|
transform(chunk, controller) {
|
|
276
288
|
callback(chunk.value);
|
package/src/decode-utf8.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { decodeUtf8 } from "@yume-chan/struct";
|
|
|
3
3
|
import { TransformStream } from "./stream.js";
|
|
4
4
|
|
|
5
5
|
export class DecodeUtf8Stream extends TransformStream<Uint8Array, string> {
|
|
6
|
-
|
|
6
|
+
constructor() {
|
|
7
7
|
super({
|
|
8
8
|
transform(chunk, controller) {
|
|
9
9
|
controller.enqueue(decodeUtf8(chunk));
|
package/src/distribution.ts
CHANGED
|
@@ -9,7 +9,7 @@ export class BufferCombiner {
|
|
|
9
9
|
#offset: number;
|
|
10
10
|
#available: number;
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
constructor(size: number) {
|
|
13
13
|
this.#capacity = size;
|
|
14
14
|
this.#buffer = new Uint8Array(size);
|
|
15
15
|
this.#offset = 0;
|
|
@@ -23,7 +23,7 @@ export class BufferCombiner {
|
|
|
23
23
|
* A generator that yields buffers of specified size.
|
|
24
24
|
* It may yield the same buffer multiple times, consume the data before calling `next`.
|
|
25
25
|
*/
|
|
26
|
-
|
|
26
|
+
*push(data: Uint8Array): Generator<Uint8Array, void, void> {
|
|
27
27
|
let offset = 0;
|
|
28
28
|
let available = data.byteLength;
|
|
29
29
|
|
|
@@ -31,7 +31,7 @@ export class BufferCombiner {
|
|
|
31
31
|
if (available >= this.#available) {
|
|
32
32
|
this.#buffer.set(
|
|
33
33
|
data.subarray(0, this.#available),
|
|
34
|
-
this.#offset
|
|
34
|
+
this.#offset,
|
|
35
35
|
);
|
|
36
36
|
offset += this.#available;
|
|
37
37
|
available -= this.#available;
|
|
@@ -65,7 +65,7 @@ export class BufferCombiner {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
flush(): Uint8Array | undefined {
|
|
69
69
|
if (this.#offset === 0) {
|
|
70
70
|
return undefined;
|
|
71
71
|
}
|
|
@@ -81,7 +81,7 @@ export class DistributionStream extends ConsumableTransformStream<
|
|
|
81
81
|
Uint8Array,
|
|
82
82
|
Uint8Array
|
|
83
83
|
> {
|
|
84
|
-
|
|
84
|
+
constructor(size: number, combine = false) {
|
|
85
85
|
const combiner = combine ? new BufferCombiner(size) : undefined;
|
|
86
86
|
super({
|
|
87
87
|
async transform(chunk, controller) {
|
package/src/duplex.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { PromiseResolver } from "@yume-chan/async";
|
|
|
2
2
|
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
|
+
QueuingStrategy,
|
|
5
6
|
ReadableStream,
|
|
6
7
|
ReadableStreamDefaultController,
|
|
7
8
|
WritableStreamDefaultWriter,
|
|
@@ -50,39 +51,45 @@ export class DuplexStreamFactory<R, W> {
|
|
|
50
51
|
#writers: WritableStreamDefaultWriter<W>[] = [];
|
|
51
52
|
|
|
52
53
|
#writableClosed = false;
|
|
53
|
-
|
|
54
|
+
get writableClosed() {
|
|
54
55
|
return this.#writableClosed;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
#closed = new PromiseResolver<void>();
|
|
58
|
-
|
|
59
|
+
get closed() {
|
|
59
60
|
return this.#closed.promise;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
readonly #options: DuplexStreamFactoryOptions;
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
constructor(options?: DuplexStreamFactoryOptions) {
|
|
65
66
|
this.#options = options ?? {};
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
wrapReadable(
|
|
70
|
+
readable: ReadableStream<R>,
|
|
71
|
+
strategy?: QueuingStrategy<R>,
|
|
72
|
+
): WrapReadableStream<R> {
|
|
73
|
+
return new WrapReadableStream<R>(
|
|
74
|
+
{
|
|
75
|
+
start: (controller) => {
|
|
76
|
+
this.#readableControllers.push(controller);
|
|
77
|
+
return readable;
|
|
78
|
+
},
|
|
79
|
+
cancel: async () => {
|
|
80
|
+
// cancel means the local peer wants to close the connection.
|
|
81
|
+
await this.close();
|
|
82
|
+
},
|
|
83
|
+
close: async () => {
|
|
84
|
+
// stream end means the remote peer closed the connection first.
|
|
85
|
+
await this.dispose();
|
|
86
|
+
},
|
|
73
87
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
await this.close();
|
|
77
|
-
},
|
|
78
|
-
close: async () => {
|
|
79
|
-
// stream end means the remote peer closed the connection first.
|
|
80
|
-
await this.dispose();
|
|
81
|
-
},
|
|
82
|
-
});
|
|
88
|
+
strategy,
|
|
89
|
+
);
|
|
83
90
|
}
|
|
84
91
|
|
|
85
|
-
|
|
92
|
+
createWritable(stream: WritableStream<W>): WritableStream<W> {
|
|
86
93
|
const writer = stream.getWriter();
|
|
87
94
|
this.#writers.push(writer);
|
|
88
95
|
|
|
@@ -104,7 +111,7 @@ export class DuplexStreamFactory<R, W> {
|
|
|
104
111
|
});
|
|
105
112
|
}
|
|
106
113
|
|
|
107
|
-
|
|
114
|
+
async close() {
|
|
108
115
|
if (this.#writableClosed) {
|
|
109
116
|
return;
|
|
110
117
|
}
|
|
@@ -122,7 +129,7 @@ export class DuplexStreamFactory<R, W> {
|
|
|
122
129
|
}
|
|
123
130
|
}
|
|
124
131
|
|
|
125
|
-
|
|
132
|
+
async dispose() {
|
|
126
133
|
this.#writableClosed = true;
|
|
127
134
|
this.#closed.resolve();
|
|
128
135
|
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from "./buffered-transform.js";
|
|
2
2
|
export * from "./buffered.js";
|
|
3
|
+
export * from "./concat.js";
|
|
3
4
|
export * from "./consumable.js";
|
|
4
5
|
export * from "./decode-utf8.js";
|
|
5
6
|
export * from "./distribution.js";
|
|
6
7
|
export * from "./duplex.js";
|
|
7
|
-
export * from "./gather-string.js";
|
|
8
8
|
export * from "./inspect.js";
|
|
9
9
|
export * from "./pipe-from.js";
|
|
10
10
|
export * from "./push-readable.js";
|
package/src/pipe-from.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { WritableStream } from "./stream.js";
|
|
|
12
12
|
*/
|
|
13
13
|
export function pipeFrom<W, T>(
|
|
14
14
|
writable: WritableStream<W>,
|
|
15
|
-
pair: ReadableWritablePair<W, T
|
|
15
|
+
pair: ReadableWritablePair<W, T>,
|
|
16
16
|
) {
|
|
17
17
|
const writer = pair.writable.getWriter();
|
|
18
18
|
const pipe = pair.readable.pipeTo(writable);
|
package/src/push-readable.ts
CHANGED
|
@@ -14,10 +14,12 @@ export interface PushReadableStreamController<T> {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export type PushReadableStreamSource<T> = (
|
|
17
|
-
controller: PushReadableStreamController<T
|
|
17
|
+
controller: PushReadableStreamController<T>,
|
|
18
18
|
) => void | Promise<void>;
|
|
19
19
|
|
|
20
20
|
export class PushReadableStream<T> extends ReadableStream<T> {
|
|
21
|
+
#zeroHighWaterMarkAllowEnqueue = false;
|
|
22
|
+
|
|
21
23
|
/**
|
|
22
24
|
* Create a new `PushReadableStream` from a source.
|
|
23
25
|
*
|
|
@@ -25,33 +27,41 @@ export class PushReadableStream<T> extends ReadableStream<T> {
|
|
|
25
27
|
* when the `Promise` is resolved, and be errored when the `Promise` is rejected.
|
|
26
28
|
* @param strategy
|
|
27
29
|
*/
|
|
28
|
-
|
|
30
|
+
constructor(
|
|
29
31
|
source: PushReadableStreamSource<T>,
|
|
30
|
-
strategy?: QueuingStrategy<T
|
|
32
|
+
strategy?: QueuingStrategy<T>,
|
|
31
33
|
) {
|
|
32
34
|
let waterMarkLow: PromiseResolver<void> | undefined;
|
|
33
35
|
const abortController = new AbortController();
|
|
34
36
|
|
|
35
37
|
super(
|
|
36
38
|
{
|
|
37
|
-
start: (controller) => {
|
|
39
|
+
start: async (controller) => {
|
|
40
|
+
await Promise.resolve();
|
|
41
|
+
|
|
38
42
|
const result = source({
|
|
39
43
|
abortSignal: abortController.signal,
|
|
40
|
-
async
|
|
44
|
+
enqueue: async (chunk) => {
|
|
41
45
|
if (abortController.signal.aborted) {
|
|
42
46
|
// If the stream is already cancelled,
|
|
43
47
|
// throw immediately.
|
|
44
|
-
throw
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
throw abortController.signal.reason;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (controller.desiredSize === null) {
|
|
52
|
+
// `desiredSize` being `null` means the stream is in error state,
|
|
53
|
+
// `controller.enqueue` will throw an error for us.
|
|
54
|
+
controller.enqueue(chunk);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (this.#zeroHighWaterMarkAllowEnqueue) {
|
|
59
|
+
this.#zeroHighWaterMarkAllowEnqueue = false;
|
|
60
|
+
controller.enqueue(chunk);
|
|
61
|
+
return;
|
|
48
62
|
}
|
|
49
63
|
|
|
50
|
-
|
|
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) {
|
|
64
|
+
if (controller.desiredSize <= 0) {
|
|
55
65
|
waterMarkLow = new PromiseResolver<void>();
|
|
56
66
|
await waterMarkLow.promise;
|
|
57
67
|
}
|
|
@@ -79,19 +89,25 @@ export class PushReadableStream<T> extends ReadableStream<T> {
|
|
|
79
89
|
},
|
|
80
90
|
(e) => {
|
|
81
91
|
controller.error(e);
|
|
82
|
-
}
|
|
92
|
+
},
|
|
83
93
|
);
|
|
84
94
|
}
|
|
85
95
|
},
|
|
86
96
|
pull: () => {
|
|
87
|
-
waterMarkLow
|
|
97
|
+
if (waterMarkLow) {
|
|
98
|
+
waterMarkLow.resolve();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (strategy?.highWaterMark === 0) {
|
|
102
|
+
this.#zeroHighWaterMarkAllowEnqueue = true;
|
|
103
|
+
}
|
|
88
104
|
},
|
|
89
105
|
cancel: (reason) => {
|
|
90
106
|
abortController.abort(reason);
|
|
91
107
|
waterMarkLow?.reject(reason);
|
|
92
108
|
},
|
|
93
109
|
},
|
|
94
|
-
strategy
|
|
110
|
+
strategy,
|
|
95
111
|
);
|
|
96
112
|
}
|
|
97
113
|
}
|
package/src/split-string.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { TransformStream } from "./stream.js";
|
|
|
2
2
|
|
|
3
3
|
function* split(
|
|
4
4
|
input: string,
|
|
5
|
-
separator: string
|
|
5
|
+
separator: string,
|
|
6
6
|
): Generator<string, void, void> {
|
|
7
7
|
let start = 0;
|
|
8
8
|
|
|
@@ -20,7 +20,7 @@ function* split(
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export class SplitStringStream extends TransformStream<string, string> {
|
|
23
|
-
|
|
23
|
+
constructor(separator: string) {
|
|
24
24
|
super({
|
|
25
25
|
transform(chunk, controller) {
|
|
26
26
|
for (const part of split(chunk, separator)) {
|
package/src/stream.ts
CHANGED
|
@@ -45,20 +45,7 @@ export type TransformStream<I = any, O = any> = TransformStreamPolyfill<I, O>;
|
|
|
45
45
|
export let TransformStream = TransformStreamPolyfill;
|
|
46
46
|
|
|
47
47
|
if (GLOBAL.ReadableStream && GLOBAL.WritableStream && GLOBAL.TransformStream) {
|
|
48
|
-
// Use browser native implementation
|
|
49
48
|
ReadableStream = GLOBAL.ReadableStream;
|
|
50
49
|
WritableStream = GLOBAL.WritableStream;
|
|
51
50
|
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
51
|
}
|
|
@@ -4,9 +4,9 @@ import type { StructValueType } from "@yume-chan/struct";
|
|
|
4
4
|
import { BufferedTransformStream } from "./buffered-transform.js";
|
|
5
5
|
|
|
6
6
|
export class StructDeserializeStream<
|
|
7
|
-
T extends Struct<any, any, any, any
|
|
7
|
+
T extends Struct<any, any, any, any>,
|
|
8
8
|
> extends BufferedTransformStream<StructValueType<T>> {
|
|
9
|
-
|
|
9
|
+
constructor(struct: T) {
|
|
10
10
|
super((stream) => {
|
|
11
11
|
return struct.deserialize(stream);
|
|
12
12
|
});
|
package/src/struct-serialize.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type Struct from "@yume-chan/struct";
|
|
|
3
3
|
import { TransformStream } from "./stream.js";
|
|
4
4
|
|
|
5
5
|
export class StructSerializeStream<
|
|
6
|
-
T extends Struct<any, any, any, any
|
|
6
|
+
T extends Struct<any, any, any, any>,
|
|
7
7
|
> extends TransformStream<T["TInit"], Uint8Array> {
|
|
8
8
|
constructor(struct: T) {
|
|
9
9
|
super({
|
package/src/wrap-readable.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ValueOrPromise } from "@yume-chan/struct";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
+
QueuingStrategy,
|
|
4
5
|
ReadableStreamDefaultController,
|
|
5
6
|
ReadableStreamDefaultReader,
|
|
6
7
|
} from "./stream.js";
|
|
7
8
|
import { ReadableStream } from "./stream.js";
|
|
8
9
|
|
|
9
10
|
export type WrapReadableStreamStart<T> = (
|
|
10
|
-
controller: ReadableStreamDefaultController<T
|
|
11
|
+
controller: ReadableStreamDefaultController<T>,
|
|
11
12
|
) => ValueOrPromise<ReadableStream<T>>;
|
|
12
13
|
|
|
13
14
|
export interface ReadableStreamWrapper<T> {
|
|
@@ -21,7 +22,7 @@ function getWrappedReadableStream<T>(
|
|
|
21
22
|
| ReadableStream<T>
|
|
22
23
|
| WrapReadableStreamStart<T>
|
|
23
24
|
| ReadableStreamWrapper<T>,
|
|
24
|
-
controller: ReadableStreamDefaultController<T
|
|
25
|
+
controller: ReadableStreamDefaultController<T>,
|
|
25
26
|
) {
|
|
26
27
|
if ("start" in wrapper) {
|
|
27
28
|
return wrapper.start(controller);
|
|
@@ -42,47 +43,51 @@ function getWrappedReadableStream<T>(
|
|
|
42
43
|
* 3. Convert native `ReadableStream`s to polyfilled ones so they can `pipe` between.
|
|
43
44
|
*/
|
|
44
45
|
export class WrapReadableStream<T> extends ReadableStream<T> {
|
|
45
|
-
|
|
46
|
+
readable!: ReadableStream<T>;
|
|
46
47
|
|
|
47
48
|
#reader!: ReadableStreamDefaultReader<T>;
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
constructor(
|
|
50
51
|
wrapper:
|
|
51
52
|
| ReadableStream<T>
|
|
52
53
|
| WrapReadableStreamStart<T>
|
|
53
|
-
| ReadableStreamWrapper<T
|
|
54
|
+
| ReadableStreamWrapper<T>,
|
|
55
|
+
strategy?: QueuingStrategy<T>,
|
|
54
56
|
) {
|
|
55
|
-
super(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
super(
|
|
58
|
+
{
|
|
59
|
+
start: async (controller) => {
|
|
60
|
+
// `start` is invoked before `ReadableStream`'s constructor finish,
|
|
61
|
+
// so using `this` synchronously causes
|
|
62
|
+
// "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
|
|
63
|
+
// Queue a microtask to avoid this.
|
|
64
|
+
await Promise.resolve();
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
66
|
+
this.readable = await getWrappedReadableStream(
|
|
67
|
+
wrapper,
|
|
68
|
+
controller,
|
|
69
|
+
);
|
|
70
|
+
this.#reader = this.readable.getReader();
|
|
71
|
+
},
|
|
72
|
+
pull: async (controller) => {
|
|
73
|
+
const result = await this.#reader.read();
|
|
74
|
+
if (result.done) {
|
|
75
|
+
controller.close();
|
|
76
|
+
if ("close" in wrapper) {
|
|
77
|
+
await wrapper.close?.();
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
controller.enqueue(result.value);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
cancel: async (reason) => {
|
|
84
|
+
await this.#reader.cancel(reason);
|
|
85
|
+
if ("cancel" in wrapper) {
|
|
86
|
+
await wrapper.cancel?.(reason);
|
|
81
87
|
}
|
|
82
|
-
}
|
|
83
|
-
controller.enqueue(result.value);
|
|
84
|
-
}
|
|
88
|
+
},
|
|
85
89
|
},
|
|
86
|
-
|
|
90
|
+
strategy,
|
|
91
|
+
);
|
|
87
92
|
}
|
|
88
93
|
}
|