@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.
Files changed (100) hide show
  1. package/CHANGELOG.md +32 -2
  2. package/LICENSE +1 -1
  3. package/esm/buffered-transform.d.ts +3 -3
  4. package/esm/buffered-transform.d.ts.map +1 -1
  5. package/esm/buffered-transform.js.map +1 -1
  6. package/esm/buffered.d.ts +3 -6
  7. package/esm/buffered.d.ts.map +1 -1
  8. package/esm/buffered.js +87 -75
  9. package/esm/buffered.js.map +1 -1
  10. package/esm/concat.d.ts +4 -3
  11. package/esm/concat.d.ts.map +1 -1
  12. package/esm/concat.js +6 -4
  13. package/esm/concat.js.map +1 -1
  14. package/esm/consumable.d.ts +36 -43
  15. package/esm/consumable.d.ts.map +1 -1
  16. package/esm/consumable.js +74 -125
  17. package/esm/consumable.js.map +1 -1
  18. package/esm/distribution.d.ts +3 -2
  19. package/esm/distribution.d.ts.map +1 -1
  20. package/esm/distribution.js +22 -18
  21. package/esm/distribution.js.map +1 -1
  22. package/esm/duplex.d.ts +2 -2
  23. package/esm/duplex.d.ts.map +1 -1
  24. package/esm/duplex.js +2 -6
  25. package/esm/duplex.js.map +1 -1
  26. package/esm/encoding.d.ts +21 -0
  27. package/esm/encoding.d.ts.map +1 -0
  28. package/esm/encoding.js +4 -0
  29. package/esm/encoding.js.map +1 -0
  30. package/esm/index.d.ts +4 -1
  31. package/esm/index.d.ts.map +1 -1
  32. package/esm/index.js +4 -1
  33. package/esm/index.js.map +1 -1
  34. package/esm/maybe-consumable-ns.d.ts +16 -0
  35. package/esm/maybe-consumable-ns.d.ts.map +1 -0
  36. package/esm/maybe-consumable-ns.js +44 -0
  37. package/esm/maybe-consumable-ns.js.map +1 -0
  38. package/esm/maybe-consumable.d.ts +4 -0
  39. package/esm/maybe-consumable.d.ts.map +1 -0
  40. package/esm/maybe-consumable.js +2 -0
  41. package/esm/maybe-consumable.js.map +1 -0
  42. package/esm/push-readable.d.ts +16 -2
  43. package/esm/push-readable.d.ts.map +1 -1
  44. package/esm/push-readable.js +176 -17
  45. package/esm/push-readable.js.map +1 -1
  46. package/esm/stream.d.ts +7 -7
  47. package/esm/stream.d.ts.map +1 -1
  48. package/esm/stream.js +54 -5
  49. package/esm/stream.js.map +1 -1
  50. package/esm/struct-deserialize.d.ts +3 -4
  51. package/esm/struct-deserialize.d.ts.map +1 -1
  52. package/esm/struct-deserialize.js.map +1 -1
  53. package/esm/struct-serialize.d.ts +2 -2
  54. package/esm/struct-serialize.d.ts.map +1 -1
  55. package/esm/struct-serialize.js.map +1 -1
  56. package/esm/task.d.ts +5 -0
  57. package/esm/task.d.ts.map +1 -0
  58. package/esm/task.js +9 -0
  59. package/esm/task.js.map +1 -0
  60. package/esm/try-close.d.ts +10 -0
  61. package/esm/try-close.d.ts.map +1 -0
  62. package/esm/try-close.js +19 -0
  63. package/esm/try-close.js.map +1 -0
  64. package/esm/types.d.ts +34 -35
  65. package/esm/types.d.ts.map +1 -1
  66. package/esm/wrap-readable.d.ts +5 -4
  67. package/esm/wrap-readable.d.ts.map +1 -1
  68. package/esm/wrap-readable.js +14 -9
  69. package/esm/wrap-readable.js.map +1 -1
  70. package/esm/wrap-writable.d.ts +2 -2
  71. package/esm/wrap-writable.d.ts.map +1 -1
  72. package/esm/wrap-writable.js +4 -6
  73. package/esm/wrap-writable.js.map +1 -1
  74. package/package.json +11 -15
  75. package/src/buffered-transform.ts +2 -2
  76. package/src/buffered.ts +107 -83
  77. package/src/concat.ts +6 -4
  78. package/src/consumable.ts +132 -213
  79. package/src/distribution.ts +30 -20
  80. package/src/duplex.ts +4 -7
  81. package/src/encoding.ts +39 -0
  82. package/src/index.ts +4 -1
  83. package/src/maybe-consumable-ns.ts +75 -0
  84. package/src/maybe-consumable.ts +5 -0
  85. package/src/push-readable.ts +213 -19
  86. package/src/stream.ts +69 -9
  87. package/src/struct-deserialize.ts +4 -7
  88. package/src/struct-serialize.ts +3 -3
  89. package/src/task.ts +21 -0
  90. package/src/try-close.ts +44 -0
  91. package/src/types.ts +34 -34
  92. package/src/wrap-readable.ts +20 -14
  93. package/src/wrap-writable.ts +6 -9
  94. package/tsconfig.build.tsbuildinfo +1 -1
  95. package/CHANGELOG.json +0 -86
  96. package/esm/decode-utf8.d.ts +0 -5
  97. package/esm/decode-utf8.d.ts.map +0 -1
  98. package/esm/decode-utf8.js +0 -12
  99. package/esm/decode-utf8.js.map +0 -1
  100. package/src/decode-utf8.ts +0 -13
