@yume-chan/stream-extra 0.0.23 → 1.0.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/CHANGELOG.md +32 -2
- package/LICENSE +1 -1
- package/esm/buffered-transform.d.ts +3 -3
- package/esm/buffered-transform.d.ts.map +1 -1
- package/esm/buffered-transform.js.map +1 -1
- package/esm/buffered.d.ts +3 -6
- package/esm/buffered.d.ts.map +1 -1
- package/esm/buffered.js +87 -75
- package/esm/buffered.js.map +1 -1
- package/esm/concat.d.ts +4 -3
- package/esm/concat.d.ts.map +1 -1
- package/esm/concat.js +6 -4
- package/esm/concat.js.map +1 -1
- package/esm/consumable.d.ts +36 -43
- package/esm/consumable.d.ts.map +1 -1
- package/esm/consumable.js +74 -125
- package/esm/consumable.js.map +1 -1
- package/esm/distribution.d.ts +3 -2
- package/esm/distribution.d.ts.map +1 -1
- package/esm/distribution.js +22 -18
- 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 -6
- package/esm/duplex.js.map +1 -1
- package/esm/encoding.d.ts +21 -0
- package/esm/encoding.d.ts.map +1 -0
- package/esm/encoding.js +4 -0
- package/esm/encoding.js.map +1 -0
- package/esm/index.d.ts +4 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +4 -1
- package/esm/index.js.map +1 -1
- package/esm/maybe-consumable-ns.d.ts +16 -0
- package/esm/maybe-consumable-ns.d.ts.map +1 -0
- package/esm/maybe-consumable-ns.js +44 -0
- package/esm/maybe-consumable-ns.js.map +1 -0
- package/esm/maybe-consumable.d.ts +4 -0
- package/esm/maybe-consumable.d.ts.map +1 -0
- package/esm/maybe-consumable.js +2 -0
- package/esm/maybe-consumable.js.map +1 -0
- package/esm/push-readable.d.ts +16 -2
- package/esm/push-readable.d.ts.map +1 -1
- package/esm/push-readable.js +176 -17
- package/esm/push-readable.js.map +1 -1
- package/esm/stream.d.ts +7 -7
- package/esm/stream.d.ts.map +1 -1
- package/esm/stream.js +54 -5
- package/esm/stream.js.map +1 -1
- package/esm/struct-deserialize.d.ts +3 -4
- package/esm/struct-deserialize.d.ts.map +1 -1
- package/esm/struct-deserialize.js.map +1 -1
- package/esm/struct-serialize.d.ts +2 -2
- package/esm/struct-serialize.d.ts.map +1 -1
- package/esm/struct-serialize.js.map +1 -1
- package/esm/task.d.ts +5 -0
- package/esm/task.d.ts.map +1 -0
- package/esm/task.js +9 -0
- package/esm/task.js.map +1 -0
- package/esm/try-close.d.ts +10 -0
- package/esm/try-close.d.ts.map +1 -0
- package/esm/try-close.js +19 -0
- package/esm/try-close.js.map +1 -0
- package/esm/types.d.ts +34 -35
- package/esm/types.d.ts.map +1 -1
- package/esm/wrap-readable.d.ts +5 -4
- package/esm/wrap-readable.d.ts.map +1 -1
- package/esm/wrap-readable.js +14 -9
- package/esm/wrap-readable.js.map +1 -1
- package/esm/wrap-writable.d.ts +2 -2
- package/esm/wrap-writable.d.ts.map +1 -1
- package/esm/wrap-writable.js +4 -6
- package/esm/wrap-writable.js.map +1 -1
- package/package.json +11 -15
- package/src/buffered-transform.ts +2 -2
- package/src/buffered.ts +107 -83
- package/src/concat.ts +6 -4
- package/src/consumable.ts +132 -213
- package/src/distribution.ts +30 -20
- package/src/duplex.ts +4 -7
- package/src/encoding.ts +39 -0
- package/src/index.ts +4 -1
- package/src/maybe-consumable-ns.ts +75 -0
- package/src/maybe-consumable.ts +5 -0
- package/src/push-readable.ts +213 -19
- package/src/stream.ts +69 -9
- package/src/struct-deserialize.ts +4 -7
- package/src/struct-serialize.ts +3 -3
- package/src/task.ts +21 -0
- package/src/try-close.ts +44 -0
- package/src/types.ts +34 -34
- package/src/wrap-readable.ts +20 -14
- package/src/wrap-writable.ts +6 -9
- package/tsconfig.build.tsbuildinfo +1 -1
- package/CHANGELOG.json +0 -86
- package/esm/decode-utf8.d.ts +0 -5
- package/esm/decode-utf8.d.ts.map +0 -1
- package/esm/decode-utf8.js +0 -12
- package/esm/decode-utf8.js.map +0 -1
- package/src/decode-utf8.ts +0 -13
package/src/buffered.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import type { MaybePromiseLike } from "@yume-chan/async";
|
|
1
2
|
import type { AsyncExactReadable } from "@yume-chan/struct";
|
|
2
|
-
import { ExactReadableEndedError } from "@yume-chan/struct";
|
|
3
|
+
import { bipedal, ExactReadableEndedError } from "@yume-chan/struct";
|
|
3
4
|
|
|
4
5
|
import { PushReadableStream } from "./push-readable.js";
|
|
5
6
|
import type { ReadableStream, ReadableStreamDefaultReader } from "./stream.js";
|
|
6
|
-
|
|
7
|
-
const NOOP = () => {
|
|
8
|
-
// no-op
|
|
9
|
-
};
|
|
7
|
+
import { tryCancel } from "./try-close.js";
|
|
10
8
|
|
|
11
9
|
export class BufferedReadableStream implements AsyncExactReadable {
|
|
12
10
|
#buffered: Uint8Array | undefined;
|
|
11
|
+
// PERF: `subarray` is slow
|
|
12
|
+
// don't use it until absolutely necessary
|
|
13
13
|
#bufferedOffset = 0;
|
|
14
14
|
#bufferedLength = 0;
|
|
15
15
|
|
|
@@ -26,99 +26,124 @@ export class BufferedReadableStream implements AsyncExactReadable {
|
|
|
26
26
|
this.reader = stream.getReader();
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
#readBuffered(length: number) {
|
|
30
|
+
if (!this.#buffered) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const value = this.#buffered.subarray(
|
|
35
|
+
this.#bufferedOffset,
|
|
36
|
+
this.#bufferedOffset + length,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// PERF: Synchronous path for reading from internal buffer
|
|
40
|
+
if (this.#bufferedLength > length) {
|
|
41
|
+
this.#position += length;
|
|
42
|
+
this.#bufferedOffset += length;
|
|
43
|
+
this.#bufferedLength -= length;
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.#position += this.#bufferedLength;
|
|
48
|
+
this.#buffered = undefined;
|
|
49
|
+
this.#bufferedOffset = 0;
|
|
50
|
+
this.#bufferedLength = 0;
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async #readSource(length: number): Promise<Uint8Array> {
|
|
30
55
|
const { done, value } = await this.reader.read();
|
|
31
56
|
if (done) {
|
|
32
57
|
throw new ExactReadableEndedError();
|
|
33
58
|
}
|
|
59
|
+
|
|
60
|
+
if (value.length > length) {
|
|
61
|
+
this.#buffered = value;
|
|
62
|
+
this.#bufferedOffset = length;
|
|
63
|
+
this.#bufferedLength = value.length - length;
|
|
64
|
+
this.#position += length;
|
|
65
|
+
return value.subarray(0, length);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.#position += value.length;
|
|
34
69
|
return value;
|
|
35
70
|
}
|
|
36
71
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
iterateExactly(
|
|
73
|
+
length: number,
|
|
74
|
+
): Iterator<MaybePromiseLike<Uint8Array>, void, void> {
|
|
75
|
+
let state = this.#buffered ? 0 : 1;
|
|
76
|
+
return {
|
|
77
|
+
next: () => {
|
|
78
|
+
switch (state) {
|
|
79
|
+
case 0: {
|
|
80
|
+
const value = this.#readBuffered(length)!;
|
|
81
|
+
if (value.length === length) {
|
|
82
|
+
state = 2;
|
|
83
|
+
} else {
|
|
84
|
+
length -= value.length;
|
|
85
|
+
state = 1;
|
|
86
|
+
}
|
|
87
|
+
return { done: false, value };
|
|
88
|
+
}
|
|
89
|
+
case 1:
|
|
90
|
+
state = 3;
|
|
91
|
+
return {
|
|
92
|
+
done: false,
|
|
93
|
+
value: this.#readSource(length).then((value) => {
|
|
94
|
+
if (value.length === length) {
|
|
95
|
+
state = 2;
|
|
96
|
+
} else {
|
|
97
|
+
length -= value.length;
|
|
98
|
+
state = 1;
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
case 2:
|
|
104
|
+
return { done: true, value: undefined };
|
|
105
|
+
case 3:
|
|
106
|
+
throw new Error(
|
|
107
|
+
"Can't call `next` before previous Promise resolves",
|
|
108
|
+
);
|
|
109
|
+
default:
|
|
110
|
+
throw new Error("unreachable");
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
40
115
|
|
|
116
|
+
readExactly = bipedal(function* (
|
|
117
|
+
this: BufferedReadableStream,
|
|
118
|
+
then,
|
|
119
|
+
length: number,
|
|
120
|
+
) {
|
|
121
|
+
let result: Uint8Array | undefined;
|
|
122
|
+
let index = 0;
|
|
123
|
+
|
|
124
|
+
const initial = this.#readBuffered(length);
|
|
41
125
|
if (initial) {
|
|
126
|
+
if (initial.length === length) {
|
|
127
|
+
return initial;
|
|
128
|
+
}
|
|
129
|
+
|
|
42
130
|
result = new Uint8Array(length);
|
|
43
|
-
result.set(initial);
|
|
44
|
-
index
|
|
131
|
+
result.set(initial, index);
|
|
132
|
+
index += initial.length;
|
|
45
133
|
length -= initial.length;
|
|
46
134
|
} else {
|
|
47
|
-
const array = await this.#readSource();
|
|
48
|
-
if (array.length === length) {
|
|
49
|
-
this.#position += length;
|
|
50
|
-
return array;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (array.length > length) {
|
|
54
|
-
this.#buffered = array;
|
|
55
|
-
this.#bufferedOffset = length;
|
|
56
|
-
this.#bufferedLength = array.length - length;
|
|
57
|
-
this.#position += length;
|
|
58
|
-
return array.subarray(0, length);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
135
|
result = new Uint8Array(length);
|
|
62
|
-
result.set(array);
|
|
63
|
-
index = array.length;
|
|
64
|
-
length -= array.length;
|
|
65
|
-
this.#position += array.length;
|
|
66
136
|
}
|
|
67
137
|
|
|
68
138
|
while (length > 0) {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return result;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (array.length > length) {
|
|
77
|
-
this.#buffered = array;
|
|
78
|
-
this.#bufferedOffset = length;
|
|
79
|
-
this.#bufferedLength = array.length - length;
|
|
80
|
-
result.set(array.subarray(0, length), index);
|
|
81
|
-
this.#position += length;
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
result.set(array, index);
|
|
86
|
-
index += array.length;
|
|
87
|
-
length -= array.length;
|
|
88
|
-
this.#position += array.length;
|
|
139
|
+
const value = yield* then(this.#readSource(length));
|
|
140
|
+
result.set(value, index);
|
|
141
|
+
index += value.length;
|
|
142
|
+
length -= value.length;
|
|
89
143
|
}
|
|
90
144
|
|
|
91
145
|
return result;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
*
|
|
96
|
-
* @param length
|
|
97
|
-
* @returns
|
|
98
|
-
*/
|
|
99
|
-
readExactly(length: number): Uint8Array | Promise<Uint8Array> {
|
|
100
|
-
// PERF: Add a synchronous path for reading from internal buffer
|
|
101
|
-
if (this.#buffered) {
|
|
102
|
-
const array = this.#buffered;
|
|
103
|
-
const offset = this.#bufferedOffset;
|
|
104
|
-
if (this.#bufferedLength > length) {
|
|
105
|
-
// PERF: `subarray` is slow
|
|
106
|
-
// don't use it until absolutely necessary
|
|
107
|
-
this.#bufferedOffset += length;
|
|
108
|
-
this.#bufferedLength -= length;
|
|
109
|
-
this.#position += length;
|
|
110
|
-
return array.subarray(offset, offset + length);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.#buffered = undefined;
|
|
114
|
-
this.#bufferedLength = 0;
|
|
115
|
-
this.#bufferedOffset = 0;
|
|
116
|
-
this.#position += array.length - offset;
|
|
117
|
-
return this.#readAsync(length, array.subarray(offset));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return this.#readAsync(length);
|
|
121
|
-
}
|
|
146
|
+
});
|
|
122
147
|
|
|
123
148
|
/**
|
|
124
149
|
* Return a readable stream with unconsumed data (if any) and
|
|
@@ -133,8 +158,7 @@ export class BufferedReadableStream implements AsyncExactReadable {
|
|
|
133
158
|
await controller.enqueue(buffered);
|
|
134
159
|
|
|
135
160
|
controller.abortSignal.addEventListener("abort", () => {
|
|
136
|
-
|
|
137
|
-
this.reader.cancel().catch(NOOP);
|
|
161
|
+
void tryCancel(this.reader);
|
|
138
162
|
});
|
|
139
163
|
|
|
140
164
|
// Manually pipe the stream
|
|
@@ -142,9 +166,9 @@ export class BufferedReadableStream implements AsyncExactReadable {
|
|
|
142
166
|
const { done, value } = await this.reader.read();
|
|
143
167
|
if (done) {
|
|
144
168
|
return;
|
|
145
|
-
} else {
|
|
146
|
-
await controller.enqueue(value);
|
|
147
169
|
}
|
|
170
|
+
|
|
171
|
+
await controller.enqueue(value);
|
|
148
172
|
}
|
|
149
173
|
});
|
|
150
174
|
} else {
|
package/src/concat.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PromiseResolver } from "@yume-chan/async";
|
|
2
|
+
import { EmptyUint8Array } from "@yume-chan/struct";
|
|
2
3
|
|
|
3
4
|
import type { ReadableStreamDefaultController } from "./stream.js";
|
|
4
5
|
import { ReadableStream, WritableStream } from "./stream.js";
|
|
@@ -81,9 +82,10 @@ export interface ConcatBufferReadableStream
|
|
|
81
82
|
* A `TransformStream` that concatenates `Uint8Array`s.
|
|
82
83
|
*
|
|
83
84
|
* If you want to decode the result as string,
|
|
84
|
-
* prefer `.pipeThrough(new
|
|
85
|
-
* than `.pipeThough(new ConcatBufferStream()).pipeThrough(new
|
|
86
|
-
* because
|
|
85
|
+
* prefer `.pipeThrough(new TextDecoderStream()).pipeThrough(new ConcatStringStream())`,
|
|
86
|
+
* than `.pipeThough(new ConcatBufferStream()).pipeThrough(new TextDecoderStream())`,
|
|
87
|
+
* because of JavaScript engine optimizations,
|
|
88
|
+
* concatenating strings is faster than concatenating `Uint8Array`s.
|
|
87
89
|
*/
|
|
88
90
|
export class ConcatBufferStream {
|
|
89
91
|
#segments: Uint8Array[] = [];
|
|
@@ -99,7 +101,7 @@ export class ConcatBufferStream {
|
|
|
99
101
|
let offset = 0;
|
|
100
102
|
switch (this.#segments.length) {
|
|
101
103
|
case 0:
|
|
102
|
-
result =
|
|
104
|
+
result = EmptyUint8Array;
|
|
103
105
|
break;
|
|
104
106
|
case 1:
|
|
105
107
|
result = this.#segments[0]!;
|
package/src/consumable.ts
CHANGED
|
@@ -1,125 +1,85 @@
|
|
|
1
|
-
import { PromiseResolver } from "@yume-chan/async";
|
|
1
|
+
import { PromiseResolver, isPromiseLike } from "@yume-chan/async";
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
4
|
QueuingStrategy,
|
|
5
5
|
WritableStreamDefaultController,
|
|
6
6
|
WritableStreamDefaultWriter,
|
|
7
7
|
} from "./stream.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface Console {
|
|
15
|
-
createTask(name: string): Task;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface GlobalExtension {
|
|
19
|
-
console: Console;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// `createTask` allows browser DevTools to track the call stack across async boundaries.
|
|
23
|
-
const { console } = globalThis as unknown as GlobalExtension;
|
|
24
|
-
const createTask: Console["createTask"] =
|
|
25
|
-
console.createTask?.bind(console) ??
|
|
26
|
-
(() => ({
|
|
27
|
-
run(callback) {
|
|
28
|
-
return callback();
|
|
29
|
-
},
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
|
-
export class Consumable<T> {
|
|
33
|
-
readonly #task: Task;
|
|
34
|
-
readonly #resolver: PromiseResolver<void>;
|
|
35
|
-
|
|
36
|
-
readonly value: T;
|
|
37
|
-
readonly consumed: Promise<void>;
|
|
38
|
-
|
|
39
|
-
constructor(value: T) {
|
|
40
|
-
this.#task = createTask("Consumable");
|
|
41
|
-
this.value = value;
|
|
42
|
-
this.#resolver = new PromiseResolver<void>();
|
|
43
|
-
this.consumed = this.#resolver.promise;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
consume() {
|
|
47
|
-
this.#resolver.resolve();
|
|
48
|
-
}
|
|
8
|
+
import {
|
|
9
|
+
ReadableStream as NativeReadableStream,
|
|
10
|
+
WritableStream as NativeWritableStream,
|
|
11
|
+
} from "./stream.js";
|
|
12
|
+
import type { Task } from "./task.js";
|
|
13
|
+
import { createTask } from "./task.js";
|
|
49
14
|
|
|
50
|
-
|
|
51
|
-
|
|
15
|
+
// Workaround https://github.com/evanw/esbuild/issues/3923
|
|
16
|
+
class WritableStream<in T> extends NativeWritableStream<Consumable<T>> {
|
|
17
|
+
static async write<T>(
|
|
18
|
+
writer: WritableStreamDefaultWriter<Consumable<T>>,
|
|
19
|
+
value: T,
|
|
20
|
+
) {
|
|
21
|
+
const consumable = new Consumable(value);
|
|
22
|
+
await writer.write(consumable);
|
|
23
|
+
await consumable.consumed;
|
|
52
24
|
}
|
|
53
25
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
26
|
+
constructor(
|
|
27
|
+
sink: Consumable.WritableStreamSink<T>,
|
|
28
|
+
strategy?: QueuingStrategy<T>,
|
|
29
|
+
) {
|
|
30
|
+
let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
|
|
31
|
+
if (strategy) {
|
|
32
|
+
wrappedStrategy = {};
|
|
33
|
+
if ("highWaterMark" in strategy) {
|
|
34
|
+
wrappedStrategy.highWaterMark = strategy.highWaterMark;
|
|
35
|
+
}
|
|
36
|
+
if ("size" in strategy) {
|
|
37
|
+
wrappedStrategy.size = (chunk) => {
|
|
38
|
+
return strategy.size!(
|
|
39
|
+
chunk instanceof Consumable ? chunk.value : chunk,
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
63
43
|
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function enqueue<T>(
|
|
68
|
-
controller: { enqueue: (chunk: Consumable<T>) => void },
|
|
69
|
-
chunk: T,
|
|
70
|
-
) {
|
|
71
|
-
const output = new Consumable(chunk);
|
|
72
|
-
controller.enqueue(output);
|
|
73
|
-
await output.consumed;
|
|
74
|
-
}
|
|
75
44
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
45
|
+
super(
|
|
46
|
+
{
|
|
47
|
+
start(controller) {
|
|
48
|
+
return sink.start?.(controller);
|
|
49
|
+
},
|
|
50
|
+
async write(chunk, controller) {
|
|
51
|
+
await chunk.tryConsume((chunk) =>
|
|
52
|
+
sink.write?.(chunk, controller),
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
abort(reason) {
|
|
56
|
+
return sink.abort?.(reason);
|
|
57
|
+
},
|
|
58
|
+
close() {
|
|
59
|
+
return sink.close?.();
|
|
60
|
+
},
|
|
81
61
|
},
|
|
82
|
-
|
|
62
|
+
wrappedStrategy,
|
|
63
|
+
);
|
|
83
64
|
}
|
|
84
65
|
}
|
|
85
66
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
chunk.consume();
|
|
95
|
-
},
|
|
96
|
-
});
|
|
67
|
+
class ReadableStream<T> extends NativeReadableStream<Consumable<T>> {
|
|
68
|
+
static async enqueue<T>(
|
|
69
|
+
controller: { enqueue: (chunk: Consumable<T>) => void },
|
|
70
|
+
chunk: T,
|
|
71
|
+
) {
|
|
72
|
+
const output = new Consumable(chunk);
|
|
73
|
+
controller.enqueue(output);
|
|
74
|
+
await output.consumed;
|
|
97
75
|
}
|
|
98
|
-
}
|
|
99
76
|
|
|
100
|
-
export interface ConsumableReadableStreamController<T> {
|
|
101
|
-
enqueue(chunk: T): Promise<void>;
|
|
102
|
-
close(): void;
|
|
103
|
-
error(reason: any): void;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface ConsumableReadableStreamSource<T> {
|
|
107
|
-
start?(
|
|
108
|
-
controller: ConsumableReadableStreamController<T>,
|
|
109
|
-
): void | PromiseLike<void>;
|
|
110
|
-
pull?(
|
|
111
|
-
controller: ConsumableReadableStreamController<T>,
|
|
112
|
-
): void | PromiseLike<void>;
|
|
113
|
-
cancel?(reason: any): void | PromiseLike<void>;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export class ConsumableReadableStream<T> extends ReadableStream<Consumable<T>> {
|
|
117
77
|
constructor(
|
|
118
|
-
source:
|
|
78
|
+
source: Consumable.ReadableStreamSource<T>,
|
|
119
79
|
strategy?: QueuingStrategy<T>,
|
|
120
80
|
) {
|
|
121
81
|
let wrappedController:
|
|
122
|
-
|
|
|
82
|
+
| Consumable.ReadableStreamController<T>
|
|
123
83
|
| undefined;
|
|
124
84
|
|
|
125
85
|
let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
|
|
@@ -140,7 +100,7 @@ export class ConsumableReadableStream<T> extends ReadableStream<Consumable<T>> {
|
|
|
140
100
|
async start(controller) {
|
|
141
101
|
wrappedController = {
|
|
142
102
|
async enqueue(chunk) {
|
|
143
|
-
await enqueue(controller, chunk);
|
|
103
|
+
await ReadableStream.enqueue(controller, chunk);
|
|
144
104
|
},
|
|
145
105
|
close() {
|
|
146
106
|
controller.close();
|
|
@@ -164,128 +124,87 @@ export class ConsumableReadableStream<T> extends ReadableStream<Consumable<T>> {
|
|
|
164
124
|
}
|
|
165
125
|
}
|
|
166
126
|
|
|
167
|
-
export
|
|
168
|
-
|
|
169
|
-
controller: WritableStreamDefaultController,
|
|
170
|
-
): void | PromiseLike<void>;
|
|
171
|
-
write?(
|
|
172
|
-
chunk: T,
|
|
173
|
-
controller: WritableStreamDefaultController,
|
|
174
|
-
): void | PromiseLike<void>;
|
|
175
|
-
abort?(reason: any): void | PromiseLike<void>;
|
|
176
|
-
close?(): void | PromiseLike<void>;
|
|
177
|
-
}
|
|
127
|
+
export class Consumable<T> {
|
|
128
|
+
static readonly WritableStream = WritableStream;
|
|
178
129
|
|
|
179
|
-
|
|
180
|
-
static async write<T>(
|
|
181
|
-
writer: WritableStreamDefaultWriter<Consumable<T>>,
|
|
182
|
-
value: T,
|
|
183
|
-
) {
|
|
184
|
-
const consumable = new Consumable(value);
|
|
185
|
-
await writer.write(consumable);
|
|
186
|
-
await consumable.consumed;
|
|
187
|
-
}
|
|
130
|
+
static readonly ReadableStream = ReadableStream;
|
|
188
131
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
strategy?: QueuingStrategy<T>,
|
|
192
|
-
) {
|
|
193
|
-
let wrappedStrategy: QueuingStrategy<Consumable<T>> | undefined;
|
|
194
|
-
if (strategy) {
|
|
195
|
-
wrappedStrategy = {};
|
|
196
|
-
if ("highWaterMark" in strategy) {
|
|
197
|
-
wrappedStrategy.highWaterMark = strategy.highWaterMark;
|
|
198
|
-
}
|
|
199
|
-
if ("size" in strategy) {
|
|
200
|
-
wrappedStrategy.size = (chunk) => {
|
|
201
|
-
return strategy.size!(chunk.value);
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
}
|
|
132
|
+
readonly #task: Task;
|
|
133
|
+
readonly #resolver: PromiseResolver<void>;
|
|
205
134
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
);
|
|
215
|
-
},
|
|
216
|
-
abort(reason) {
|
|
217
|
-
return sink.abort?.(reason);
|
|
218
|
-
},
|
|
219
|
-
close() {
|
|
220
|
-
return sink.close?.();
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
wrappedStrategy,
|
|
224
|
-
);
|
|
135
|
+
readonly value: T;
|
|
136
|
+
readonly consumed: Promise<void>;
|
|
137
|
+
|
|
138
|
+
constructor(value: T) {
|
|
139
|
+
this.#task = createTask("Consumable");
|
|
140
|
+
this.value = value;
|
|
141
|
+
this.#resolver = new PromiseResolver<void>();
|
|
142
|
+
this.consumed = this.#resolver.promise;
|
|
225
143
|
}
|
|
226
|
-
}
|
|
227
144
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
): void | PromiseLike<void>;
|
|
232
|
-
transform?(
|
|
233
|
-
chunk: I,
|
|
234
|
-
controller: ConsumableReadableStreamController<O>,
|
|
235
|
-
): void | PromiseLike<void>;
|
|
236
|
-
flush?(
|
|
237
|
-
controller: ConsumableReadableStreamController<O>,
|
|
238
|
-
): void | PromiseLike<void>;
|
|
239
|
-
}
|
|
145
|
+
consume() {
|
|
146
|
+
this.#resolver.resolve();
|
|
147
|
+
}
|
|
240
148
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
> {
|
|
245
|
-
constructor(transformer: ConsumableTransformer<I, O>) {
|
|
246
|
-
let wrappedController:
|
|
247
|
-
| ConsumableReadableStreamController<O>
|
|
248
|
-
| undefined;
|
|
149
|
+
error(error: unknown) {
|
|
150
|
+
this.#resolver.reject(error);
|
|
151
|
+
}
|
|
249
152
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
153
|
+
tryConsume<U>(callback: (value: T) => U) {
|
|
154
|
+
try {
|
|
155
|
+
let result = this.#task.run(() => callback(this.value));
|
|
156
|
+
if (isPromiseLike(result)) {
|
|
157
|
+
result = result.then(
|
|
158
|
+
(value) => {
|
|
159
|
+
this.#resolver.resolve();
|
|
160
|
+
return value;
|
|
258
161
|
},
|
|
259
|
-
|
|
260
|
-
|
|
162
|
+
(e) => {
|
|
163
|
+
this.#resolver.reject(e);
|
|
164
|
+
throw e;
|
|
261
165
|
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
},
|
|
272
|
-
async flush() {
|
|
273
|
-
await transformer.flush?.(wrappedController!);
|
|
274
|
-
},
|
|
275
|
-
});
|
|
166
|
+
) as U;
|
|
167
|
+
} else {
|
|
168
|
+
this.#resolver.resolve();
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
} catch (e) {
|
|
172
|
+
this.#resolver.reject(e);
|
|
173
|
+
throw e;
|
|
174
|
+
}
|
|
276
175
|
}
|
|
277
176
|
}
|
|
278
177
|
|
|
279
|
-
export
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
178
|
+
export namespace Consumable {
|
|
179
|
+
export interface WritableStreamSink<in T> {
|
|
180
|
+
start?(
|
|
181
|
+
controller: WritableStreamDefaultController,
|
|
182
|
+
): void | PromiseLike<void>;
|
|
183
|
+
write?(
|
|
184
|
+
chunk: T,
|
|
185
|
+
controller: WritableStreamDefaultController,
|
|
186
|
+
): void | PromiseLike<void>;
|
|
187
|
+
abort?(reason: unknown): void | PromiseLike<void>;
|
|
188
|
+
close?(): void | PromiseLike<void>;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export type WritableStream<in T> = typeof Consumable.WritableStream<T>;
|
|
192
|
+
|
|
193
|
+
export interface ReadableStreamController<T> {
|
|
194
|
+
enqueue(chunk: T): Promise<void>;
|
|
195
|
+
close(): void;
|
|
196
|
+
error(reason: unknown): void;
|
|
290
197
|
}
|
|
198
|
+
|
|
199
|
+
export interface ReadableStreamSource<T> {
|
|
200
|
+
start?(
|
|
201
|
+
controller: ReadableStreamController<T>,
|
|
202
|
+
): void | PromiseLike<void>;
|
|
203
|
+
pull?(
|
|
204
|
+
controller: ReadableStreamController<T>,
|
|
205
|
+
): void | PromiseLike<void>;
|
|
206
|
+
cancel?(reason: unknown): void | PromiseLike<void>;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export type ReadableStream<T> = typeof Consumable.ReadableStream<T>;
|
|
291
210
|
}
|