@yume-chan/stream-extra 2.6.1 → 3.0.0-beta.1

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.
Files changed (162) hide show
  1. package/LICENSE +1 -1
  2. package/esm/buffered-transform.d.ts +2 -2
  3. package/esm/buffered-transform.d.ts.map +1 -1
  4. package/esm/buffered-transform.js +16 -16
  5. package/esm/buffered-transform.js.map +1 -1
  6. package/esm/buffered.d.ts +1 -1
  7. package/esm/buffered.d.ts.map +1 -1
  8. package/esm/buffered.js.map +1 -1
  9. package/esm/concat.d.ts +25 -16
  10. package/esm/concat.d.ts.map +1 -1
  11. package/esm/concat.js +124 -93
  12. package/esm/concat.js.map +1 -1
  13. package/esm/consumable/readable.d.ts +2 -2
  14. package/esm/consumable/readable.d.ts.map +1 -1
  15. package/esm/consumable/readable.js +1 -1
  16. package/esm/consumable/readable.js.map +1 -1
  17. package/esm/consumable/wrap-byte-readable.d.ts +5 -2
  18. package/esm/consumable/wrap-byte-readable.d.ts.map +1 -1
  19. package/esm/consumable/wrap-byte-readable.js +12 -6
  20. package/esm/consumable/wrap-byte-readable.js.map +1 -1
  21. package/esm/consumable/wrap-writable.d.ts +1 -1
  22. package/esm/consumable/wrap-writable.d.ts.map +1 -1
  23. package/esm/consumable/wrap-writable.js +1 -1
  24. package/esm/consumable/wrap-writable.js.map +1 -1
  25. package/esm/consumable/writable.d.ts +2 -2
  26. package/esm/consumable/writable.d.ts.map +1 -1
  27. package/esm/consumable/writable.js +1 -1
  28. package/esm/consumable/writable.js.map +1 -1
  29. package/esm/consumable.d.ts +4 -0
  30. package/esm/consumable.d.ts.map +1 -1
  31. package/esm/consumable.js +5 -0
  32. package/esm/consumable.js.map +1 -1
  33. package/esm/distribution.d.ts +1 -1
  34. package/esm/distribution.d.ts.map +1 -1
  35. package/esm/distribution.js +1 -1
  36. package/esm/distribution.js.map +1 -1
  37. package/esm/duplex.d.ts +2 -2
  38. package/esm/duplex.d.ts.map +1 -1
  39. package/esm/duplex.js +1 -1
  40. package/esm/duplex.js.map +1 -1
  41. package/esm/global/abort-signal.d.ts +87 -0
  42. package/esm/global/abort-signal.d.ts.map +1 -0
  43. package/esm/global/abort-signal.js +4 -0
  44. package/esm/global/abort-signal.js.map +1 -0
  45. package/esm/global/compression.d.ts +15 -0
  46. package/esm/global/compression.d.ts.map +1 -0
  47. package/esm/global/compression.js +4 -0
  48. package/esm/global/compression.js.map +1 -0
  49. package/esm/global/encoding.d.ts +25 -0
  50. package/esm/global/encoding.d.ts.map +1 -0
  51. package/esm/global/encoding.js +4 -0
  52. package/esm/global/encoding.js.map +1 -0
  53. package/esm/global/event.d.ts +191 -0
  54. package/esm/global/event.d.ts.map +1 -0
  55. package/esm/global/event.js +4 -0
  56. package/esm/global/event.js.map +1 -0
  57. package/esm/global/index.d.ts +7 -0
  58. package/esm/global/index.d.ts.map +1 -0
  59. package/esm/global/index.js +7 -0
  60. package/esm/global/index.js.map +1 -0
  61. package/esm/{types.d.ts → global/streams.d.ts} +136 -222
  62. package/esm/global/streams.d.ts.map +1 -0
  63. package/esm/{stream.js → global/streams.js} +16 -8
  64. package/esm/global/streams.js.map +1 -0
  65. package/esm/global/timer.d.ts +13 -0
  66. package/esm/global/timer.d.ts.map +1 -0
  67. package/esm/global/timer.js +4 -0
  68. package/esm/global/timer.js.map +1 -0
  69. package/esm/global/utils.d.ts +9 -0
  70. package/esm/global/utils.d.ts.map +1 -0
  71. package/esm/global/utils.js +5 -0
  72. package/esm/global/utils.js.map +1 -0
  73. package/esm/index.d.ts +2 -2
  74. package/esm/index.d.ts.map +1 -1
  75. package/esm/index.js +2 -2
  76. package/esm/index.js.map +1 -1
  77. package/esm/inspect.d.ts +6 -2
  78. package/esm/inspect.d.ts.map +1 -1
  79. package/esm/inspect.js +10 -4
  80. package/esm/inspect.js.map +1 -1
  81. package/esm/maybe-consumable/wrap-writable.d.ts +1 -1
  82. package/esm/maybe-consumable/wrap-writable.d.ts.map +1 -1
  83. package/esm/maybe-consumable/wrap-writable.js +1 -1
  84. package/esm/maybe-consumable/wrap-writable.js.map +1 -1
  85. package/esm/maybe-consumable/writable.d.ts +2 -2
  86. package/esm/maybe-consumable/writable.d.ts.map +1 -1
  87. package/esm/maybe-consumable/writable.js +1 -1
  88. package/esm/maybe-consumable/writable.js.map +1 -1
  89. package/esm/pipe-from.d.ts +3 -3
  90. package/esm/pipe-from.d.ts.map +1 -1
  91. package/esm/pipe-from.js +1 -1
  92. package/esm/pipe-from.js.map +1 -1
  93. package/esm/push-readable.d.ts +2 -2
  94. package/esm/push-readable.d.ts.map +1 -1
  95. package/esm/push-readable.js +1 -1
  96. package/esm/push-readable.js.map +1 -1
  97. package/esm/split-string.d.ts +6 -2
  98. package/esm/split-string.d.ts.map +1 -1
  99. package/esm/split-string.js +28 -7
  100. package/esm/split-string.js.map +1 -1
  101. package/esm/struct-deserialize.js.map +1 -1
  102. package/esm/struct-serialize.d.ts +1 -1
  103. package/esm/struct-serialize.d.ts.map +1 -1
  104. package/esm/struct-serialize.js +1 -1
  105. package/esm/struct-serialize.js.map +1 -1
  106. package/esm/try-close.d.ts +3 -5
  107. package/esm/try-close.d.ts.map +1 -1
  108. package/esm/try-close.js.map +1 -1
  109. package/esm/wrap-readable.d.ts +2 -2
  110. package/esm/wrap-readable.d.ts.map +1 -1
  111. package/esm/wrap-readable.js +1 -1
  112. package/esm/wrap-readable.js.map +1 -1
  113. package/esm/wrap-writable.d.ts +2 -2
  114. package/esm/wrap-writable.d.ts.map +1 -1
  115. package/esm/wrap-writable.js +1 -1
  116. package/esm/wrap-writable.js.map +1 -1
  117. package/package.json +10 -10
  118. package/src/buffered-transform.ts +24 -25
  119. package/src/buffered.ts +4 -1
  120. package/src/concat.ts +161 -118
  121. package/src/consumable/readable.ts +2 -2
  122. package/src/consumable/wrap-byte-readable.ts +20 -7
  123. package/src/consumable/wrap-writable.ts +1 -1
  124. package/src/consumable/writable.ts +2 -2
  125. package/src/consumable.ts +8 -0
  126. package/src/distribution.ts +1 -1
  127. package/src/duplex.ts +2 -2
  128. package/src/global/abort-signal.ts +129 -0
  129. package/src/global/compression.ts +28 -0
  130. package/src/global/encoding.ts +36 -0
  131. package/src/global/event.ts +220 -0
  132. package/src/global/index.ts +6 -0
  133. package/src/{types.ts → global/streams.ts} +266 -244
  134. package/src/global/timer.ts +18 -0
  135. package/src/global/utils.ts +14 -0
  136. package/src/index.ts +2 -2
  137. package/src/inspect.ts +18 -4
  138. package/src/maybe-consumable/wrap-writable.ts +1 -1
  139. package/src/maybe-consumable/writable.ts +3 -3
  140. package/src/pipe-from.ts +2 -2
  141. package/src/push-readable.ts +2 -2
  142. package/src/split-string.ts +41 -7
  143. package/src/struct-deserialize.ts +1 -1
  144. package/src/struct-serialize.ts +1 -1
  145. package/src/try-close.ts +0 -12
  146. package/src/wrap-readable.ts +2 -2
  147. package/src/wrap-writable.ts +5 -2
  148. package/tsconfig.build.json +1 -6
  149. package/CHANGELOG.md +0 -102
  150. package/esm/encoding.d.ts +0 -21
  151. package/esm/encoding.d.ts.map +0 -1
  152. package/esm/encoding.js +0 -4
  153. package/esm/encoding.js.map +0 -1
  154. package/esm/stream.d.ts +0 -25
  155. package/esm/stream.d.ts.map +0 -1
  156. package/esm/stream.js.map +0 -1
  157. package/esm/types.d.ts.map +0 -1
  158. package/esm/types.js +0 -3
  159. package/esm/types.js.map +0 -1
  160. package/src/encoding.ts +0 -39
  161. package/src/stream.ts +0 -106
  162. package/tsconfig.build.tsbuildinfo +0 -1
