@yume-chan/stream-extra 0.0.17

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 (92) hide show
  1. package/CHANGELOG.json +11 -0
  2. package/CHANGELOG.md +9 -0
  3. package/LICENSE +21 -0
  4. package/README.md +11 -0
  5. package/esm/buffered-transform.d.ts +11 -0
  6. package/esm/buffered-transform.d.ts.map +1 -0
  7. package/esm/buffered-transform.js +50 -0
  8. package/esm/buffered-transform.js.map +1 -0
  9. package/esm/buffered.d.ts +28 -0
  10. package/esm/buffered.d.ts.map +1 -0
  11. package/esm/buffered.js +131 -0
  12. package/esm/buffered.js.map +1 -0
  13. package/esm/chunk.d.ts +5 -0
  14. package/esm/chunk.d.ts.map +1 -0
  15. package/esm/chunk.js +15 -0
  16. package/esm/chunk.js.map +1 -0
  17. package/esm/decode-utf8.d.ts +5 -0
  18. package/esm/decode-utf8.d.ts.map +1 -0
  19. package/esm/decode-utf8.js +12 -0
  20. package/esm/decode-utf8.js.map +1 -0
  21. package/esm/duplex.d.ts +48 -0
  22. package/esm/duplex.d.ts.map +1 -0
  23. package/esm/duplex.js +89 -0
  24. package/esm/duplex.js.map +1 -0
  25. package/esm/gather-string.d.ts +7 -0
  26. package/esm/gather-string.d.ts.map +1 -0
  27. package/esm/gather-string.js +14 -0
  28. package/esm/gather-string.js.map +1 -0
  29. package/esm/index.d.ts +16 -0
  30. package/esm/index.d.ts.map +1 -0
  31. package/esm/index.js +16 -0
  32. package/esm/index.js.map +1 -0
  33. package/esm/inspect.d.ts +5 -0
  34. package/esm/inspect.d.ts.map +1 -0
  35. package/esm/inspect.js +12 -0
  36. package/esm/inspect.js.map +1 -0
  37. package/esm/native.d.ts +240 -0
  38. package/esm/native.d.ts.map +1 -0
  39. package/esm/native.js +60 -0
  40. package/esm/native.js.map +1 -0
  41. package/esm/pipe-from.d.ts +13 -0
  42. package/esm/pipe-from.d.ts.map +1 -0
  43. package/esm/pipe-from.js +27 -0
  44. package/esm/pipe-from.js.map +1 -0
  45. package/esm/push-readable.d.ts +12 -0
  46. package/esm/push-readable.d.ts.map +1 -0
  47. package/esm/push-readable.js +47 -0
  48. package/esm/push-readable.js.map +1 -0
  49. package/esm/split-string.d.ts +5 -0
  50. package/esm/split-string.d.ts.map +1 -0
  51. package/esm/split-string.js +25 -0
  52. package/esm/split-string.js.map +1 -0
  53. package/esm/stream.d.ts +18 -0
  54. package/esm/stream.d.ts.map +1 -0
  55. package/esm/stream.js +4 -0
  56. package/esm/stream.js.map +1 -0
  57. package/esm/struct-deserialize.d.ts +7 -0
  58. package/esm/struct-deserialize.d.ts.map +1 -0
  59. package/esm/struct-deserialize.js +9 -0
  60. package/esm/struct-deserialize.js.map +1 -0
  61. package/esm/struct-serialize.d.ts +6 -0
  62. package/esm/struct-serialize.d.ts.map +1 -0
  63. package/esm/struct-serialize.js +11 -0
  64. package/esm/struct-serialize.js.map +1 -0
  65. package/esm/wrap-readable.d.ts +21 -0
  66. package/esm/wrap-readable.d.ts.map +1 -0
  67. package/esm/wrap-readable.js +57 -0
  68. package/esm/wrap-readable.js.map +1 -0
  69. package/esm/wrap-writable.d.ts +13 -0
  70. package/esm/wrap-writable.d.ts.map +1 -0
  71. package/esm/wrap-writable.js +53 -0
  72. package/esm/wrap-writable.js.map +1 -0
  73. package/package.json +46 -0
  74. package/src/buffered-transform.ts +58 -0
  75. package/src/buffered.ts +148 -0
  76. package/src/chunk.ts +15 -0
  77. package/src/decode-utf8.ts +12 -0
  78. package/src/duplex.ts +120 -0
  79. package/src/gather-string.ts +15 -0
  80. package/src/index.ts +15 -0
  81. package/src/inspect.ts +12 -0
  82. package/src/native.ts +362 -0
  83. package/src/pipe-from.ts +27 -0
  84. package/src/push-readable.ts +62 -0
  85. package/src/split-string.ts +29 -0
  86. package/src/stream.ts +22 -0
  87. package/src/struct-deserialize.ts +12 -0
  88. package/src/struct-serialize.ts +13 -0
  89. package/src/wrap-readable.ts +70 -0
  90. package/src/wrap-writable.ts +65 -0
  91. package/tsconfig.build.json +3 -0
  92. package/tsconfig.build.tsbuildinfo +1 -0