@@ -1,4 +1,6 @@
1
- import { ConsumableTransformStream } from "./consumable.js";
1
+ import { Consumable } from "./consumable.js";
2
+ import { MaybeConsumable } from "./maybe-consumable.js";
3
+ import { TransformStream } from "./stream.js";
2
4
 
3
5
  /**
4
6
  * Splits or combines buffers to specified size.
@@ -25,7 +27,7 @@ export class BufferCombiner {
25
27
  */
26
28
  *push(data: Uint8Array): Generator<Uint8Array, void, void> {
27
29
  let offset = 0;
28
- let available = data.byteLength;
30
+ let available = data.length;
29
31
 
30
32
  if (this.#offset !== 0) {
31
33
  if (available >= this.#available) {
@@ -77,34 +79,42 @@ export class BufferCombiner {
77
79
  }
78
80
  }
79
81
 
80
- export class DistributionStream extends ConsumableTransformStream<
81
- Uint8Array,
82
- Uint8Array
82
+ export class DistributionStream extends TransformStream<
83
+ MaybeConsumable<Uint8Array>,
84
+ MaybeConsumable<Uint8Array>
83
85
  > {
84
86
  constructor(size: number, combine = false) {
85
87
  const combiner = combine ? new BufferCombiner(size) : undefined;
86
88
  super({
87
89
  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;
90
+ await MaybeConsumable.tryConsume(chunk, async (chunk) => {
91
+ if (combiner) {
92
+ for (const buffer of combiner.push(chunk)) {
93
+ await Consumable.ReadableStream.enqueue(
94
+ controller,
95
+ buffer,
96
+ );
97
+ }
98
+ } else {
99
+ let offset = 0;
100
+ let available = chunk.length;
101
+ while (available > 0) {
102
+ const end = offset + size;
103
+ await Consumable.ReadableStream.enqueue(
104
+ controller,
105
+ chunk.subarray(offset, end),
106
+ );
107
+ offset = end;
108
+ available -= size;
109
+ }
100
110
  }
101
- }
111
+ });
102
112
  },
103
- async flush(controller) {
113
+ flush(controller) {
104
114
  if (combiner) {
105
115
  const data = combiner.flush();
106
116
  if (data) {
107
- await controller.enqueue(data);
117
+ controller.enqueue(data);
108
118
  }
109
119
  }
110
120
  },
package/src/duplex.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import type { MaybePromiseLike } from "@yume-chan/async";
1
2
  import { PromiseResolver } from "@yume-chan/async";
2
- import type { ValueOrPromise } from "@yume-chan/struct";
3
3
 
4
4
  import type {
5
5
  QueuingStrategy,
@@ -8,6 +8,7 @@ import type {
8
8
  WritableStreamDefaultWriter,
9
9
  } from "./stream.js";
10
10
  import { WritableStream } from "./stream.js";
11
+ import { tryClose } from "./try-close.js";
11
12
  import { WrapReadableStream } from "./wrap-readable.js";
12
13
 
13
14
  const NOOP = () => {
@@ -27,7 +28,7 @@ export interface DuplexStreamFactoryOptions {
27
28
  * `DuplexStreamFactory#dispose` yourself, you can return `false`
28
29
  * (or a `Promise` that resolves to `false`) to disable the automatic call.
29
30
  */
30
- close?: (() => ValueOrPromise<boolean | void>) | undefined;
31
+ close?: (() => MaybePromiseLike<boolean | void>) | undefined;
31
32
 
32
33
  /**
33
34
  * Callback when any `ReadableStream` is closed (the other peer doesn't produce any more data),
@@ -134,11 +135,7 @@ export class DuplexStreamFactory<R, W> {
134
135
  this.#closed.resolve();
135
136
 
136
137
  for (const controller of this.#readableControllers) {
137
- try {
138
- controller.close();
139
- } catch {
140
- // ignore
141
- }
138
+ tryClose(controller);
142
139
  }
143
140
 
144
141
  await this.#options.dispose?.();
@@ -0,0 +1,39 @@
1
+ import type { TransformStream } from "./stream.js";
2
+
3
+ export interface TextDecoderOptions {
4
+ fatal?: boolean;
5
+ ignoreBOM?: boolean;
6
+ }
7
+
8
+ declare class TextDecoderStreamType extends TransformStream<
9
+ ArrayBufferView | ArrayBuffer,
10
+ string
11
+ > {
12
+ constructor(label?: string, options?: TextDecoderOptions);
13
+
14
+ readonly encoding: string;
15
+ readonly fatal: boolean;
16
+ readonly ignoreBOM: boolean;
17
+ }
18
+
19
+ declare class TextEncoderStreamType extends TransformStream<
20
+ string,
21
+ Uint8Array
22
+ > {
23
+ constructor();
24
+
25
+ readonly encoding: string;
26
+ }
27
+
28
+ interface GlobalExtension {
29
+ TextDecoderStream: typeof TextDecoderStreamType;
30
+ TextEncoderStream: typeof TextEncoderStreamType;
31
+ }
32
+
33
+ const Global = globalThis as unknown as GlobalExtension;
34
+
35
+ export const TextDecoderStream = Global.TextDecoderStream;
36
+ export type TextDecoderStream = TextDecoderStreamType;
37
+
38
+ export const TextEncoderStream = Global.TextEncoderStream;
39
+ export type TextEncoderStream = TextEncoderStreamType;
package/src/index.ts CHANGED
@@ -2,15 +2,18 @@ export * from "./buffered-transform.js";
2
2
  export * from "./buffered.js";
3
3
  export * from "./concat.js";
4
4
  export * from "./consumable.js";
5
- export * from "./decode-utf8.js";
6
5
  export * from "./distribution.js";
7
6
  export * from "./duplex.js";
7
+ export * from "./encoding.js";
8
8
  export * from "./inspect.js";
9
+ export * from "./maybe-consumable.js";
9
10
  export * from "./pipe-from.js";
10
11
  export * from "./push-readable.js";
11
12
  export * from "./split-string.js";
12
13
  export * from "./stream.js";
13
14
  export * from "./struct-deserialize.js";
14
15
  export * from "./struct-serialize.js";
16
+ export * from "./task.js";
17
+ export * from "./try-close.js";
15
18
  export * from "./wrap-readable.js";
16
19
  export * from "./wrap-writable.js";
@@ -0,0 +1,75 @@
1
+ import { Consumable } from "./consumable.js";
2
+ import type { MaybeConsumable } from "./maybe-consumable.js";
3
+ import type {
4
+ QueuingStrategy,
5
+ WritableStreamDefaultController,
6
+ } from "./stream.js";
7
+ import { WritableStream as NativeWritableStream } from "./stream.js";
8
+
9
+ export function getValue<T>(value: MaybeConsumable<T>): T {
10
+ return value instanceof Consumable ? value.value : value;
11
+ }
12
+
13
+ export function tryConsume<T, R>(
14
+ value: T,
15
+ callback: (value: T extends Consumable<infer U> ? U : T) => R,
16
+ ): R {
17
+ if (value instanceof Consumable) {
18
+ return value.tryConsume(callback);
19
+ } else {
20
+ return callback(value as never);
21
+ }
22
+ }
23
+
24
+ export interface WritableStreamSink<in T> {
25
+ start?(
26
+ controller: WritableStreamDefaultController,
27
+ ): void | PromiseLike<void>;
28
+ write?(
29
+ chunk: T,
30
+ controller: WritableStreamDefaultController,
31
+ ): void | PromiseLike<void>;
32
+ abort?(reason: unknown): void | PromiseLike<void>;
33
+ close?(): void | PromiseLike<void>;
34
+ }
35
+
36
+ export class WritableStream<in T> extends NativeWritableStream<
37
+ MaybeConsumable<T>
38
+ > {
39
+ constructor(sink: WritableStreamSink<T>, strategy?: QueuingStrategy<T>) {
40
+ let wrappedStrategy: QueuingStrategy<MaybeConsumable<T>> | undefined;
41
+ if (strategy) {
42
+ wrappedStrategy = {};
43
+ if ("highWaterMark" in strategy) {
44
+ wrappedStrategy.highWaterMark = strategy.highWaterMark;
45
+ }
46
+ if ("size" in strategy) {
47
+ wrappedStrategy.size = (chunk) => {
48
+ return strategy.size!(
49
+ chunk instanceof Consumable ? chunk.value : chunk,
50
+ );
51
+ };
52
+ }
53
+ }
54
+
55
+ super(
56
+ {
57
+ start(controller) {
58
+ return sink.start?.(controller);
59
+ },
60
+ async write(chunk, controller) {
61
+ await tryConsume(chunk, (chunk) =>
62
+ sink.write?.(chunk as T, controller),
63
+ );
64
+ },
65
+ abort(reason) {
66
+ return sink.abort?.(reason);
67
+ },
68
+ close() {
69
+ return sink.close?.();
70
+ },
71
+ },
72
+ wrappedStrategy,
73
+ );
74
+ }
75
+ }
@@ -0,0 +1,5 @@
1
+ import type { Consumable } from "./consumable.js";
2
+
3
+ export type MaybeConsumable<T> = T | Consumable<T>;
4
+
5
+ export * as MaybeConsumable from "./maybe-consumable-ns.js";
@@ -17,9 +17,28 @@ export type PushReadableStreamSource<T> = (
17
17
  controller: PushReadableStreamController<T>,
18
18
  ) => void | Promise<void>;
19
19
 
20
- export class PushReadableStream<T> extends ReadableStream<T> {
21
- #zeroHighWaterMarkAllowEnqueue = false;
20
+ export type PushReadableLogger<T> = (
21
+ event:
22
+ | {
23
+ source: "producer";
24
+ operation: "enqueue";
25
+ value: T;
26
+ phase: "start" | "waiting" | "ignored" | "complete";
27
+ }
28
+ | {
29
+ source: "producer";
30
+ operation: "close" | "error";
31
+ explicit: boolean;
32
+ phase: "start" | "ignored" | "complete";
33
+ }
34
+ | {
35
+ source: "consumer";
36
+ operation: "pull" | "cancel";
37
+ phase: "start" | "complete";
38
+ },
39
+ ) => void;
22
40
 
41
+ export class PushReadableStream<T> extends ReadableStream<T> {
23
42
  /**
24
43
  * Create a new `PushReadableStream` from a source.
25
44
  *
@@ -30,81 +49,256 @@ export class PushReadableStream<T> extends ReadableStream<T> {
30
49
  constructor(
31
50
  source: PushReadableStreamSource<T>,
32
51
  strategy?: QueuingStrategy<T>,
52
+ logger?: PushReadableLogger<T>,
33
53
  ) {
34
54
  let waterMarkLow: PromiseResolver<void> | undefined;
55
+ let zeroHighWaterMarkAllowEnqueue = false;
35
56
  const abortController = new AbortController();
36
57
 
37
58
  super(
38
59
  {
39
- start: async (controller) => {
40
- await Promise.resolve();
41
-
60
+ start: (controller) => {
42
61
  const result = source({
43
62
  abortSignal: abortController.signal,
44
63
  enqueue: async (chunk) => {
64
+ logger?.({
65
+ source: "producer",
66
+ operation: "enqueue",
67
+ value: chunk,
68
+ phase: "start",
69
+ });
70
+
45
71
  if (abortController.signal.aborted) {
46
- // If the stream is already cancelled,
47
- // throw immediately.
48
- throw abortController.signal.reason;
72
+ // In original `ReadableStream`, calling `enqueue` or `close`
73
+ // on an cancelled stream will throw an error,
74
+ //
75
+ // But in `PushReadableStream`, `enqueue` is an async function,
76
+ // the producer can't just check `abortSignal.aborted`
77
+ // before calling `enqueue`, as it might change when waiting
78
+ // for the backpressure to be reduced.
79
+ //
80
+ // So IMO it's better to handle this for the producer
81
+ // by simply ignoring the `enqueue` call.
82
+ //
83
+ // Note that we check `abortSignal.aborted` instead of `stopped`,
84
+ // as it's not allowed for the producer to call `enqueue` after
85
+ // they called `close` or `error`.
86
+ //
87
+ // Obviously, the producer should listen to the `abortSignal` and
88
+ // stop producing, but most pushing data sources don't support that.
89
+ logger?.({
90
+ source: "producer",
91
+ operation: "enqueue",
92
+ value: chunk,
93
+ phase: "ignored",
94
+ });
95
+ return;
49
96
  }
50
97
 
51
98
  if (controller.desiredSize === null) {
52
99
  // `desiredSize` being `null` means the stream is in error state,
53
100
  // `controller.enqueue` will throw an error for us.
54
101
  controller.enqueue(chunk);
102
+ // istanbul ignore next
55
103
  return;
56
104
  }
57
105
 
58
- if (this.#zeroHighWaterMarkAllowEnqueue) {
59
- this.#zeroHighWaterMarkAllowEnqueue = false;
106
+ if (zeroHighWaterMarkAllowEnqueue) {
107
+ // When `highWaterMark` is set to `0`,
108
+ // `controller.desiredSize` will always be `0`,
109
+ // even if the consumer has called `reader.read()`.
110
+ // (in this case, each `reader.read()`/`pull`
111
+ // should allow one `enqueue` of any size)
112
+ //
113
+ // If the consumer has already called `reader.read()`,
114
+ // before the producer tries to `enqueue`,
115
+ // `controller.desiredSize` is `0` and normal `waterMarkLow` signal
116
+ // will never trigger,
117
+ // (because `ReadableStream` prevents reentrance of `pull`)
118
+ // The stream will stuck.
119
+ //
120
+ // So we need a special signal for this case.
121
+ zeroHighWaterMarkAllowEnqueue = false;
60
122
  controller.enqueue(chunk);
123
+ logger?.({
124
+ source: "producer",
125
+ operation: "enqueue",
126
+ value: chunk,
127
+ phase: "complete",
128
+ });
61
129
  return;
62
130
  }
63
131
 
64
132
  if (controller.desiredSize <= 0) {
133
+ logger?.({
134
+ source: "producer",
135
+ operation: "enqueue",
136
+ value: chunk,
137
+ phase: "waiting",
138
+ });
139
+
65
140
  waterMarkLow = new PromiseResolver<void>();
66
141
  await waterMarkLow.promise;
142
+
143
+ // Recheck consumer cancellation after async operations.
144
+ if (abortController.signal.aborted) {
145
+ logger?.({
146
+ source: "producer",
147
+ operation: "enqueue",
148
+ value: chunk,
149
+ phase: "ignored",
150
+ });
151
+ return;
152
+ }
67
153
  }
68
154
 
69
- // `controller.enqueue` will throw error for us
70
- // if the stream is already errored.
71
155
  controller.enqueue(chunk);
156
+ logger?.({
157
+ source: "producer",
158
+ operation: "enqueue",
159
+ value: chunk,
160
+ phase: "complete",
161
+ });
72
162
  },
73
163
  close() {
164
+ logger?.({
165
+ source: "producer",
166
+ operation: "close",
167
+ explicit: true,
168
+ phase: "start",
169
+ });
170
+
171
+ // Since `enqueue` on an cancelled stream won't throw an error,
172
+ // so does `close`.
173
+ if (abortController.signal.aborted) {
174
+ logger?.({
175
+ source: "producer",
176
+ operation: "close",
177
+ explicit: true,
178
+ phase: "ignored",
179
+ });
180
+ return;
181
+ }
182
+
74
183
  controller.close();
184
+ logger?.({
185
+ source: "producer",
186
+ operation: "close",
187
+ explicit: true,
188
+ phase: "complete",
189
+ });
75
190
  },
76
191
  error(e) {
192
+ logger?.({
193
+ source: "producer",
194
+ operation: "error",
195
+ explicit: true,
196
+ phase: "start",
197
+ });
198
+
199
+ // Calling `error` on an already closed or errored stream is a no-op.
77
200
  controller.error(e);
201
+
202
+ logger?.({
203
+ source: "producer",
204
+ operation: "error",
205
+ explicit: true,
206
+ phase: "complete",
207
+ });
78
208
  },
79
209
  });
80
210
 
81
211
  if (result && "then" in result) {
212
+ // If `source` returns a `Promise`,
213
+ // close the stream when the `Promise` is resolved,
214
+ // and error the stream when the `Promise` is rejected.
215
+ // The producer can return a never-settling `Promise`
216
+ // to disable this behavior.
82
217
  result.then(
83
218
  () => {
219
+ logger?.({
220
+ source: "producer",
221
+ operation: "close",
222
+ explicit: false,
223
+ phase: "start",
224
+ });
225
+
84
226
  try {
85
227
  controller.close();
86
- } catch (e) {
87
- // controller already closed
228
+
229
+ logger?.({
230
+ source: "producer",
231
+ operation: "close",
232
+ explicit: false,
233
+ phase: "complete",
234
+ });
235
+ } catch {
236
+ logger?.({
237
+ source: "producer",
238
+ operation: "close",
239
+ explicit: false,
240
+ phase: "ignored",
241
+ });
242
+
243
+ // The stream is already closed by the producer,
244
+ // Or cancelled by the consumer.
88
245
  }
89
246
  },
90
247
  (e) => {
248
+ logger?.({
249
+ source: "producer",
250
+ operation: "error",
251
+ explicit: false,
252
+ phase: "start",
253
+ });
254
+
91
255
  controller.error(e);
256
+
257
+ logger?.({
258
+ source: "producer",
259
+ operation: "error",
260
+ explicit: false,
261
+ phase: "complete",
262
+ });
92
263
  },
93
264
  );
94
265
  }
95
266
  },
96
267
  pull: () => {
268
+ logger?.({
269
+ source: "consumer",
270
+ operation: "pull",
271
+ phase: "start",
272
+ });
273
+
97
274
  if (waterMarkLow) {
98
275
  waterMarkLow.resolve();
99
- return;
100
- }
101
- if (strategy?.highWaterMark === 0) {
102
- this.#zeroHighWaterMarkAllowEnqueue = true;
276
+ } else if (strategy?.highWaterMark === 0) {
277
+ zeroHighWaterMarkAllowEnqueue = true;
103
278
  }
279
+
280
+ logger?.({
281
+ source: "consumer",
282
+ operation: "pull",
283
+ phase: "complete",
284
+ });
104
285
  },
105
286
  cancel: (reason) => {
287
+ logger?.({
288
+ source: "consumer",
289
+ operation: "cancel",
290
+ phase: "start",
291
+ });
292
+
106
293
  abortController.abort(reason);
107
- waterMarkLow?.reject(reason);
294
+ // Resolve it on cancellation. `pull` will check `abortSignal.aborted` again.
295
+ waterMarkLow?.resolve();
296
+
297
+ logger?.({
298
+ source: "consumer",
299
+ operation: "cancel",
300
+ phase: "complete",
301
+ });
108
302
  },
109
303
  },
110
304
  strategy,
package/src/stream.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  import type {
2
2
  AbortSignal,
3
+ ReadableStreamIteratorOptions,
3
4
  ReadableStream as ReadableStreamType,
4
5
  TransformStream as TransformStreamType,
5
6
  WritableStream as WritableStreamType,
6
7
  } from "./types.js";
7
8
 
8
9
  export * from "./types.js";
10
+ export { ReadableStream };
9
11
 
10
12
  /** A controller object that allows you to abort one or more DOM requests as and when desired. */
11
13
  export interface AbortController {
@@ -17,7 +19,7 @@ export interface AbortController {
17
19
  /**
18
20
  * 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.
19
21
  */
20
- abort(reason?: any): void;
22
+ abort(reason?: unknown): void;
21
23
  }
22
24
 
23
25
  interface AbortControllerConstructor {
@@ -32,15 +34,73 @@ interface GlobalExtension {
32
34
  TransformStream: typeof TransformStreamType;
33
35
  }
34
36
 
35
- const Global = globalThis as unknown as GlobalExtension;
37
+ export const { AbortController } = globalThis as unknown as GlobalExtension;
36
38
 
37
- export const AbortController = Global.AbortController;
39
+ export type ReadableStream<T> = ReadableStreamType<T>;
40
+ export type WritableStream<T> = WritableStreamType<T>;
41
+ export type TransformStream<I, O> = TransformStreamType<I, O>;
38
42
 
39
- export type ReadableStream<T = any> = ReadableStreamType<T>;
40
- export const ReadableStream = Global.ReadableStream;
43
+ const ReadableStream = /* #__PURE__ */ (() => {
44
+ const { ReadableStream } = globalThis as unknown as GlobalExtension;
41
45
 
42
- export type WritableStream<T = any> = WritableStreamType<T>;
43
- export const WritableStream = Global.WritableStream;
46
+ if (!ReadableStream.from) {
47
+ ReadableStream.from = function (iterable) {
48
+ const iterator =
49
+ Symbol.asyncIterator in iterable
50
+ ? iterable[Symbol.asyncIterator]()
51
+ : iterable[Symbol.iterator]();
44
52
 
45
- export type TransformStream<I = any, O = any> = TransformStreamType<I, O>;
46
- export const TransformStream = Global.TransformStream;
53
+ return new ReadableStream({
54
+ async pull(controller) {
55
+ const result = await iterator.next();
56
+ if (result.done) {
57
+ controller.close();
58
+ return;
59
+ }
60
+ controller.enqueue(result.value);
61
+ },
62
+ async cancel(reason) {
63
+ await iterator.return?.(reason);
64
+ },
65
+ });
66
+ };
67
+ }
68
+
69
+ if (
70
+ !ReadableStream.prototype[Symbol.asyncIterator] ||
71
+ !ReadableStream.prototype.values
72
+ ) {
73
+ ReadableStream.prototype.values = async function* <R>(
74
+ this: ReadableStream<R>,
75
+ options?: ReadableStreamIteratorOptions,
76
+ ) {
77
+ const reader = this.getReader();
78
+ try {
79
+ while (true) {
80
+ const { done, value } = await reader.read();
81
+ if (done) {
82
+ return;
83
+ }
84
+ yield value;
85
+ }
86
+ } finally {
87
+ // Calling `iterator.return` will enter this `finally` block.
88
+ // We don't need to care about the parameter to `iterator.return`,
89
+ // it will be returned as the final `result.value` automatically.
90
+ if (!options?.preventCancel) {
91
+ await reader.cancel();
92
+ }
93
+ reader.releaseLock();
94
+ }
95
+ };
96
+
97
+ ReadableStream.prototype[Symbol.asyncIterator] =
98
+ // eslint-disable-next-line @typescript-eslint/unbound-method
99
+ ReadableStream.prototype.values;
100
+ }
101
+
102
+ return ReadableStream;
103
+ })();
104
+
105
+ export const { WritableStream, TransformStream } =
106
+ globalThis as unknown as GlobalExtension;