@@ -2,18 +2,19 @@ import type { MaybePromiseLike } from "@yume-chan/async";
2
2
  import { StructEmptyError } from "@yume-chan/struct";
3
3
 
4
4
  import { BufferedReadableStream } from "./buffered.js";
5
- import type { PushReadableStreamController } from "./push-readable.js";
6
- import { PushReadableStream } from "./push-readable.js";
7
5
  import type {
8
6
  ReadableWritablePair,
9
7
  WritableStreamDefaultController,
10
- } from "./stream.js";
11
- import { ReadableStream, WritableStream } from "./stream.js";
8
+ } from "./global/index.js";
9
+ import { ReadableStream, WritableStream } from "./global/index.js";
10
+ import type { PushReadableStreamController } from "./push-readable.js";
11
+ import { PushReadableStream } from "./push-readable.js";
12
12
 
13
13
  // TODO: BufferedTransformStream: find better implementation
14
- export class BufferedTransformStream<T>
15
- implements ReadableWritablePair<T, Uint8Array>
16
- {
14
+ export class BufferedTransformStream<T> implements ReadableWritablePair<
15
+ T,
16
+ Uint8Array
17
+ > {
17
18
  #readable: ReadableStream<T>;
18
19
  get readable() {
19
20
  return this.#readable;
@@ -29,15 +30,28 @@ export class BufferedTransformStream<T>
29
30
  ) {
30
31
  // Convert incoming chunks to a `BufferedReadableStream`
31
32
  let bufferedStreamController!: PushReadableStreamController<Uint8Array>;
32
-
33
- let writableStreamController!: WritableStreamDefaultController;
34
-
35
33
  const buffered = new BufferedReadableStream(
36
34
  new PushReadableStream<Uint8Array>((controller) => {
37
35
  bufferedStreamController = controller;
38
36
  }),
39
37
  );
40
38
 
39
+ let writableStreamController!: WritableStreamDefaultController;
40
+ this.#writable = new WritableStream({
41
+ start(controller) {
42
+ writableStreamController = controller;
43
+ },
44
+ async write(chunk) {
45
+ await bufferedStreamController.enqueue(chunk);
46
+ },
47
+ abort() {
48
+ bufferedStreamController.close();
49
+ },
50
+ close() {
51
+ bufferedStreamController.close();
52
+ },
53
+ });
54
+
41
55
  this.#readable = new ReadableStream<T>({
42
56
  async pull(controller) {
43
57
  try {
@@ -62,20 +76,5 @@ export class BufferedTransformStream<T>
62
76
  return writableStreamController.error(reason);
63
77
  },
64
78
  });