package/src/native.ts ADDED
@@ -0,0 +1,362 @@
1
+ // cspell: ignore chainable
2
+ // cspell: ignore backpressure
3
+ // cspell: ignore endregion
4
+
5
+ // Because Node.js exports Web Streams types from `stream/web` package,
6
+ // this module uses Top-Level Await to support both Web Browsers and Node.js.
7
+ // For Webpack, the `experimental.topLevelAwait` option is required.
8
+ // (See: https://webpack.js.org/configuration/experiments/)
9
+
10
+ // It's also possible to add fallback to some polyfill.
11
+
12
+ //#region borrowed
13
+ // from https://github.com/microsoft/TypeScript/blob/38da7c600c83e7b31193a62495239a0fe478cb67/lib/lib.webworker.d.ts#L633 until moved to separate lib
14
+ /** A controller object that allows you to abort one or more DOM requests as and when desired. */
15
+ export interface AbortController {
16
+ /**
17
+ * Returns the AbortSignal object associated with this object.
18
+ */
19
+
20
+ readonly signal: AbortSignal;
21
+ /**
22
+ * 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.
23
+ */
24
+ abort(): void;
25
+ }
26
+
27
+ // A simplified version of AbortSignal events.
28
+ export interface AbortSignalEventMap {
29
+ "abort": any;
30
+ }
31
+
32
+ export interface EventListenerOptions {
33
+ capture?: boolean;
34
+ }
35
+
36
+ export interface AddEventListenerOptions extends EventListenerOptions {
37
+ once?: boolean;
38
+ passive?: boolean;
39
+ signal?: AbortSignal;
40
+ }
41
+
42
+ /** A signal object that allows you to communicate with a DOM request (such as a Fetch) and abort it if required via an AbortController object. */
43
+ export interface AbortSignal {
44
+ /**
45
+ * Returns true if this AbortSignal's AbortController has signaled to abort, and false otherwise.
46
+ */
47
+ readonly aborted: boolean;
48
+
49
+ onabort: ((this: AbortSignal, ev: any) => any) | null;
50
+ addEventListener<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
51
+ addEventListener(type: string, listener: (ev: any) => void, options?: boolean | AddEventListenerOptions): void;
52
+ removeEventListener<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
53
+ removeEventListener(type: string, listener: (ev: any) => void, options?: boolean | EventListenerOptions): void;
54
+ }
55
+
56
+ export let AbortController: {
57
+ prototype: AbortController;
58
+ new(): AbortController;
59
+ };
60
+
61
+ export let AbortSignal: {
62
+ prototype: AbortSignal;
63
+ new(): AbortSignal;
64
+ // TODO: Add abort() static
65
+ };
66
+
67
+ ({ AbortController, AbortSignal } = globalThis as any);
68
+ //#endregion borrowed
69
+
70
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L1194-L1206
71
+ export interface QueuingStrategy<T = any> {
72
+ highWaterMark?: number;
73
+ size?: QueuingStrategySize<T>;
74
+ }
75
+
76
+ export interface QueuingStrategyInit {
77
+ /**
78
+ * Creates a new ByteLengthQueuingStrategy with the provided high water mark.
79
+ *
80
+ * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw.
81
+ */
82
+ highWaterMark: number;
83
+ }
84
+
85
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L1450-L1468
86
+ export interface ReadableStreamDefaultReadDoneResult {
87
+ done: true;
88
+ value?: undefined;
89
+ }
90
+
91
+ export interface ReadableStreamDefaultReadValueResult<T> {
92
+ done: false;
93
+ value: T;
94
+ }
95
+
96
+ export interface ReadableWritablePair<R = any, W = any> {
97
+ readable: ReadableStream<R>;
98
+ /**
99
+ * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use.
100
+ *
101
+ * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.
102
+ */
103
+ writable: WritableStream<W>;
104
+ }
105
+
106
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L1636
107
+ export interface StreamPipeOptions {
108
+ preventAbort?: boolean;
109
+ preventCancel?: boolean;
110
+ /**
111
+ * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered.
112
+ *
113
+ * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader.
114
+ *
115
+ * Errors and closures of the source and destination streams propagate as follows:
116
+ *
117
+ * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination.
118
+ *
119
+ * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source.
120
+ *
121
+ * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error.
122
+ *
123
+ * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source.
124
+ *
125
+ * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set.
126
+ */
127
+ preventClose?: boolean;
128
+ signal?: AbortSignal;
129
+ }
130
+
131
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L1710
132
+ export interface Transformer<I = any, O = any> {
133
+ flush?: TransformerFlushCallback<O>;
134
+ readableType?: undefined;
135
+ start?: TransformerStartCallback<O>;
136
+ transform?: TransformerTransformCallback<I, O>;
137
+ writableType?: undefined;
138
+ }
139
+
140
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L11132-L11172
141
+ /** This Streams API interface represents a readable stream of byte data. The Fetch API offers a concrete instance of a ReadableStream through the body property of a Response object. */
142
+ export interface ReadableStream<R = any> {
143
+ readonly locked: boolean;
144
+ cancel(reason?: any): Promise<void>;
145
+ getReader(): ReadableStreamDefaultReader<R>;
146
+ pipeThrough<T>(transform: ReadableWritablePair<T, R>, options?: StreamPipeOptions): ReadableStream<T>;
147
+ pipeTo(destination: WritableStream<R>, options?: StreamPipeOptions): Promise<void>;
148
+ tee(): [ReadableStream<R>, ReadableStream<R>];
149
+ }
150
+
151
+ export let ReadableStream: {
152
+ prototype: ReadableStream;
153
+ new <R = any>(underlyingSource?: UnderlyingSource<R>, strategy?: QueuingStrategy<R>): ReadableStream<R>;
154
+ };
155
+
156
+ export interface ReadableStreamDefaultController<R = any> {
157
+ readonly desiredSize: number | null;
158
+ close(): void;
159
+ enqueue(chunk?: R): void;
160
+ error(e?: any): void;
161
+ }
162
+
163
+ export let ReadableStreamDefaultController: {
164
+ prototype: ReadableStreamDefaultController;
165
+ new(): ReadableStreamDefaultController;
166
+ };
167
+
168
+ export interface ReadableStreamDefaultReader<R = any> extends ReadableStreamGenericReader {
169
+ read(): Promise<ReadableStreamDefaultReadResult<R>>;
170
+ releaseLock(): void;
171
+ }
172
+
173
+ export let ReadableStreamDefaultReader: {
174
+ prototype: ReadableStreamDefaultReader;
175
+ new <R = any>(stream: ReadableStream<R>): ReadableStreamDefaultReader<R>;
176
+ };
177
+
178
+ export interface ReadableStreamGenericReader {
179
+ readonly closed: Promise<undefined>;
180
+ cancel(reason?: any): Promise<void>;
181
+ }
182
+
183
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L13924-L13944
184
+ export interface TransformStream<I = any, O = any> {
185
+ readonly readable: ReadableStream<O>;
186
+ readonly writable: WritableStream<I>;
187
+ }
188
+
189
+ export let TransformStream: {
190
+ prototype: TransformStream;
191
+ new <I = any, O = any>(transformer?: Transformer<I, O>, writableStrategy?: QueuingStrategy<I>, readableStrategy?: QueuingStrategy<O>): TransformStream<I, O>;
192
+ };
193
+
194
+ export interface TransformStreamDefaultController<O = any> {
195
+ readonly desiredSize: number | null;
196
+ enqueue(chunk?: O): void;
197
+ error(reason?: any): void;
198
+ terminate(): void;
199
+ }
200
+
201
+ export let TransformStreamDefaultController: {
202
+ prototype: TransformStreamDefaultController;
203
+ new(): TransformStreamDefaultController;
204
+ };
205
+
206
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L16509-L16546
207
+ /** This Streams API interface provides a standard abstraction for writing streaming data to a destination, known as a sink. This object comes with built-in backpressure and queuing. */
208
+ export interface WritableStream<W = any> {
209
+ readonly locked: boolean;
210
+ abort(reason?: any): Promise<void>;
211
+ close(): Promise<void>;
212
+ getWriter(): WritableStreamDefaultWriter<W>;
213
+ }
214
+
215
+ export let WritableStream: {
216
+ prototype: WritableStream;
217
+ new <W = any>(underlyingSink?: UnderlyingSink<W>, strategy?: QueuingStrategy<W>): WritableStream<W>;
218
+ };
219
+
220
+ /** This Streams API interface represents a controller allowing control of a WritableStream's state. When constructing a WritableStream, the underlying sink is given a corresponding WritableStreamDefaultController instance to manipulate. */
221
+ export interface WritableStreamDefaultController {
222
+ error(e?: any): void;
223
+ }
224
+
225
+ export let WritableStreamDefaultController: {
226
+ prototype: WritableStreamDefaultController;
227
+ new(): WritableStreamDefaultController;
228
+ };
229
+
230
+ /** This Streams API interface is the object returned by WritableStream.getWriter() and once created locks the < writer to the WritableStream ensuring that no other streams can write to the underlying sink. */
231
+ export interface WritableStreamDefaultWriter<W = any> {
232
+ readonly closed: Promise<undefined>;
233
+ readonly desiredSize: number | null;
234
+ readonly ready: Promise<undefined>;
235
+ abort(reason?: any): Promise<void>;
236
+ close(): Promise<void>;
237
+ releaseLock(): void;
238
+ write(chunk?: W): Promise<void>;
239
+ }
240
+
241
+ export let WritableStreamDefaultWriter: {
242
+ prototype: WritableStreamDefaultWriter;
243
+ new <W = any>(stream: WritableStream<W>): WritableStreamDefaultWriter<W>;
244
+ };
245
+
246
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L17045
247
+ export interface QueuingStrategySize<T = any> {
248
+ (chunk: T): number;
249
+ }
250
+
251
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L17065-L17103
252
+ export interface TransformerFlushCallback<O> {
253
+ (controller: TransformStreamDefaultController<O>): void | PromiseLike<void>;
254
+ }
255
+
256
+ export interface TransformerStartCallback<O> {
257
+ (controller: TransformStreamDefaultController<O>): any;
258
+ }
259
+
260
+ export interface TransformerTransformCallback<I, O> {
261
+ (chunk: I, controller: TransformStreamDefaultController<O>): void | PromiseLike<void>;
262
+ }
263
+
264
+ export interface UnderlyingSink<W = any> {
265
+ abort?: UnderlyingSinkAbortCallback;
266
+ close?: UnderlyingSinkCloseCallback;
267
+ start?: UnderlyingSinkStartCallback;
268
+ type?: undefined;
269
+ write?: UnderlyingSinkWriteCallback<W>;
270
+ }
271
+
272
+ export interface UnderlyingSource<R = any> {
273
+ cancel?: UnderlyingSourceCancelCallback;
274
+ pull?: UnderlyingSourcePullCallback<R> | undefined;
275
+ start?: UnderlyingSourceStartCallback<R>;
276
+ type?: undefined;
277
+ }
278
+
279
+ export interface UnderlyingSinkAbortCallback {
280
+ (reason?: any): void | PromiseLike<void>;
281
+ }
282
+
283
+ export interface UnderlyingSinkCloseCallback {
284
+ (): void | PromiseLike<void>;
285
+ }
286
+
287
+ export interface UnderlyingSinkStartCallback {
288
+ (controller: WritableStreamDefaultController): any;
289
+ }
290
+
291
+ export interface UnderlyingSinkWriteCallback<W> {
292
+ (chunk: W, controller: WritableStreamDefaultController): void | PromiseLike<void>;
293
+ }
294
+
295
+ export interface UnderlyingSourceCancelCallback {
296
+ (reason?: any): void | PromiseLike<void>;
297
+ }
298
+
299
+ export interface UnderlyingSourcePullCallback<R> {
300
+ (controller: ReadableStreamController<R>): void | PromiseLike<void>;
301
+ }
302
+
303
+ export interface UnderlyingSourceStartCallback<R> {
304
+ (controller: ReadableStreamController<R>): any;
305
+ }
306
+
307
+ // https://github.com/microsoft/TypeScript-DOM-lib-generator/blob/11d922f302743cb3fcee9ab59b03d40074a2965c/baselines/dom.generated.d.ts#L17796-L17798
308
+ export type ReadableStreamController<T> = ReadableStreamDefaultController<T>;
309
+ export type ReadableStreamDefaultReadResult<T> = ReadableStreamDefaultReadValueResult<T> | ReadableStreamDefaultReadDoneResult;
310
+ export type ReadableStreamReader<T> = ReadableStreamDefaultReader<T>;
311
+
312
+ // Extra
313
+ export interface ReadableStreamIteratorOptions {
314
+ preventCancel?: boolean;
315
+ }
316
+
317
+ // This library can't use `@types/node` or `lib: dom`
318
+ // because they will pollute the global scope
319
+ // So `ReadableStream`, `WritableStream` and `TransformStream` are not available
320
+
321
+ if ('ReadableStream' in globalThis && 'WritableStream' in globalThis && 'TransformStream' in globalThis) {
322
+ ({
323
+ ReadableStream,
324
+ ReadableStreamDefaultController,
325
+ ReadableStreamDefaultReader,
326
+ TransformStream,
327
+ TransformStreamDefaultController,
328
+ WritableStream,
329
+ WritableStreamDefaultController,
330
+ WritableStreamDefaultWriter,
331
+ } = globalThis as any);
332
+ } else {
333
+ try {
334
+ // // Node.js 16 has Web Streams types in `stream/web` module
335
+ ({
336
+ // @ts-ignore
337
+ ReadableStream,
338
+ // @ts-ignore
339
+ ReadableStreamDefaultController,
340
+ // @ts-ignore
341
+ ReadableStreamDefaultReader,
342
+ // @ts-ignore
343
+ TransformStream,
344
+ // @ts-ignore
345
+ TransformStreamDefaultController,
346
+ // @ts-ignore
347
+ WritableStream,
348
+ // @ts-ignore
349
+ WritableStreamDefaultController,
350
+ // @ts-ignore
351
+ WritableStreamDefaultWriter,
352
+ // @ts-ignore
353
+ } = await import(/* webpackIgnore: true */ 'stream/web'));
354
+ } catch { }
355
+ }
356
+
357
+ // TODO: stream/detect: Load some polyfills
358
+
359
+ // @ts-ignore
360
+ if (!ReadableStream || !WritableStream || !TransformStream) {
361
+ // throw new Error('Web Streams API is not available');
362
+ }
@@ -0,0 +1,27 @@
1
+ import { WritableStream, type ReadableWritablePair } from "./stream.js";
2
+
3
+ /**
4
+ * Create a new `WritableStream` that, when written to, will write that chunk to
5
+ * `pair.writable`, when pipe `pair.readable` to `writable`.
6
+ *
7
+ * It's the opposite of `ReadableStream.pipeThrough`.
8
+ *
9
+ * @param writable The `WritableStream` to write to.
10
+ * @param pair A `TransformStream` that converts chunks.
11
+ * @returns A new `WritableStream`.
12
+ */
13
+ export function pipeFrom<W, T>(writable: WritableStream<W>, pair: ReadableWritablePair<W, T>) {
14
+ const writer = pair.writable.getWriter();
15
+ const pipe = pair.readable
16
+ .pipeTo(writable);
17
+ return new WritableStream<T>({
18
+ async write(chunk) {
19
+ await writer.ready;
20
+ await writer.write(chunk);
21
+ },
22
+ async close() {
23
+ await writer.close();
24
+ await pipe;
25
+ }
26
+ });
27
+ }
@@ -0,0 +1,62 @@
1
+ import { PromiseResolver } from '@yume-chan/async';
2
+ import { AbortController, AbortSignal, QueuingStrategy, ReadableStream } from "./stream.js";
3
+
4
+ export interface PushReadableStreamController<T> {
5
+ abortSignal: AbortSignal;
6
+
7
+ enqueue(chunk: T): Promise<void>;
8
+
9
+ close(): void;
10
+
11
+ error(e?: any): void;
12
+ }
13
+
14
+ export type PushReadableStreamSource<T> = (controller: PushReadableStreamController<T>) => void;
15
+
16
+ export class PushReadableStream<T> extends ReadableStream<T> {
17
+ public constructor(source: PushReadableStreamSource<T>, strategy?: QueuingStrategy<T>) {
18
+ let waterMarkLow: PromiseResolver<void> | undefined;
19
+ const canceled: AbortController = new AbortController();
20
+
21
+ super({
22
+ start: (controller) => {
23
+ source({
24
+ abortSignal: canceled.signal,
25
+ async enqueue(chunk) {
26
+ if (canceled.signal.aborted) {
27
+ // If the stream is already cancelled,
28
+ // throw immediately.
29
+ throw canceled.signal.reason ?? new Error('Aborted');
30
+ }
31
+
32
+ // Only when the stream is errored, `desiredSize` will be `null`.
33
+ // But since `null <= 0` is `true`
34
+ // (`null <= 0` is evaluated as `!(null > 0)` => `!false` => `true`),
35
+ // not handling it will cause a deadlock.
36
+ if ((controller.desiredSize ?? 1) <= 0) {
37
+ waterMarkLow = new PromiseResolver<void>();
38
+ await waterMarkLow.promise;
39
+ }
40
+
41
+ // `controller.enqueue` will throw error for us
42
+ // if the stream is already errored.
43
+ controller.enqueue(chunk);
44
+ },
45
+ close() {
46
+ controller.close();
47
+ },
48
+ error(e) {
49
+ controller.error(e);
50
+ },
51
+ });
52
+ },
53
+ pull: () => {
54
+ waterMarkLow?.resolve();
55
+ },
56
+ cancel: async (reason) => {
57
+ canceled.abort(reason);
58
+ waterMarkLow?.reject(reason);
59
+ },
60
+ }, strategy);
61
+ }
62
+ }
@@ -0,0 +1,29 @@
1
+ import { TransformStream } from "./stream.js";
2
+
3
+ function* split(input: string, separator: string): Generator<string, void, void> {
4
+ let start = 0;
5
+
6
+ while (true) {
7
+ const index = input.indexOf(separator, start);
8
+ if (index === -1) {
9
+ return;
10
+ }
11
+
12
+ const part = input.substring(start, index);
13
+ yield part;
14
+
15
+ start = index + 1;
16
+ }
17
+ }
18
+
19
+ export class SplitStringStream extends TransformStream<string, string> {
20
+ public constructor(separator: string) {
21
+ super({
22
+ transform(chunk, controller) {
23
+ for (const part of split(chunk, separator)) {
24
+ controller.enqueue(part);
25
+ }
26
+ }
27
+ });
28
+ }
29
+ }
package/src/stream.ts ADDED
@@ -0,0 +1,22 @@
1
+ import type { AbortSignal } from "web-streams-polyfill";
2
+ export * from 'web-streams-polyfill';
3
+
4
+ /** A controller object that allows you to abort one or more DOM requests as and when desired. */
5
+ export interface AbortController {
6
+ /**
7
+ * Returns the AbortSignal object associated with this object.
8
+ */
9
+
10
+ readonly signal: AbortSignal;
11
+ /**
12
+ * Invoking this method will set this object's AbortSignal's aborted flag and signal to any observers that the associated activity is to be aborted.
13
+ */
14
+ abort(reason?: any): void;
15
+ }
16
+
17
+ export let AbortController: {
18
+ prototype: AbortController;
19
+ new(): AbortController;
20
+ };
21
+
22
+ ({ AbortController } = globalThis as any);
@@ -0,0 +1,12 @@
1
+ import type Struct from "@yume-chan/struct";
2
+ import type { StructValueType } from "@yume-chan/struct";
3
+ import { BufferedTransformStream } from './buffered-transform.js';
4
+
5
+ export class StructDeserializeStream<T extends Struct<any, any, any, any>>
6
+ extends BufferedTransformStream<StructValueType<T>> {
7
+ public constructor(struct: T) {
8
+ super((stream) => {
9
+ return struct.deserialize(stream)
10
+ });
11
+ }
12
+ }
@@ -0,0 +1,13 @@
1
+ import type Struct from "@yume-chan/struct";
2
+ import { TransformStream } from "./stream.js";
3
+
4
+ export class StructSerializeStream<T extends Struct<any, any, any, any>>
5
+ extends TransformStream<T['TInit'], Uint8Array>{
6
+ constructor(struct: T) {
7
+ super({
8
+ transform(chunk, controller) {
9
+ controller.enqueue(struct.serialize(chunk));
10
+ },
11
+ });
12
+ }
13
+ }
@@ -0,0 +1,70 @@
1
+ import type { ValueOrPromise } from "@yume-chan/struct";
2
+ import { ReadableStream, ReadableStreamDefaultController, ReadableStreamDefaultReader } from "./stream.js";
3
+
4
+ export type WrapReadableStreamStart<T> = (controller: ReadableStreamDefaultController<T>) => ValueOrPromise<ReadableStream<T>>;
5
+
6
+ export interface ReadableStreamWrapper<T> {
7
+ start: WrapReadableStreamStart<T>;
8
+ cancel?(reason?: any): ValueOrPromise<void>;
9
+ close?(): ValueOrPromise<void>;
10
+ }
11
+
12
+ function getWrappedReadableStream<T>(
13
+ wrapper: ReadableStream<T> | WrapReadableStreamStart<T> | ReadableStreamWrapper<T>,
14
+ controller: ReadableStreamDefaultController<T>
15
+ ) {
16
+ if ('start' in wrapper) {
17
+ return wrapper.start(controller);
18
+ } else if (typeof wrapper === 'function') {
19
+ return wrapper(controller);
20
+ } else {
21
+ // Can't use `wrapper instanceof ReadableStream`
22
+ // Because we want to be compatible with any ReadableStream-like objects
23
+ return wrapper;
24
+ }
25
+ }
26
+
27
+ /**
28
+ * This class has multiple usages:
29
+ *
30
+ * 1. Get notified when the stream is cancelled or closed.
31
+ * 2. Synchronously create a `ReadableStream` by asynchronously return another `ReadableStream`.
32
+ * 3. Convert native `ReadableStream`s to polyfilled ones so they can `pipe` between.
33
+ */
34
+ export class WrapReadableStream<T> extends ReadableStream<T>{
35
+ public readable!: ReadableStream<T>;
36
+
37
+ private reader!: ReadableStreamDefaultReader<T>;
38
+
39
+ public constructor(wrapper: ReadableStream<T> | WrapReadableStreamStart<T> | ReadableStreamWrapper<T>) {
40
+ super({
41
+ start: async (controller) => {
42
+ // `start` is invoked before `ReadableStream`'s constructor finish,
43
+ // so using `this` synchronously causes
44
+ // "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
45
+ // Queue a microtask to avoid this.
46
+ await Promise.resolve();
47
+
48
+ this.readable = await getWrappedReadableStream(wrapper, controller);
49
+ this.reader = this.readable.getReader();
50
+ },
51
+ cancel: async (reason) => {
52
+ await this.reader.cancel(reason);
53
+ if ('cancel' in wrapper) {
54
+ await wrapper.cancel?.(reason);
55
+ }
56
+ },
57
+ pull: async (controller) => {
58
+ const result = await this.reader.read();
59
+ if (result.done) {
60
+ controller.close();
61
+ if ('close' in wrapper) {
62
+ await wrapper.close?.();
63
+ }
64
+ } else {
65
+ controller.enqueue(result.value);
66
+ }
67
+ }
68
+ });
69
+ }
70
+ }
@@ -0,0 +1,65 @@
1
+ import type { ValueOrPromise } from "@yume-chan/struct";
2
+ import { WritableStream, WritableStreamDefaultWriter } from "./stream.js";
3
+
4
+ export type WrapWritableStreamStart<T> = () => ValueOrPromise<WritableStream<T>>;
5
+
6
+ export interface WritableStreamWrapper<T> {
7
+ start: WrapWritableStreamStart<T>;
8
+ close?(): Promise<void>;
9
+ }
10
+
11
+ async function getWrappedWritableStream<T>(
12
+ wrapper: WritableStream<T> | WrapWritableStreamStart<T> | WritableStreamWrapper<T>
13
+ ) {
14
+ if ('start' in wrapper) {
15
+ return await wrapper.start();
16
+ } else if (typeof wrapper === 'function') {
17
+ return await wrapper();
18
+ } else {
19
+ // Can't use `wrapper instanceof WritableStream`
20
+ // Because we want to be compatible with any WritableStream-like objects
21
+ return wrapper;
22
+ }
23
+ }
24
+
25
+ export class WrapWritableStream<T> extends WritableStream<T> {
26
+ public writable!: WritableStream<T>;
27
+
28
+ private writer!: WritableStreamDefaultWriter<T>;
29
+
30
+ public constructor(wrapper: WritableStream<T> | WrapWritableStreamStart<T> | WritableStreamWrapper<T>) {
31
+ super({
32
+ start: async () => {
33
+ // `start` is invoked before `ReadableStream`'s constructor finish,
34
+ // so using `this` synchronously causes
35
+ // "Must call super constructor in derived class before accessing 'this' or returning from derived constructor".
36
+ // Queue a microtask to avoid this.
37
+ await Promise.resolve();
38
+
39
+ this.writable = await getWrappedWritableStream(wrapper);
40
+ this.writer = this.writable.getWriter();
41
+ },
42
+ write: async (chunk) => {
43
+ // Maintain back pressure
44
+ await this.writer.ready;
45
+ await this.writer.write(chunk);
46
+ },
47
+ abort: async (reason) => {
48
+ await this.writer.abort(reason);
49
+ if ('close' in wrapper) {
50
+ await wrapper.close?.();
51
+ }
52
+ },
53
+ close: async () => {
54
+ // Close the inner stream first.
55
+ // Usually the inner stream is a logical sub-stream over the outer stream,
56
+ // closing the outer stream first will make the inner stream incapable of
57
+ // sending data in its `close` handler.
58
+ await this.writer.close();
59
+ if ('close' in wrapper) {
60
+ await wrapper.close?.();
61
+ }
62
+ },
63
+ });
64
+ }
65
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "./node_modules/@yume-chan/ts-package-builder/tsconfig.base.json"
3
+ }