@gjsify/web-streams 0.1.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/README.md +30 -0
- package/lib/esm/index.js +121 -0
- package/lib/esm/queuing-strategies.js +56 -0
- package/lib/esm/readable-stream.js +1064 -0
- package/lib/esm/text-decoder-stream.js +126 -0
- package/lib/esm/text-encoder-stream.js +46 -0
- package/lib/esm/transform-stream.js +336 -0
- package/lib/esm/util.js +161 -0
- package/lib/esm/writable-stream.js +676 -0
- package/lib/types/index.d.ts +77 -0
- package/lib/types/queuing-strategies.d.ts +18 -0
- package/lib/types/readable-stream.d.ts +61 -0
- package/lib/types/text-decoder-stream.d.ts +16 -0
- package/lib/types/text-encoder-stream.d.ts +15 -0
- package/lib/types/transform-stream.d.ts +21 -0
- package/lib/types/util.d.ts +40 -0
- package/lib/types/writable-stream.d.ts +49 -0
- package/package.json +44 -0
- package/src/index.spec.ts +2043 -0
- package/src/index.ts +131 -0
- package/src/queuing-strategies.ts +67 -0
- package/src/readable-stream.ts +1337 -0
- package/src/test.mts +6 -0
- package/src/text-decoder-stream.ts +183 -0
- package/src/text-encoder-stream.ts +62 -0
- package/src/transform-stream.ts +410 -0
- package/src/util.ts +170 -0
- package/src/writable-stream.ts +773 -0
- package/tsconfig.json +32 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
// WHATWG Streams — WritableStream
|
|
2
|
+
// Adapted from refs/node/lib/internal/webstreams/writablestream.js
|
|
3
|
+
// Copyright (c) Node.js contributors. MIT license.
|
|
4
|
+
// Modifications: Removed primordials, transfer, inspect, Node.js error codes
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
kState, kType,
|
|
8
|
+
isBrandCheck,
|
|
9
|
+
createPromiseCallback,
|
|
10
|
+
dequeueValue,
|
|
11
|
+
enqueueValueWithSize,
|
|
12
|
+
extractHighWaterMark,
|
|
13
|
+
extractSizeAlgorithm,
|
|
14
|
+
peekQueueValue,
|
|
15
|
+
resetQueue,
|
|
16
|
+
setPromiseHandled,
|
|
17
|
+
nonOpCancel,
|
|
18
|
+
nonOpStart,
|
|
19
|
+
nonOpWrite,
|
|
20
|
+
} from './util.js';
|
|
21
|
+
|
|
22
|
+
// Use native AbortController if available, otherwise a minimal shim
|
|
23
|
+
const _AbortController = typeof globalThis.AbortController === 'function'
|
|
24
|
+
? globalThis.AbortController
|
|
25
|
+
: class AbortControllerShim {
|
|
26
|
+
signal = { aborted: false, reason: undefined as unknown, addEventListener() {}, removeEventListener() {} };
|
|
27
|
+
abort(reason?: unknown) {
|
|
28
|
+
(this.signal as { aborted: boolean; reason: unknown }).aborted = true;
|
|
29
|
+
(this.signal as { aborted: boolean; reason: unknown }).reason = reason;
|
|
30
|
+
}
|
|
31
|
+
} as unknown as typeof AbortController;
|
|
32
|
+
|
|
33
|
+
const kAbort = Symbol('kAbort');
|
|
34
|
+
const kCloseSentinel = Symbol('kCloseSentinel');
|
|
35
|
+
const kError = Symbol('kError');
|
|
36
|
+
const kSkipThrow = Symbol('kSkipThrow');
|
|
37
|
+
|
|
38
|
+
// ---- WritableStream ----
|
|
39
|
+
|
|
40
|
+
export class WritableStream {
|
|
41
|
+
[kType] = 'WritableStream';
|
|
42
|
+
[kState]: any;
|
|
43
|
+
|
|
44
|
+
constructor(sink: any = {}, strategy: any = {}) {
|
|
45
|
+
if (sink != null && typeof sink !== 'object') {
|
|
46
|
+
throw new TypeError('sink must be an object');
|
|
47
|
+
}
|
|
48
|
+
if (strategy != null && typeof strategy !== 'object') {
|
|
49
|
+
throw new TypeError('strategy must be an object');
|
|
50
|
+
}
|
|
51
|
+
const type = sink?.type;
|
|
52
|
+
if (type !== undefined) {
|
|
53
|
+
throw new RangeError(`Invalid type: ${type}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this[kState] = createWritableStreamState();
|
|
57
|
+
|
|
58
|
+
const size = extractSizeAlgorithm(strategy?.size);
|
|
59
|
+
const highWaterMark = extractHighWaterMark(strategy?.highWaterMark, 1);
|
|
60
|
+
|
|
61
|
+
setupWritableStreamDefaultControllerFromSink(this, sink, highWaterMark, size);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get locked(): boolean {
|
|
65
|
+
if (!isWritableStream(this)) throw new TypeError('Invalid this');
|
|
66
|
+
return isWritableStreamLocked(this);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
abort(reason?: unknown): Promise<void> {
|
|
70
|
+
if (!isWritableStream(this)) return Promise.reject(new TypeError('Invalid this'));
|
|
71
|
+
if (isWritableStreamLocked(this)) {
|
|
72
|
+
return Promise.reject(new TypeError('WritableStream is locked'));
|
|
73
|
+
}
|
|
74
|
+
return writableStreamAbort(this, reason);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
close(): Promise<void> {
|
|
78
|
+
if (!isWritableStream(this)) return Promise.reject(new TypeError('Invalid this'));
|
|
79
|
+
if (isWritableStreamLocked(this)) {
|
|
80
|
+
return Promise.reject(new TypeError('WritableStream is locked'));
|
|
81
|
+
}
|
|
82
|
+
if (writableStreamCloseQueuedOrInFlight(this)) {
|
|
83
|
+
return Promise.reject(new TypeError('Failure closing WritableStream'));
|
|
84
|
+
}
|
|
85
|
+
return writableStreamClose(this);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getWriter(): WritableStreamDefaultWriter {
|
|
89
|
+
if (!isWritableStream(this)) throw new TypeError('Invalid this');
|
|
90
|
+
return new WritableStreamDefaultWriter(this);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get [Symbol.toStringTag]() {
|
|
94
|
+
return 'WritableStream';
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---- WritableStreamDefaultWriter ----
|
|
99
|
+
|
|
100
|
+
export class WritableStreamDefaultWriter {
|
|
101
|
+
[kType] = 'WritableStreamDefaultWriter';
|
|
102
|
+
[kState]: any;
|
|
103
|
+
|
|
104
|
+
constructor(stream: WritableStream) {
|
|
105
|
+
if (!isWritableStream(stream)) {
|
|
106
|
+
throw new TypeError('Expected a WritableStream');
|
|
107
|
+
}
|
|
108
|
+
this[kState] = {
|
|
109
|
+
stream: undefined,
|
|
110
|
+
close: { promise: undefined, resolve: undefined, reject: undefined },
|
|
111
|
+
ready: { promise: undefined, resolve: undefined, reject: undefined },
|
|
112
|
+
};
|
|
113
|
+
setupWritableStreamDefaultWriter(this, stream);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get closed(): Promise<void> {
|
|
117
|
+
if (!isWritableStreamDefaultWriter(this)) return Promise.reject(new TypeError('Invalid this'));
|
|
118
|
+
return this[kState].close.promise;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get desiredSize(): number | null {
|
|
122
|
+
if (!isWritableStreamDefaultWriter(this)) throw new TypeError('Invalid this');
|
|
123
|
+
if (this[kState].stream === undefined) {
|
|
124
|
+
throw new TypeError('Writer is not bound to a WritableStream');
|
|
125
|
+
}
|
|
126
|
+
return writableStreamDefaultWriterGetDesiredSize(this);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
get ready(): Promise<void> {
|
|
130
|
+
if (!isWritableStreamDefaultWriter(this)) return Promise.reject(new TypeError('Invalid this'));
|
|
131
|
+
return this[kState].ready.promise;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
abort(reason?: unknown): Promise<void> {
|
|
135
|
+
if (!isWritableStreamDefaultWriter(this)) return Promise.reject(new TypeError('Invalid this'));
|
|
136
|
+
if (this[kState].stream === undefined) {
|
|
137
|
+
return Promise.reject(new TypeError('Writer is not bound to a WritableStream'));
|
|
138
|
+
}
|
|
139
|
+
return writableStreamDefaultWriterAbort(this, reason);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
close(): Promise<void> {
|
|
143
|
+
if (!isWritableStreamDefaultWriter(this)) return Promise.reject(new TypeError('Invalid this'));
|
|
144
|
+
const { stream } = this[kState];
|
|
145
|
+
if (stream === undefined) {
|
|
146
|
+
return Promise.reject(new TypeError('Writer is not bound to a WritableStream'));
|
|
147
|
+
}
|
|
148
|
+
if (writableStreamCloseQueuedOrInFlight(stream)) {
|
|
149
|
+
return Promise.reject(new TypeError('Failure to close WritableStream'));
|
|
150
|
+
}
|
|
151
|
+
return writableStreamDefaultWriterClose(this);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
releaseLock(): void {
|
|
155
|
+
if (!isWritableStreamDefaultWriter(this)) throw new TypeError('Invalid this');
|
|
156
|
+
const { stream } = this[kState];
|
|
157
|
+
if (stream === undefined) return;
|
|
158
|
+
writableStreamDefaultWriterRelease(this);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
write(chunk?: any): Promise<void> {
|
|
162
|
+
if (!isWritableStreamDefaultWriter(this)) return Promise.reject(new TypeError('Invalid this'));
|
|
163
|
+
if (this[kState].stream === undefined) {
|
|
164
|
+
return Promise.reject(new TypeError('Writer is not bound to a WritableStream'));
|
|
165
|
+
}
|
|
166
|
+
return writableStreamDefaultWriterWrite(this, chunk);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
get [Symbol.toStringTag]() {
|
|
170
|
+
return 'WritableStreamDefaultWriter';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ---- WritableStreamDefaultController ----
|
|
175
|
+
|
|
176
|
+
export class WritableStreamDefaultController {
|
|
177
|
+
[kType] = 'WritableStreamDefaultController';
|
|
178
|
+
[kState]: any;
|
|
179
|
+
|
|
180
|
+
constructor(skipThrowSymbol?: symbol) {
|
|
181
|
+
if (skipThrowSymbol !== kSkipThrow) {
|
|
182
|
+
throw new TypeError('Illegal constructor');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
[kAbort](reason: unknown) {
|
|
187
|
+
const result = this[kState].abortAlgorithm(reason);
|
|
188
|
+
writableStreamDefaultControllerClearAlgorithms(this);
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
[kError]() {
|
|
193
|
+
resetQueue(this);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
get signal(): AbortSignal {
|
|
197
|
+
if (!isWritableStreamDefaultController(this)) throw new TypeError('Invalid this');
|
|
198
|
+
return this[kState].abortController.signal;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
error(error?: unknown): void {
|
|
202
|
+
if (!isWritableStreamDefaultController(this)) throw new TypeError('Invalid this');
|
|
203
|
+
if (this[kState].stream[kState].state !== 'writable') return;
|
|
204
|
+
writableStreamDefaultControllerError(this, error);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
get [Symbol.toStringTag]() {
|
|
208
|
+
return 'WritableStreamDefaultController';
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ---- Brand checks ----
|
|
213
|
+
|
|
214
|
+
export const isWritableStream = isBrandCheck('WritableStream');
|
|
215
|
+
export const isWritableStreamDefaultWriter = isBrandCheck('WritableStreamDefaultWriter');
|
|
216
|
+
export const isWritableStreamDefaultController = isBrandCheck('WritableStreamDefaultController');
|
|
217
|
+
|
|
218
|
+
// ---- Internal state factory ----
|
|
219
|
+
|
|
220
|
+
interface PromiseRecord<T = void> {
|
|
221
|
+
promise: Promise<T> | undefined;
|
|
222
|
+
resolve: ((value: T | PromiseLike<T>) => void) | undefined;
|
|
223
|
+
reject: ((reason?: unknown) => void) | undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function createEmptyPromiseRecord<T = void>(): PromiseRecord<T> {
|
|
227
|
+
return { promise: undefined, resolve: undefined, reject: undefined };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function createWritableStreamState() {
|
|
231
|
+
return {
|
|
232
|
+
close: Promise.withResolvers<void>(),
|
|
233
|
+
closeRequest: createEmptyPromiseRecord(),
|
|
234
|
+
inFlightWriteRequest: createEmptyPromiseRecord(),
|
|
235
|
+
inFlightCloseRequest: createEmptyPromiseRecord(),
|
|
236
|
+
pendingAbortRequest: {
|
|
237
|
+
abort: createEmptyPromiseRecord(),
|
|
238
|
+
reason: undefined as unknown,
|
|
239
|
+
wasAlreadyErroring: false,
|
|
240
|
+
},
|
|
241
|
+
backpressure: false,
|
|
242
|
+
controller: undefined as WritableStreamDefaultController | undefined,
|
|
243
|
+
state: 'writable' as string,
|
|
244
|
+
storedError: undefined as unknown,
|
|
245
|
+
writeRequests: [] as PromiseRecord[],
|
|
246
|
+
writer: undefined as WritableStreamDefaultWriter | undefined,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ---- Internal functions ----
|
|
251
|
+
|
|
252
|
+
export function isWritableStreamLocked(stream: any): boolean {
|
|
253
|
+
return stream[kState].writer !== undefined;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function setupWritableStreamDefaultWriter(writer: any, stream: any): void {
|
|
257
|
+
if (isWritableStreamLocked(stream)) {
|
|
258
|
+
throw new TypeError('WritableStream is locked');
|
|
259
|
+
}
|
|
260
|
+
writer[kState].stream = stream;
|
|
261
|
+
stream[kState].writer = writer;
|
|
262
|
+
switch (stream[kState].state) {
|
|
263
|
+
case 'writable':
|
|
264
|
+
if (!writableStreamCloseQueuedOrInFlight(stream) && stream[kState].backpressure) {
|
|
265
|
+
writer[kState].ready = Promise.withResolvers<void>();
|
|
266
|
+
} else {
|
|
267
|
+
writer[kState].ready = {
|
|
268
|
+
promise: Promise.resolve(),
|
|
269
|
+
resolve: undefined,
|
|
270
|
+
reject: undefined,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
writer[kState].close = Promise.withResolvers<void>();
|
|
274
|
+
break;
|
|
275
|
+
case 'erroring':
|
|
276
|
+
writer[kState].ready = {
|
|
277
|
+
promise: Promise.reject(stream[kState].storedError),
|
|
278
|
+
resolve: undefined,
|
|
279
|
+
reject: undefined,
|
|
280
|
+
};
|
|
281
|
+
setPromiseHandled(writer[kState].ready.promise);
|
|
282
|
+
writer[kState].close = Promise.withResolvers<void>();
|
|
283
|
+
break;
|
|
284
|
+
case 'closed':
|
|
285
|
+
writer[kState].ready = {
|
|
286
|
+
promise: Promise.resolve(),
|
|
287
|
+
resolve: undefined,
|
|
288
|
+
reject: undefined,
|
|
289
|
+
};
|
|
290
|
+
writer[kState].close = {
|
|
291
|
+
promise: Promise.resolve(),
|
|
292
|
+
resolve: undefined,
|
|
293
|
+
reject: undefined,
|
|
294
|
+
};
|
|
295
|
+
break;
|
|
296
|
+
default:
|
|
297
|
+
writer[kState].ready = {
|
|
298
|
+
promise: Promise.reject(stream[kState].storedError),
|
|
299
|
+
resolve: undefined,
|
|
300
|
+
reject: undefined,
|
|
301
|
+
};
|
|
302
|
+
writer[kState].close = {
|
|
303
|
+
promise: Promise.reject(stream[kState].storedError),
|
|
304
|
+
resolve: undefined,
|
|
305
|
+
reject: undefined,
|
|
306
|
+
};
|
|
307
|
+
setPromiseHandled(writer[kState].ready.promise);
|
|
308
|
+
setPromiseHandled(writer[kState].close.promise);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function writableStreamAbort(stream: any, reason?: unknown): Promise<void> {
|
|
313
|
+
const { state, controller } = stream[kState];
|
|
314
|
+
if (state === 'closed' || state === 'errored') return Promise.resolve();
|
|
315
|
+
|
|
316
|
+
controller[kState].abortController.abort(reason);
|
|
317
|
+
|
|
318
|
+
if (stream[kState].pendingAbortRequest.abort.promise !== undefined) {
|
|
319
|
+
return stream[kState].pendingAbortRequest.abort.promise;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let wasAlreadyErroring = false;
|
|
323
|
+
if (state === 'erroring') {
|
|
324
|
+
wasAlreadyErroring = true;
|
|
325
|
+
reason = undefined;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const abort = Promise.withResolvers<void>();
|
|
329
|
+
stream[kState].pendingAbortRequest = { abort, reason, wasAlreadyErroring };
|
|
330
|
+
|
|
331
|
+
if (!wasAlreadyErroring) writableStreamStartErroring(stream, reason);
|
|
332
|
+
|
|
333
|
+
return abort.promise;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function writableStreamClose(stream: any): Promise<void> {
|
|
337
|
+
const { state, writer, backpressure, controller } = stream[kState];
|
|
338
|
+
if (state === 'closed' || state === 'errored') {
|
|
339
|
+
return Promise.reject(new TypeError('WritableStream is closed'));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
stream[kState].closeRequest = Promise.withResolvers<void>();
|
|
343
|
+
const { promise } = stream[kState].closeRequest;
|
|
344
|
+
if (writer !== undefined && backpressure && state === 'writable') {
|
|
345
|
+
writer[kState].ready.resolve?.();
|
|
346
|
+
}
|
|
347
|
+
writableStreamDefaultControllerClose(controller);
|
|
348
|
+
return promise;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function writableStreamUpdateBackpressure(stream: any, backpressure: boolean): void {
|
|
352
|
+
const { writer } = stream[kState];
|
|
353
|
+
if (writer !== undefined && stream[kState].backpressure !== backpressure) {
|
|
354
|
+
if (backpressure) {
|
|
355
|
+
writer[kState].ready = Promise.withResolvers<void>();
|
|
356
|
+
} else {
|
|
357
|
+
writer[kState].ready.resolve?.();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
stream[kState].backpressure = backpressure;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function writableStreamStartErroring(stream: any, reason: unknown): void {
|
|
364
|
+
const { controller, writer } = stream[kState];
|
|
365
|
+
stream[kState].state = 'erroring';
|
|
366
|
+
stream[kState].storedError = reason;
|
|
367
|
+
if (writer !== undefined) {
|
|
368
|
+
writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason);
|
|
369
|
+
}
|
|
370
|
+
if (!writableStreamHasOperationMarkedInFlight(stream) && controller[kState].started) {
|
|
371
|
+
writableStreamFinishErroring(stream);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream: any): void {
|
|
376
|
+
if (stream[kState].closeRequest.promise !== undefined) {
|
|
377
|
+
stream[kState].closeRequest.reject?.(stream[kState].storedError);
|
|
378
|
+
stream[kState].closeRequest = { promise: undefined, resolve: undefined, reject: undefined };
|
|
379
|
+
}
|
|
380
|
+
setPromiseHandled(stream[kState].close.promise);
|
|
381
|
+
stream[kState].close.reject?.(stream[kState].storedError);
|
|
382
|
+
|
|
383
|
+
const { writer } = stream[kState];
|
|
384
|
+
if (writer !== undefined) {
|
|
385
|
+
setPromiseHandled(writer[kState].close.promise);
|
|
386
|
+
writer[kState].close.reject?.(stream[kState].storedError);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function writableStreamMarkFirstWriteRequestInFlight(stream: any): void {
|
|
391
|
+
const writeRequest = stream[kState].writeRequests.shift();
|
|
392
|
+
stream[kState].inFlightWriteRequest = writeRequest;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function writableStreamMarkCloseRequestInFlight(stream: any): void {
|
|
396
|
+
stream[kState].inFlightCloseRequest = stream[kState].closeRequest;
|
|
397
|
+
stream[kState].closeRequest = { promise: undefined, resolve: undefined, reject: undefined };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function writableStreamHasOperationMarkedInFlight(stream: any): boolean {
|
|
401
|
+
return stream[kState].inFlightWriteRequest.promise !== undefined ||
|
|
402
|
+
stream[kState].inFlightCloseRequest.promise !== undefined;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function writableStreamFinishInFlightWriteWithError(stream: any, error: unknown): void {
|
|
406
|
+
stream[kState].inFlightWriteRequest.reject?.(error);
|
|
407
|
+
stream[kState].inFlightWriteRequest = { promise: undefined, resolve: undefined, reject: undefined };
|
|
408
|
+
writableStreamDealWithRejection(stream, error);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function writableStreamFinishInFlightWrite(stream: any): void {
|
|
412
|
+
stream[kState].inFlightWriteRequest.resolve?.();
|
|
413
|
+
stream[kState].inFlightWriteRequest = { promise: undefined, resolve: undefined, reject: undefined };
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function writableStreamFinishInFlightCloseWithError(stream: any, error: unknown): void {
|
|
417
|
+
stream[kState].inFlightCloseRequest.reject?.(error);
|
|
418
|
+
stream[kState].inFlightCloseRequest = { promise: undefined, resolve: undefined, reject: undefined };
|
|
419
|
+
if (stream[kState].pendingAbortRequest.abort.promise !== undefined) {
|
|
420
|
+
stream[kState].pendingAbortRequest.abort.reject?.(error);
|
|
421
|
+
stream[kState].pendingAbortRequest = {
|
|
422
|
+
abort: { promise: undefined, resolve: undefined, reject: undefined },
|
|
423
|
+
reason: undefined,
|
|
424
|
+
wasAlreadyErroring: false,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
writableStreamDealWithRejection(stream, error);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function writableStreamFinishInFlightClose(stream: any): void {
|
|
431
|
+
stream[kState].inFlightCloseRequest.resolve?.();
|
|
432
|
+
stream[kState].inFlightCloseRequest = { promise: undefined, resolve: undefined, reject: undefined };
|
|
433
|
+
if (stream[kState].state === 'erroring') {
|
|
434
|
+
stream[kState].storedError = undefined;
|
|
435
|
+
if (stream[kState].pendingAbortRequest.abort.promise !== undefined) {
|
|
436
|
+
stream[kState].pendingAbortRequest.abort.resolve?.();
|
|
437
|
+
stream[kState].pendingAbortRequest = {
|
|
438
|
+
abort: { promise: undefined, resolve: undefined, reject: undefined },
|
|
439
|
+
reason: undefined,
|
|
440
|
+
wasAlreadyErroring: false,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
stream[kState].state = 'closed';
|
|
445
|
+
if (stream[kState].writer !== undefined) {
|
|
446
|
+
stream[kState].writer[kState].close.resolve?.();
|
|
447
|
+
}
|
|
448
|
+
stream[kState].close.resolve?.();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function writableStreamFinishErroring(stream: any): void {
|
|
452
|
+
stream[kState].state = 'errored';
|
|
453
|
+
stream[kState].controller[kError]();
|
|
454
|
+
const storedError = stream[kState].storedError;
|
|
455
|
+
for (const req of stream[kState].writeRequests) {
|
|
456
|
+
req.reject?.(storedError);
|
|
457
|
+
}
|
|
458
|
+
stream[kState].writeRequests = [];
|
|
459
|
+
|
|
460
|
+
if (stream[kState].pendingAbortRequest.abort.promise === undefined) {
|
|
461
|
+
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const abortRequest = stream[kState].pendingAbortRequest;
|
|
466
|
+
stream[kState].pendingAbortRequest = {
|
|
467
|
+
abort: { promise: undefined, resolve: undefined, reject: undefined },
|
|
468
|
+
reason: undefined,
|
|
469
|
+
wasAlreadyErroring: false,
|
|
470
|
+
};
|
|
471
|
+
if (abortRequest.wasAlreadyErroring) {
|
|
472
|
+
abortRequest.abort.reject?.(storedError);
|
|
473
|
+
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
stream[kState].controller[kAbort](abortRequest.reason).then(
|
|
477
|
+
() => {
|
|
478
|
+
abortRequest.abort.resolve?.();
|
|
479
|
+
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
|
|
480
|
+
},
|
|
481
|
+
(error: unknown) => {
|
|
482
|
+
abortRequest.abort.reject?.(error);
|
|
483
|
+
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
|
|
484
|
+
},
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function writableStreamDealWithRejection(stream: any, error: unknown): void {
|
|
489
|
+
if (stream[kState].state === 'writable') {
|
|
490
|
+
writableStreamStartErroring(stream, error);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
writableStreamFinishErroring(stream);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
export function writableStreamCloseQueuedOrInFlight(stream: any): boolean {
|
|
497
|
+
return stream[kState].closeRequest.promise !== undefined ||
|
|
498
|
+
stream[kState].inFlightCloseRequest.promise !== undefined;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function writableStreamAddWriteRequest(stream: any): Promise<void> {
|
|
502
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
503
|
+
stream[kState].writeRequests.push({ promise, resolve, reject });
|
|
504
|
+
return promise;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export function writableStreamDefaultWriterWrite(writer: any, chunk: any): Promise<void> {
|
|
508
|
+
const { stream } = writer[kState];
|
|
509
|
+
const { controller } = stream[kState];
|
|
510
|
+
const chunkSize = writableStreamDefaultControllerGetChunkSize(controller, chunk);
|
|
511
|
+
if (stream !== writer[kState].stream) {
|
|
512
|
+
return Promise.reject(new TypeError('Mismatched WritableStreams'));
|
|
513
|
+
}
|
|
514
|
+
const { state } = stream[kState];
|
|
515
|
+
if (state === 'errored') return Promise.reject(stream[kState].storedError);
|
|
516
|
+
if (writableStreamCloseQueuedOrInFlight(stream) || state === 'closed') {
|
|
517
|
+
return Promise.reject(new TypeError('WritableStream is closed'));
|
|
518
|
+
}
|
|
519
|
+
if (state === 'erroring') return Promise.reject(stream[kState].storedError);
|
|
520
|
+
|
|
521
|
+
const promise = writableStreamAddWriteRequest(stream);
|
|
522
|
+
writableStreamDefaultControllerWrite(controller, chunk, chunkSize);
|
|
523
|
+
return promise;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function writableStreamDefaultWriterRelease(writer: any): void {
|
|
527
|
+
const { stream } = writer[kState];
|
|
528
|
+
const releasedError = new TypeError('Writer has been released');
|
|
529
|
+
writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError);
|
|
530
|
+
writableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError);
|
|
531
|
+
stream[kState].writer = undefined;
|
|
532
|
+
writer[kState].stream = undefined;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function writableStreamDefaultWriterGetDesiredSize(writer: any): number | null {
|
|
536
|
+
const { stream } = writer[kState];
|
|
537
|
+
switch (stream[kState].state) {
|
|
538
|
+
case 'errored':
|
|
539
|
+
case 'erroring':
|
|
540
|
+
return null;
|
|
541
|
+
case 'closed':
|
|
542
|
+
return 0;
|
|
543
|
+
}
|
|
544
|
+
return writableStreamDefaultControllerGetDesiredSize(stream[kState].controller);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function writableStreamDefaultWriterEnsureReadyPromiseRejected(writer: any, error: unknown): void {
|
|
548
|
+
if (writer[kState].ready.reject) {
|
|
549
|
+
writer[kState].ready.reject(error);
|
|
550
|
+
writer[kState].ready.resolve = undefined;
|
|
551
|
+
writer[kState].ready.reject = undefined;
|
|
552
|
+
} else {
|
|
553
|
+
writer[kState].ready = {
|
|
554
|
+
promise: Promise.reject(error),
|
|
555
|
+
resolve: undefined,
|
|
556
|
+
reject: undefined,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
setPromiseHandled(writer[kState].ready.promise);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function writableStreamDefaultWriterEnsureClosedPromiseRejected(writer: any, error: unknown): void {
|
|
563
|
+
if (writer[kState].close.reject) {
|
|
564
|
+
writer[kState].close.reject(error);
|
|
565
|
+
writer[kState].close.resolve = undefined;
|
|
566
|
+
writer[kState].close.reject = undefined;
|
|
567
|
+
} else {
|
|
568
|
+
writer[kState].close = {
|
|
569
|
+
promise: Promise.reject(error),
|
|
570
|
+
resolve: undefined,
|
|
571
|
+
reject: undefined,
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
setPromiseHandled(writer[kState].close.promise);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
export function writableStreamDefaultWriterCloseWithErrorPropagation(writer: any): Promise<void> {
|
|
578
|
+
const { stream } = writer[kState];
|
|
579
|
+
const { state } = stream[kState];
|
|
580
|
+
if (writableStreamCloseQueuedOrInFlight(stream) || state === 'closed') {
|
|
581
|
+
return Promise.resolve();
|
|
582
|
+
}
|
|
583
|
+
if (state === 'errored') return Promise.reject(stream[kState].storedError);
|
|
584
|
+
return writableStreamDefaultWriterClose(writer);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function writableStreamDefaultWriterClose(writer: any): Promise<void> {
|
|
588
|
+
return writableStreamClose(writer[kState].stream);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function writableStreamDefaultWriterAbort(writer: any, reason: unknown): Promise<void> {
|
|
592
|
+
return writableStreamAbort(writer[kState].stream, reason);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// ---- Controller internals ----
|
|
596
|
+
|
|
597
|
+
function writableStreamDefaultControllerWrite(controller: any, chunk: any, chunkSize: number): void {
|
|
598
|
+
try {
|
|
599
|
+
enqueueValueWithSize(controller, chunk, chunkSize);
|
|
600
|
+
} catch (error) {
|
|
601
|
+
writableStreamDefaultControllerErrorIfNeeded(controller, error);
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
604
|
+
const { stream } = controller[kState];
|
|
605
|
+
if (!writableStreamCloseQueuedOrInFlight(stream) && stream[kState].state === 'writable') {
|
|
606
|
+
writableStreamUpdateBackpressure(stream, writableStreamDefaultControllerGetBackpressure(controller));
|
|
607
|
+
}
|
|
608
|
+
writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function writableStreamDefaultControllerProcessWrite(controller: any, chunk: any): void {
|
|
612
|
+
const { stream, writeAlgorithm } = controller[kState];
|
|
613
|
+
writableStreamMarkFirstWriteRequestInFlight(stream);
|
|
614
|
+
|
|
615
|
+
writeAlgorithm(chunk, controller).then(
|
|
616
|
+
() => {
|
|
617
|
+
writableStreamFinishInFlightWrite(stream);
|
|
618
|
+
const { state } = stream[kState];
|
|
619
|
+
dequeueValue(controller);
|
|
620
|
+
if (!writableStreamCloseQueuedOrInFlight(stream) && state === 'writable') {
|
|
621
|
+
writableStreamUpdateBackpressure(stream, writableStreamDefaultControllerGetBackpressure(controller));
|
|
622
|
+
}
|
|
623
|
+
writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
|
|
624
|
+
},
|
|
625
|
+
(error: unknown) => {
|
|
626
|
+
if (stream[kState].state === 'writable') {
|
|
627
|
+
writableStreamDefaultControllerClearAlgorithms(controller);
|
|
628
|
+
}
|
|
629
|
+
writableStreamFinishInFlightWriteWithError(stream, error);
|
|
630
|
+
},
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function writableStreamDefaultControllerProcessClose(controller: any): void {
|
|
635
|
+
const { closeAlgorithm, queue, stream } = controller[kState];
|
|
636
|
+
writableStreamMarkCloseRequestInFlight(stream);
|
|
637
|
+
dequeueValue(controller);
|
|
638
|
+
const sinkClosePromise = closeAlgorithm();
|
|
639
|
+
writableStreamDefaultControllerClearAlgorithms(controller);
|
|
640
|
+
sinkClosePromise.then(
|
|
641
|
+
() => writableStreamFinishInFlightClose(stream),
|
|
642
|
+
(error: unknown) => writableStreamFinishInFlightCloseWithError(stream, error),
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function writableStreamDefaultControllerGetDesiredSize(controller: any): number {
|
|
647
|
+
return controller[kState].highWaterMark - controller[kState].queueTotalSize;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function writableStreamDefaultControllerGetChunkSize(controller: any, chunk: any): number {
|
|
651
|
+
const { stream, sizeAlgorithm } = controller[kState];
|
|
652
|
+
if (sizeAlgorithm === undefined) return 1;
|
|
653
|
+
try {
|
|
654
|
+
return sizeAlgorithm(chunk);
|
|
655
|
+
} catch (error) {
|
|
656
|
+
writableStreamDefaultControllerErrorIfNeeded(controller, error);
|
|
657
|
+
return 1;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
export function writableStreamDefaultControllerErrorIfNeeded(controller: any, error: unknown): void {
|
|
662
|
+
if (controller[kState].stream[kState].state === 'writable') {
|
|
663
|
+
writableStreamDefaultControllerError(controller, error);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function writableStreamDefaultControllerError(controller: any, error: unknown): void {
|
|
668
|
+
writableStreamDefaultControllerClearAlgorithms(controller);
|
|
669
|
+
writableStreamStartErroring(controller[kState].stream, error);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function writableStreamDefaultControllerClose(controller: any): void {
|
|
673
|
+
enqueueValueWithSize(controller, kCloseSentinel, 0);
|
|
674
|
+
writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function writableStreamDefaultControllerClearAlgorithms(controller: any): void {
|
|
678
|
+
controller[kState].writeAlgorithm = undefined;
|
|
679
|
+
controller[kState].closeAlgorithm = undefined;
|
|
680
|
+
controller[kState].abortAlgorithm = undefined;
|
|
681
|
+
controller[kState].sizeAlgorithm = undefined;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function writableStreamDefaultControllerGetBackpressure(controller: any): boolean {
|
|
685
|
+
return writableStreamDefaultControllerGetDesiredSize(controller) <= 0;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function writableStreamDefaultControllerAdvanceQueueIfNeeded(controller: any): void {
|
|
689
|
+
const { queue, started, stream } = controller[kState];
|
|
690
|
+
if (!started || stream[kState].inFlightWriteRequest.promise !== undefined) return;
|
|
691
|
+
if (stream[kState].state === 'erroring') {
|
|
692
|
+
writableStreamFinishErroring(stream);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
if (!queue.length) return;
|
|
696
|
+
const value = peekQueueValue(controller);
|
|
697
|
+
if (value === kCloseSentinel) {
|
|
698
|
+
writableStreamDefaultControllerProcessClose(controller);
|
|
699
|
+
} else {
|
|
700
|
+
writableStreamDefaultControllerProcessWrite(controller, value);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// ---- Setup functions ----
|
|
705
|
+
|
|
706
|
+
function setupWritableStreamDefaultControllerFromSink(
|
|
707
|
+
stream: any, sink: any, highWaterMark: number, sizeAlgorithm: (chunk: any) => number,
|
|
708
|
+
): void {
|
|
709
|
+
const controller = new WritableStreamDefaultController(kSkipThrow);
|
|
710
|
+
const start = sink?.start;
|
|
711
|
+
const write = sink?.write;
|
|
712
|
+
const close = sink?.close;
|
|
713
|
+
const abort = sink?.abort;
|
|
714
|
+
const startAlgorithm = start ? start.bind(sink, controller) : nonOpStart;
|
|
715
|
+
const writeAlgorithm = write ? createPromiseCallback('sink.write', write, sink) : nonOpWrite;
|
|
716
|
+
const closeAlgorithm = close ? createPromiseCallback('sink.close', close, sink) : nonOpCancel;
|
|
717
|
+
const abortAlgorithm = abort ? createPromiseCallback('sink.abort', abort, sink) : nonOpCancel;
|
|
718
|
+
setupWritableStreamDefaultController(
|
|
719
|
+
stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm,
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function setupWritableStreamDefaultController(
|
|
724
|
+
stream: any, controller: any,
|
|
725
|
+
startAlgorithm: Function, writeAlgorithm: Function,
|
|
726
|
+
closeAlgorithm: Function, abortAlgorithm: Function,
|
|
727
|
+
highWaterMark: number, sizeAlgorithm: (chunk: any) => number,
|
|
728
|
+
): void {
|
|
729
|
+
controller[kState] = {
|
|
730
|
+
abortAlgorithm,
|
|
731
|
+
closeAlgorithm,
|
|
732
|
+
highWaterMark,
|
|
733
|
+
queue: [] as { value: unknown; size: number }[],
|
|
734
|
+
queueTotalSize: 0,
|
|
735
|
+
abortController: new _AbortController(),
|
|
736
|
+
sizeAlgorithm,
|
|
737
|
+
started: false,
|
|
738
|
+
stream,
|
|
739
|
+
writeAlgorithm,
|
|
740
|
+
};
|
|
741
|
+
stream[kState].controller = controller;
|
|
742
|
+
|
|
743
|
+
writableStreamUpdateBackpressure(stream, writableStreamDefaultControllerGetBackpressure(controller));
|
|
744
|
+
|
|
745
|
+
const startResult = startAlgorithm();
|
|
746
|
+
new Promise((r) => r(startResult)).then(
|
|
747
|
+
() => {
|
|
748
|
+
controller[kState].started = true;
|
|
749
|
+
writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
|
|
750
|
+
},
|
|
751
|
+
(error) => {
|
|
752
|
+
controller[kState].started = true;
|
|
753
|
+
writableStreamDealWithRejection(stream, error);
|
|
754
|
+
},
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// ---- Internal factory (used by TransformStream) ----
|
|
759
|
+
|
|
760
|
+
export function createWritableStream(
|
|
761
|
+
start: Function, write: Function, close: Function, abort: Function,
|
|
762
|
+
highWaterMark = 1, size: (chunk: any) => number = () => 1,
|
|
763
|
+
): WritableStream {
|
|
764
|
+
const stream = Object.create(WritableStream.prototype);
|
|
765
|
+
stream[kType] = 'WritableStream';
|
|
766
|
+
stream[kState] = createWritableStreamState();
|
|
767
|
+
|
|
768
|
+
const controller = new WritableStreamDefaultController(kSkipThrow);
|
|
769
|
+
setupWritableStreamDefaultController(
|
|
770
|
+
stream, controller, start, write, close, abort, highWaterMark, size,
|
|
771
|
+
);
|
|
772
|
+
return stream;
|
|
773
|
+
}
|