65
-
66
- this.#writable = new WritableStream({
67
- start(controller) {
68
- writableStreamController = controller;
69
- },
70
- async write(chunk) {
71
- await bufferedStreamController.enqueue(chunk);
72
- },
73
- abort() {
74
- bufferedStreamController.close();
75
- },
76
- close() {
77
- bufferedStreamController.close();
78
- },
79
- });
80
79
  }
81
80
  }
package/src/buffered.ts CHANGED
@@ -2,8 +2,11 @@ import type { MaybePromiseLike } from "@yume-chan/async";
2
2
  import type { AsyncExactReadable } from "@yume-chan/struct";
3
3
  import { bipedal, ExactReadableEndedError } from "@yume-chan/struct";
4
4
 
5
+ import type {
6
+ ReadableStream,
7
+ ReadableStreamDefaultReader,
8
+ } from "./global/index.js";
5
9
  import { PushReadableStream } from "./push-readable.js";
6
- import type { ReadableStream, ReadableStreamDefaultReader } from "./stream.js";
7
10
  import { tryCancel } from "./try-close.js";
8
11
 
9
12
  export class BufferedReadableStream implements AsyncExactReadable {
package/src/concat.ts CHANGED
@@ -1,162 +1,205 @@
1
1
  import { PromiseResolver } from "@yume-chan/async";
2
2
  import { EmptyUint8Array } from "@yume-chan/struct";
3
3
 
4
- import type { ReadableStreamDefaultController } from "./stream.js";
5
- import { ReadableStream, WritableStream } from "./stream.js";
6
-
7
- export interface ConcatStringReadableStream
8
- extends ReadableStream<string>,
9
- Promise<string> {}
4
+ import type { ReadableWritablePair } from "./global/index.js";
5
+ import { ReadableStream, WritableStream } from "./global/index.js";
10
6
 
11
7
  // `TransformStream` only calls its `source.flush` method when its `readable` is being read.
12
- // If the user want to use the `Promise` interface, the `flush` method will never be called,
8
+ // If the user wants to use the `Promise` interface, the `flush` method will never be called,
13
9
  // so the `PromiseResolver` will never be resolved.
14
10
  // Thus we need to implement our own `TransformStream` using a `WritableStream` and a `ReadableStream`.
11
+ export class AccumulateStream<
12
+ Input,
13
+ Output,
14
+ Accumulated = Output,
15
+ > implements ReadableWritablePair<Output, Input> {
16
+ #current: Accumulated;
15
17
 
16
- /**
17
- * A `TransformStream` that concatenates strings.
18
- *
19
- * Its `readable` is also a `Promise<string>`, so it's possible to `await` it to get the result.
20
- *
21
- * ```ts
22
- * const result: string = await readable.pipeThrough(new ConcatStringStream());
23
- * ```
24
- */
25
- export class ConcatStringStream {
26
- // PERF: rope (concat strings) is faster than `[].join('')`
27
- #result = "";
18
+ #write: (chunk: Input, current: Accumulated) => Accumulated;
19
+ #finalize: (current: Accumulated) => Output;
28
20
 
29
- #resolver = new PromiseResolver<string>();
21
+ #resolver = new PromiseResolver<Output>();
30
22
 
31
- #writable = new WritableStream<string>({
23
+ #writable = new WritableStream<Input>({
32
24
  write: (chunk) => {
33
- this.#result += chunk;
25
+ try {
26
+ this.#current = this.#write(chunk, this.#current);
27
+ } catch (e) {
28
+ this.#resolver.reject(e);
29
+ throw e;
30
+ }
34
31
  },
35
32
  close: () => {
36
- this.#resolver.resolve(this.#result);
37
- this.#readableController.enqueue(this.#result);
38
- this.#readableController.close();
33
+ try {
34
+ const output = this.#finalize(this.#current);
35
+ this.#resolver.resolve(output);
36
+ } catch (e) {
37
+ this.#resolver.reject(e);
38
+ throw e;
39
+ }
39
40
  },
40
41
  abort: (reason) => {
41
42
  this.#resolver.reject(reason);
42
- this.#readableController.error(reason);
43
43
  },
44
44
  });
45
- get writable(): WritableStream<string> {
45
+ get writable() {
46
46
  return this.#writable;
47
47
  }
48
48
 
49
- #readableController!: ReadableStreamDefaultController<string>;
50
- #readable = new ReadableStream<string>({
51
- start: (controller) => {
52
- this.#readableController = controller;
49
+ #readable = new ReadableStream<Output>(
50
+ {
51
+ pull: async (controller) => {
52
+ const output = await this.#resolver.promise;
53
+ controller.enqueue(output);
54
+ controller.close();
55
+ },
56
+ // Ignore `abort` event since the user might use the `Promise` interface.
53
57
  },
54
- }) as ConcatStringReadableStream;
55
- get readable(): ConcatStringReadableStream {
58
+ // `highWaterMark: 0` makes the `pull` method
59
+ // only be called when the `ReadableStream` is being read.
60
+ // If the user only uses the `Promise` interface,
61
+ // it's unnecessary to run the `pull` method.
62
+ { highWaterMark: 0 },
63
+ ) as ReadableStream<Output> &
64
+ Omit<Promise<Output>, typeof Symbol.toStringTag>;
65
+ get readable() {
56
66
  return this.#readable;
57
67
  }
58
68
 
59
- constructor() {
60
- void Object.defineProperties(this.#readable, {
61
- then: {
62
- get: () =>
63
- this.#resolver.promise.then.bind(this.#resolver.promise),
64
- },
65
- catch: {
66
- get: () =>
67
- this.#resolver.promise.catch.bind(this.#resolver.promise),
68
- },
69
- finally: {
70
- get: () =>
71
- this.#resolver.promise.finally.bind(this.#resolver.promise),
72
- },
73
- });
69
+ constructor(
70
+ initial: Accumulated,
71
+ write: (chunk: Input, current: Accumulated) => Accumulated,
72
+ finalize: (current: Accumulated) => Output,
73
+ ) {
74
+ this.#current = initial;
75
+ this.#write = write;
76
+ this.#finalize = finalize;
77
+
78
+ // biome-ignore lint/suspicious/noThenProperty: This object is intentionally thenable
79
+ this.#readable.then = (onfulfilled, onrejected) => {
80
+ return this.#resolver.promise.then(onfulfilled, onrejected);
81
+ };
82
+ // biome-ignore lint/suspicious/noThenProperty: This object is intentionally thenable
83
+ this.#readable.catch = (onrejected) => {
84
+ return this.#resolver.promise.catch(onrejected);
85
+ };
86
+ // biome-ignore lint/suspicious/noThenProperty: This object is intentionally thenable
87
+ this.#readable.finally = (onfinally) => {
88
+ return this.#resolver.promise.finally(onfinally);
89
+ };
74
90
  }
75
91
  }
76
92
 
77
- export interface ConcatBufferReadableStream
78
- extends ReadableStream<Uint8Array>,
79
- Promise<Uint8Array> {}
93
+ /**
94
+ * A `TransformStream` that concatenates strings.
95
+ *
96
+ * Its `readable` is also a Promise-like, that can be `await`ed to get the result.
97
+ *
98
+ * ```ts
99
+ * const result: string = await readable.pipeThrough(new ConcatStringStream());
100
+ * ```
101
+ */
102
+ export class ConcatStringStream extends AccumulateStream<string, string> {
103
+ constructor() {
104
+ // PERF: rope (concat strings) is faster than `[].join('')`
105
+ super(
106
+ "",
107
+ (chunk, current) => current + chunk,
108
+ (output) => output,
109
+ );
110
+ }
111
+ }
80
112
 
81
113
  /**
82
- * A `TransformStream` that concatenates `Uint8Array`s.
114
+ * Concatenate all chunks into a single `Uint8Array`.
83
115
  *
84
- * If you want to decode the result as string,
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.
116
+ * If there is only one chunk, it will be returned directly.
117
+ * @param chunks An array of `Uint8Array`s to concatenate
118
+ * @returns An `Uint8Array` containing all chunks. If there is only one chunk, it will be returned directly.
89
119
  */
90
- export class ConcatBufferStream {
91
- #segments: Uint8Array[] = [];
120
+ export function concatUint8Arrays(chunks: readonly Uint8Array[]): Uint8Array {
121
+ switch (chunks.length) {
122
+ case 0:
123
+ return EmptyUint8Array;
124
+ case 1:
125
+ return chunks[0]!;
126
+ }
92
127
 
93
- #resolver = new PromiseResolver<Uint8Array>();
128
+ const length = chunks.reduce((a, b) => a + b.length, 0);
129
+ const output = new Uint8Array(length);
130
+ let offset = 0;
131
+ for (const chunk of chunks) {
132
+ output.set(chunk, offset);
133
+ offset += chunk.length;
134
+ }
135
+ return output;
136
+ }
94
137
 
95
- #writable = new WritableStream<Uint8Array>({
96
- write: (chunk) => {
97
- this.#segments.push(chunk);
98
- },
99
- close: () => {
100
- let result: Uint8Array;
101
- let offset = 0;
102
- switch (this.#segments.length) {
103
- case 0:
104
- result = EmptyUint8Array;
105
- break;
106
- case 1:
107
- result = this.#segments[0]!;
108
- break;
109
- default:
110
- result = new Uint8Array(
111
- this.#segments.reduce(
112
- (prev, item) => prev + item.length,
113
- 0,
114
- ),
115
- );
116
- for (const segment of this.#segments) {
117
- result.set(segment, offset);
118
- offset += segment.length;
119
- }
120
- break;
121
- }
138
+ export function toUint8Array(
139
+ buffer: ArrayBufferLike | ArrayBufferView<ArrayBufferLike>,
140
+ ): Uint8Array {
141
+ if (buffer instanceof Uint8Array) {
142
+ return buffer;
143
+ }
144
+ if (ArrayBuffer.isView(buffer)) {
145
+ return new Uint8Array(
146
+ buffer.buffer,
147
+ buffer.byteOffset,
148
+ buffer.byteLength,
149
+ );
150
+ }
151
+ return new Uint8Array(buffer);
152
+ }
122
153
 
123
- this.#resolver.resolve(result);
124
- this.#readableController.enqueue(result);
125
- this.#readableController.close();
126
- },
127
- abort: (reason) => {
128
- this.#resolver.reject(reason);
129
- this.#readableController.error(reason);
130
- },
131
- });
132
- get writable(): WritableStream<Uint8Array> {
133
- return this.#writable;
154
+ export function concatBuffers(
155
+ buffers: readonly (ArrayBufferLike | ArrayBufferView<ArrayBufferLike>)[],
156
+ ): Uint8Array {
157
+ switch (buffers.length) {
158
+ case 0:
159
+ return EmptyUint8Array;
160
+ case 1:
161
+ return toUint8Array(buffers[0]!);
134
162
  }
135
163
 
136
- #readableController!: ReadableStreamDefaultController<Uint8Array>;
137
- #readable = new ReadableStream<Uint8Array>({
138
- start: (controller) => {
139
- this.#readableController = controller;
140
- },
141
- }) as ConcatBufferReadableStream;
142
- get readable(): ConcatBufferReadableStream {
143
- return this.#readable;
164
+ const length = buffers.reduce((a, b) => a + b.byteLength, 0);
165
+ const output = new Uint8Array(length);
166
+ let offset = 0;
167
+ for (const buffer of buffers) {
168
+ output.set(toUint8Array(buffer), offset);
169
+ offset += buffer.byteLength;
144
170
  }
171
+ return output;
172
+ }
145
173
 
174
+ /**
175
+ * A `TransformStream` that concatenates `Uint8Array`s.
176
+ *
177
+ * Its `readable` is also a Promise-like, that can be `await`ed to get the result.
178
+ *
179
+ * To convert a `ReadableStream<Uint8Array>` to a string,
180
+ * prefer `.pipeThrough(new TextDecoderStream()).pipeThrough(new ConcatStringStream())`,
181
+ * to `.pipeThrough(new ConcatBufferStream()).pipeThrough(new TextDecoderStream())`.
182
+ * Because of JavaScript engine optimizations,
183
+ * concatenating strings is faster than concatenating `Uint8Array`s.
184
+ */
185
+ export class ConcatBufferStream extends AccumulateStream<
186
+ Uint8Array,
187
+ Uint8Array,
188
+ Uint8Array[]
189
+ > {
146
190
  constructor() {
147
- void Object.defineProperties(this.#readable, {
148
- then: {
149
- get: () =>
150
- this.#resolver.promise.then.bind(this.#resolver.promise),
151
- },
152
- catch: {
153
- get: () =>
154
- this.#resolver.promise.catch.bind(this.#resolver.promise),
191
+ super(
192
+ [],
193
+ (chunk, current) => {
194
+ current.push(chunk);
195
+ return current;
155
196
  },
156
- finally: {
157
- get: () =>
158
- this.#resolver.promise.finally.bind(this.#resolver.promise),
197
+ (current) => {
198
+ const result = concatUint8Arrays(current);
199
+ // `current` is no longer needed. Clear it to free memory.
200
+ current.length = 0;
201
+ return result;
159
202
  },
160
- });
203
+ );
161
204
  }
162
205
  }
@@ -1,6 +1,6 @@
1
1
  import { Consumable } from "../consumable.js";
2
- import type { QueuingStrategy } from "../stream.js";
3
- import { ReadableStream } from "../stream.js";
2
+ import type { QueuingStrategy } from "../global/index.js";
3
+ import { ReadableStream } from "../global/index.js";
4
4
 
5
5
  export interface ConsumableReadableStreamController<T> {
6
6
  enqueue(chunk: T): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import type { Consumable } from "../consumable.js";
2
- import { ReadableStream } from "../stream.js";
2
+ import { ReadableStream } from "../global/index.js";
3
3
 
4
4
  import { ConsumableReadableStream } from "./readable.js";
5
5
 
@@ -9,21 +9,34 @@ export class ConsumableWrapByteReadableStream extends ReadableStream<
9
9
  constructor(
10
10
  stream: ReadableStream<Uint8Array>,
11
11
  chunkSize: number,
12
- min?: number,
12
+ options?: {
13
+ min?: number | undefined;
14
+ onRead?: ((size: number) => void) | undefined;
15
+ },
13
16
  ) {
14
17
  const reader = stream.getReader({ mode: "byob" });
15
- let array = new Uint8Array(chunkSize);
18
+ let buffer = new ArrayBuffer(chunkSize);
16
19
  super({
17
20
  async pull(controller) {
18
- const { done, value } = await reader.read(array, { min });
21
+ const { done, value } = await reader.read(
22
+ new Uint8Array(buffer),
23
+ {
24
+ min: options?.min,
25
+ },
26
+ );
27
+
28
+ // `value` might be defined even when `done` is true
29
+ if (value) {
30
+ await ConsumableReadableStream.enqueue(controller, value);
31
+ options?.onRead?.(value.byteLength);
32
+ }
33
+
19
34
  if (done) {
20
35
  controller.close();
21
36
  return;
22
37
  }
23
38
 
24
- await ConsumableReadableStream.enqueue(controller, value);
25
-
26
- array = new Uint8Array(value.buffer);
39
+ buffer = value.buffer;
27
40
  },
28
41
  cancel(reason) {
29
42
  return reader.cancel(reason);
@@ -1,5 +1,5 @@
1
1
  import type { Consumable } from "../consumable.js";
2
- import { WritableStream } from "../stream.js";
2
+ import { WritableStream } from "../global/index.js";
3
3
 
4
4
  export class ConsumableWrapWritableStream<in T> extends WritableStream<
5
5
  Consumable<T>
@@ -3,8 +3,8 @@ import type {
3
3
  QueuingStrategy,
4
4
  WritableStreamDefaultController,
5
5
  WritableStreamDefaultWriter,
6
- } from "../stream.js";
7
- import { WritableStream } from "../stream.js";
6
+ } from "../global/index.js";
7
+ import { WritableStream } from "../global/index.js";
8
8
 
9
9
  export interface ConsumableWritableStreamSink<in T> {
10
10
  start?(
package/src/consumable.ts CHANGED
@@ -14,12 +14,20 @@ import {
14
14
  import type { Task } from "./task.js";
15
15
  import { createTask } from "./task.js";
16
16
 
17
+ const Brand = Symbol.for("Consumable.brand");
18
+
17
19
  export class Consumable<T> {
18
20
  static readonly WritableStream = ConsumableWritableStream;
19
21
  static readonly WrapWritableStream = ConsumableWrapWritableStream;
20
22
  static readonly ReadableStream = ConsumableReadableStream;
21
23
  static readonly WrapByteReadableStream = ConsumableWrapByteReadableStream;
22
24
 
25
+ readonly [Brand] = true;
26
+
27
+ static [Symbol.hasInstance](value: unknown) {
28
+ return !!(value as Consumable<unknown> | undefined)?.[Brand];
29
+ }
30
+
23
31
  readonly #task: Task;
24
32
  readonly #resolver: PromiseResolver<void>;
25
33
 
@@ -1,6 +1,6 @@
1
1
  import { Consumable } from "./consumable.js";
2
+ import { TransformStream } from "./global/index.js";
2
3
  import { MaybeConsumable } from "./maybe-consumable.js";
3
- import { TransformStream } from "./stream.js";
4
4
 
5
5
  /**
6
6
  * Splits or combines buffers to specified size.
package/src/duplex.ts CHANGED
@@ -6,8 +6,8 @@ import type {
6
6
  ReadableStream,
7
7
  ReadableStreamDefaultController,
8
8
  WritableStreamDefaultWriter,
9
- } from "./stream.js";
10
- import { WritableStream } from "./stream.js";
9
+ } from "./global/index.js";
10
+ import { WritableStream } from "./global/index.js";
11
11
  import { tryClose } from "./try-close.js";
12
12
  import { WrapReadableStream } from "./wrap-readable.js";
13
13
 
@@ -0,0 +1,129 @@
1
+ import type {
2
+ AddEventListenerOptions,
3
+ Event,
4
+ EventListenerOptions,
5
+ EventListenerOrEventListenerObject,
6
+ } from "./event.js";
7
+ import type { GlobalPrototypeOr, GlobalValueOr } from "./utils.js";
8
+ import { getGlobalValue } from "./utils.js";
9
+
10
+ interface AbortSignalEventMap {
11
+ abort: Event;
12
+ }
13
+
14
+ /**
15
+ * The **`AbortSignal`** interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object.
16
+ *
17
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal)
18
+ */
19
+ interface _AbortSignal {
20
+ /**
21
+ * The **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (true) or not (false).
22
+ *
23
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted)
24
+ */
25
+ readonly aborted: boolean;
26
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */
27
+ onabort: ((this: AbortSignal, ev: Event) => void) | null;
28
+ /**
29
+ * The **`reason`** read-only property returns a JavaScript value that indicates the abort reason.
30
+ *
31
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason)
32
+ */
33
+ readonly reason: unknown;
34
+ /**
35
+ * The **`throwIfAborted()`** method throws the signal's abort reason if the signal has been aborted; otherwise it does nothing.
36
+ *
37
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted)
38
+ */
39
+ throwIfAborted(): void;
40
+ addEventListener<K extends keyof AbortSignalEventMap>(
41
+ type: K,
42
+ listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => void,
43
+ options?: boolean | AddEventListenerOptions,
44
+ ): void;
45
+ addEventListener(
46
+ type: string,
47
+ listener: EventListenerOrEventListenerObject,
48
+ options?: boolean | AddEventListenerOptions,
49
+ ): void;
50
+ removeEventListener<K extends keyof AbortSignalEventMap>(
51
+ type: K,
52
+ listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => void,
53
+ options?: boolean | EventListenerOptions,
54
+ ): void;
55
+ removeEventListener(
56
+ type: string,
57
+ listener: EventListenerOrEventListenerObject,
58
+ options?: boolean | EventListenerOptions,
59
+ ): void;
60
+ }
61
+
62
+ export type AbortSignal = GlobalPrototypeOr<"AbortSignal", _AbortSignal>;
63
+
64
+ interface _AbortSignalConstructor {
65
+ /**
66
+ * The **`AbortSignal.abort()`** static method returns an AbortSignal that is already set as aborted (and which does not trigger an abort event).
67
+ *
68
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static)
69
+ */
70
+ abort(reason?: unknown): AbortSignal;
71
+ /**
72
+ * The **`AbortSignal.any()`** static method takes an iterable of abort signals and returns an AbortSignal. The returned abort signal is aborted when any of the input iterable abort signals are aborted. The abort reason will be set to the reason of the first signal that is aborted. If any of the given abort signals are already aborted then so will be the returned AbortSignal.
73
+ *
74
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static)
75
+ */
76
+ any(signals: Iterable<AbortSignal>): AbortSignal;
77
+ /**
78
+ * The **`AbortSignal.timeout()`** static method returns an AbortSignal that will automatically abort after a specified time.
79
+ *
80
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static)
81
+ */
82
+ timeout(milliseconds: number): AbortSignal;
83
+ }
84
+
85
+ type AbortSignalConstructor = GlobalValueOr<
86
+ "AbortSignal",
87
+ _AbortSignalConstructor
88
+ >;
89
+
90
+ export const AbortSignal =
91
+ getGlobalValue<AbortSignalConstructor>("AbortSignal");
92
+
93
+ /**
94
+ * The **`AbortController`** interface represents a controller object that allows you to abort one or more Web requests as and when desired.
95
+ *
96
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController)
97
+ */
98
+ interface _AbortController {
99
+ /**
100
+ * The **`signal`** read-only property of the AbortController interface returns an AbortSignal object instance, which can be used to communicate with/abort an asynchronous operation as desired.
101
+ *
102
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal)
103
+ */
104
+ readonly signal: AbortSignal;
105
+ /**
106
+ * The **`abort()`** method of the AbortController interface aborts an asynchronous operation before it has completed. This is able to abort fetch requests, the consumption of any response bodies, or streams.
107
+ *
108
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort)
109
+ */
110
+ abort(reason?: unknown): void;
111
+ }
112
+
113
+ export type AbortController = GlobalPrototypeOr<
114
+ "AbortController",
115
+ _AbortController
116
+ >;
117
+
118
+ interface _AbortControllerConstructor {
119
+ prototype: AbortController;
120
+ new (): AbortController;
121
+ }
122
+
123
+ type AbortControllerConstructor = GlobalValueOr<
124
+ "AbortController",
125
+ _AbortControllerConstructor
126
+ >;
127
+
128
+ export const AbortController =
129
+ getGlobalValue<AbortControllerConstructor>("AbortController");