nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
1,362 lines (1,228 loc) • 38.8 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/webstreams/writablestream.js
import { codes as __codes__ } from "nstdlib/lib/internal/errors";
import { DOMException } from "nstdlib/stub/binding/messaging";
import {
createDeferredPromise,
customInspectSymbol as kInspect,
kEmptyObject,
kEnumerableProperty,
SideEffectFreeRegExpPrototypeSymbolReplace,
} from "nstdlib/lib/internal/util";
import {
validateObject,
kValidateObjectAllowObjects,
kValidateObjectAllowObjectsAndNull,
} from "nstdlib/lib/internal/validators";
import { MessageChannel } from "nstdlib/lib/internal/worker/io";
import {
kDeserialize,
kTransfer,
kTransferList,
markTransferMode,
} from "nstdlib/lib/internal/worker/js_transferable";
import {
createPromiseCallback,
customInspect,
dequeueValue,
enqueueValueWithSize,
extractHighWaterMark,
extractSizeAlgorithm,
lazyTransfer,
isBrandCheck,
isPromisePending,
peekQueueValue,
resetQueue,
setPromiseHandled,
nonOpCancel,
nonOpStart,
nonOpWrite,
kType,
kState,
} from "nstdlib/lib/internal/webstreams/util";
import {
kIsClosedPromise,
kControllerErrorFunction,
} from "nstdlib/lib/internal/streams/utils";
import { AbortController } from "nstdlib/lib/internal/abort_controller";
import * as assert from "nstdlib/lib/internal/assert";
const {
ERR_ILLEGAL_CONSTRUCTOR,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_STATE,
ERR_INVALID_THIS,
} = __codes__;
const kAbort = Symbol("kAbort");
const kCloseSentinel = Symbol("kCloseSentinel");
const kError = Symbol("kError");
const kSkipThrow = Symbol("kSkipThrow");
let releasedError;
function lazyWritableReleasedError() {
if (releasedError) {
return releasedError;
}
const userModuleRegExp =
/^ {4}at (?:[^/\\(]+ \()(?!node:(.+):\d+:\d+\)$).*/gm;
releasedError = new ERR_INVALID_STATE.TypeError("Writer has been released");
// Avoid V8 leak and remove userland stackstrace
releasedError.stack = SideEffectFreeRegExpPrototypeSymbolReplace(
userModuleRegExp,
releasedError.stack,
"",
);
return releasedError;
}
const getNonWritablePropertyDescriptor = (value) => {
return {
__proto__: null,
configurable: true,
value,
};
};
/**
* @typedef {import('../abort_controller').AbortSignal} AbortSignal
* @typedef {import('./queuingstrategies').QueuingStrategy
* } QueuingStrategy
* @typedef {import('./queuingstrategies').QueuingStrategySize
* } QueuingStrategySize
*/
/**
* @callback UnderlyingSinkStartCallback
* @param {WritableStreamDefaultController} controller
*/
/**
* @callback UnderlyingSinkWriteCallback
* @param {any} chunk
* @param {WritableStreamDefaultController} controller
* @returns {Promise<void>}
*/
/**
* @callback UnderlyingSinkCloseCallback
* @returns {Promise<void>}
*/
/**
* @callback UnderlyingSinkAbortCallback
* @param {any} reason
* @returns {Promise<void>}
*/
/**
* @typedef {{
* start? : UnderlyingSinkStartCallback,
* write? : UnderlyingSinkWriteCallback,
* close? : UnderlyingSinkCloseCallback,
* abort? : UnderlyingSinkAbortCallback,
* type? : any,
* }} UnderlyingSink
*/
class WritableStream {
[kType] = "WritableStream";
/**
* @param {UnderlyingSink} [sink]
* @param {QueuingStrategy} [strategy]
*/
constructor(sink = kEmptyObject, strategy = kEmptyObject) {
markTransferMode(this, false, true);
validateObject(sink, "sink", kValidateObjectAllowObjects);
validateObject(strategy, "strategy", kValidateObjectAllowObjectsAndNull);
const type = sink?.type;
if (type !== undefined)
throw new ERR_INVALID_ARG_VALUE.RangeError("type", type);
this[kState] = createWritableStreamState();
this[kIsClosedPromise] = createDeferredPromise();
this[kControllerErrorFunction] = () => {};
const size = extractSizeAlgorithm(strategy?.size);
const highWaterMark = extractHighWaterMark(strategy?.highWaterMark, 1);
setupWritableStreamDefaultControllerFromSink(
this,
sink,
highWaterMark,
size,
);
}
/**
* @readonly
* @type {boolean}
*/
get locked() {
if (!isWritableStream(this)) throw new ERR_INVALID_THIS("WritableStream");
return isWritableStreamLocked(this);
}
/**
* @param {any} [reason]
* @returns {Promise<void>}
*/
abort(reason = undefined) {
if (!isWritableStream(this))
return Promise.reject(new ERR_INVALID_THIS("WritableStream"));
if (isWritableStreamLocked(this)) {
return Promise.reject(
new ERR_INVALID_STATE.TypeError("WritableStream is locked"),
);
}
return writableStreamAbort(this, reason);
}
/**
* @returns {Promise<void>}
*/
close() {
if (!isWritableStream(this))
return Promise.reject(new ERR_INVALID_THIS("WritableStream"));
if (isWritableStreamLocked(this)) {
return Promise.reject(
new ERR_INVALID_STATE.TypeError("WritableStream is locked"),
);
}
if (writableStreamCloseQueuedOrInFlight(this)) {
return Promise.reject(
new ERR_INVALID_STATE.TypeError("Failure closing WritableStream"),
);
}
return writableStreamClose(this);
}
/**
* @returns {WritableStreamDefaultWriter}
*/
getWriter() {
if (!isWritableStream(this)) throw new ERR_INVALID_THIS("WritableStream");
// eslint-disable-next-line no-use-before-define
return new WritableStreamDefaultWriter(this);
}
[kInspect](depth, options) {
return customInspect(depth, options, this[kType], {
locked: this.locked,
state: this[kState].state,
});
}
[kTransfer]() {
if (!isWritableStream(this)) throw new ERR_INVALID_THIS("WritableStream");
if (this.locked) {
this[kState].transfer.port1?.close();
this[kState].transfer.port1 = undefined;
this[kState].transfer.port2 = undefined;
throw new DOMException(
"Cannot transfer a locked WritableStream",
"DataCloneError",
);
}
const { readable, promise } = lazyTransfer().newCrossRealmReadableStream(
this,
this[kState].transfer.port1,
);
this[kState].transfer.readable = readable;
this[kState].transfer.promise = promise;
return {
data: { port: this[kState].transfer.port2 },
deserializeInfo:
"internal/webstreams/writablestream:TransferredWritableStream",
};
}
[kTransferList]() {
const { port1, port2 } = new MessageChannel();
this[kState].transfer.port1 = port1;
this[kState].transfer.port2 = port2;
return [port2];
}
[kDeserialize]({ port }) {
const transfer = lazyTransfer();
setupWritableStreamDefaultControllerFromSink(
this,
// The MessagePort is set to be referenced when reading.
// After two MessagePorts are closed, there is a problem with
// lingering promise not being properly resolved.
// https://github.com/nodejs/node/issues/51486
new transfer.CrossRealmTransformWritableSink(port, true),
1,
() => 1,
);
}
}
Object.defineProperties(WritableStream.prototype, {
locked: kEnumerableProperty,
abort: kEnumerableProperty,
close: kEnumerableProperty,
getWriter: kEnumerableProperty,
[Symbol.toStringTag]: getNonWritablePropertyDescriptor(WritableStream.name),
});
function InternalTransferredWritableStream() {
markTransferMode(this, false, true);
this[kType] = "WritableStream";
this[kState] = createWritableStreamState();
this[kIsClosedPromise] = createDeferredPromise();
}
Object.setPrototypeOf(
InternalTransferredWritableStream.prototype,
WritableStream.prototype,
);
Object.setPrototypeOf(InternalTransferredWritableStream, WritableStream);
function TransferredWritableStream() {
const stream = new InternalTransferredWritableStream();
stream.constructor = WritableStream;
return stream;
}
TransferredWritableStream.prototype[kDeserialize] = () => {};
class WritableStreamDefaultWriter {
[kType] = "WritableStreamDefaultWriter";
/**
* @param {WritableStream} stream
*/
constructor(stream) {
if (!isWritableStream(stream))
throw new ERR_INVALID_ARG_TYPE("stream", "WritableStream", stream);
this[kState] = {
stream: undefined,
close: {
promise: undefined,
resolve: undefined,
reject: undefined,
},
ready: {
promise: undefined,
resolve: undefined,
reject: undefined,
},
};
setupWritableStreamDefaultWriter(this, stream);
}
/**
* @readonly
* @type {Promise<void>}
*/
get closed() {
if (!isWritableStreamDefaultWriter(this))
return Promise.reject(
new ERR_INVALID_THIS("WritableStreamDefaultWriter"),
);
return this[kState].close.promise;
}
/**
* @readonly
* @type {number}
*/
get desiredSize() {
if (!isWritableStreamDefaultWriter(this))
throw new ERR_INVALID_THIS("WritableStreamDefaultWriter");
if (this[kState].stream === undefined) {
throw new ERR_INVALID_STATE.TypeError(
"Writer is not bound to a WritableStream",
);
}
return writableStreamDefaultWriterGetDesiredSize(this);
}
/**
* @readonly
* @type {Promise<void>}
*/
get ready() {
if (!isWritableStreamDefaultWriter(this))
return Promise.reject(
new ERR_INVALID_THIS("WritableStreamDefaultWriter"),
);
return this[kState].ready.promise;
}
/**
* @param {any} [reason]
* @returns {Promise<void>}
*/
abort(reason = undefined) {
if (!isWritableStreamDefaultWriter(this))
return Promise.reject(
new ERR_INVALID_THIS("WritableStreamDefaultWriter"),
);
if (this[kState].stream === undefined) {
return Promise.reject(
new ERR_INVALID_STATE.TypeError(
"Writer is not bound to a WritableStream",
),
);
}
return writableStreamDefaultWriterAbort(this, reason);
}
/**
* @returns {Promise<void>}
*/
close() {
if (!isWritableStreamDefaultWriter(this))
return Promise.reject(
new ERR_INVALID_THIS("WritableStreamDefaultWriter"),
);
const { stream } = this[kState];
if (stream === undefined) {
return Promise.reject(
new ERR_INVALID_STATE.TypeError(
"Writer is not bound to a WritableStream",
),
);
}
if (writableStreamCloseQueuedOrInFlight(stream)) {
return Promise.reject(
new ERR_INVALID_STATE.TypeError("Failure to close WritableStream"),
);
}
return writableStreamDefaultWriterClose(this);
}
releaseLock() {
if (!isWritableStreamDefaultWriter(this))
throw new ERR_INVALID_THIS("WritableStreamDefaultWriter");
const { stream } = this[kState];
if (stream === undefined) return;
assert(stream[kState].writer !== undefined);
writableStreamDefaultWriterRelease(this);
}
/**
* @param {any} [chunk]
* @returns {Promise<void>}
*/
write(chunk = undefined) {
if (!isWritableStreamDefaultWriter(this))
return Promise.reject(
new ERR_INVALID_THIS("WritableStreamDefaultWriter"),
);
if (this[kState].stream === undefined) {
return Promise.reject(
new ERR_INVALID_STATE.TypeError(
"Writer is not bound to a WritableStream",
),
);
}
return writableStreamDefaultWriterWrite(this, chunk);
}
[kInspect](depth, options) {
return customInspect(depth, options, this[kType], {
stream: this[kState].stream,
close: this[kState].close.promise,
ready: this[kState].ready.promise,
desiredSize: this.desiredSize,
});
}
}
Object.defineProperties(WritableStreamDefaultWriter.prototype, {
closed: kEnumerableProperty,
ready: kEnumerableProperty,
desiredSize: kEnumerableProperty,
abort: kEnumerableProperty,
close: kEnumerableProperty,
releaseLock: kEnumerableProperty,
write: kEnumerableProperty,
[Symbol.toStringTag]: getNonWritablePropertyDescriptor(
WritableStreamDefaultWriter.name,
),
});
class WritableStreamDefaultController {
[kType] = "WritableStreamDefaultController";
constructor(skipThrowSymbol = undefined) {
if (skipThrowSymbol !== kSkipThrow) {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
}
[kAbort](reason) {
const result = this[kState].abortAlgorithm(reason);
writableStreamDefaultControllerClearAlgorithms(this);
return result;
}
[kError]() {
resetQueue(this);
}
/**
* @type {AbortSignal}
*/
get signal() {
if (!isWritableStreamDefaultController(this))
throw new ERR_INVALID_THIS("WritableStreamDefaultController");
return this[kState].abortController.signal;
}
/**
* @param {any} [error]
*/
error(error = undefined) {
if (!isWritableStreamDefaultController(this))
throw new ERR_INVALID_THIS("WritableStreamDefaultController");
if (this[kState].stream[kState].state !== "writable") return;
writableStreamDefaultControllerError(this, error);
}
[kInspect](depth, options) {
return customInspect(depth, options, this[kType], {
stream: this[kState].stream,
});
}
}
Object.defineProperties(WritableStreamDefaultController.prototype, {
signal: kEnumerableProperty,
error: kEnumerableProperty,
[Symbol.toStringTag]: getNonWritablePropertyDescriptor(
WritableStreamDefaultController.name,
),
});
function InternalWritableStream(
start,
write,
close,
abort,
highWaterMark,
size,
) {
markTransferMode(this, false, true);
this[kType] = "WritableStream";
this[kState] = createWritableStreamState();
this[kIsClosedPromise] = createDeferredPromise();
const controller = new WritableStreamDefaultController(kSkipThrow);
setupWritableStreamDefaultController(
this,
controller,
start,
write,
close,
abort,
highWaterMark,
size,
);
}
Object.setPrototypeOf(
InternalWritableStream.prototype,
WritableStream.prototype,
);
Object.setPrototypeOf(InternalWritableStream, WritableStream);
function createWritableStream(
start,
write,
close,
abort,
highWaterMark = 1,
size = () => 1,
) {
const stream = new InternalWritableStream(
start,
write,
close,
abort,
highWaterMark,
size,
);
// For spec compliance the InternalWritableStream must be a WritableStream
stream.constructor = WritableStream;
return stream;
}
const isWritableStream = isBrandCheck("WritableStream");
const isWritableStreamDefaultWriter = isBrandCheck(
"WritableStreamDefaultWriter",
);
const isWritableStreamDefaultController = isBrandCheck(
"WritableStreamDefaultController",
);
function createWritableStreamState() {
return {
__proto__: null,
close: createDeferredPromise(),
closeRequest: {
__proto__: null,
promise: undefined,
resolve: undefined,
reject: undefined,
},
inFlightWriteRequest: {
__proto__: null,
promise: undefined,
resolve: undefined,
reject: undefined,
},
inFlightCloseRequest: {
__proto__: null,
promise: undefined,
resolve: undefined,
reject: undefined,
},
pendingAbortRequest: {
__proto__: null,
abort: {
__proto__: null,
promise: undefined,
resolve: undefined,
reject: undefined,
},
reason: undefined,
wasAlreadyErroring: false,
},
backpressure: false,
controller: undefined,
state: "writable",
storedError: undefined,
writeRequests: [],
writer: undefined,
transfer: {
__proto__: null,
readable: undefined,
port1: undefined,
port2: undefined,
promise: undefined,
},
};
}
function isWritableStreamLocked(stream) {
return stream[kState].writer !== undefined;
}
function setupWritableStreamDefaultWriter(writer, stream) {
if (isWritableStreamLocked(stream))
throw new ERR_INVALID_STATE.TypeError("WritableStream is locked");
writer[kState].stream = stream;
stream[kState].writer = writer;
switch (stream[kState].state) {
case "writable":
if (
!writableStreamCloseQueuedOrInFlight(stream) &&
stream[kState].backpressure
) {
writer[kState].ready = createDeferredPromise();
} else {
writer[kState].ready = {
promise: Promise.resolve(),
resolve: undefined,
reject: undefined,
};
}
setClosedPromiseToNewPromise();
break;
case "erroring":
writer[kState].ready = {
promise: Promise.reject(stream[kState].storedError),
resolve: undefined,
reject: undefined,
};
setPromiseHandled(writer[kState].ready.promise);
setClosedPromiseToNewPromise();
break;
case "closed":
writer[kState].ready = {
promise: Promise.resolve(),
resolve: undefined,
reject: undefined,
};
writer[kState].close = {
promise: Promise.resolve(),
resolve: undefined,
reject: undefined,
};
break;
default:
writer[kState].ready = {
promise: Promise.reject(stream[kState].storedError),
resolve: undefined,
reject: undefined,
};
writer[kState].close = {
promise: Promise.reject(stream[kState].storedError),
resolve: undefined,
reject: undefined,
};
setPromiseHandled(writer[kState].ready.promise);
setPromiseHandled(writer[kState].close.promise);
}
function setClosedPromiseToNewPromise() {
writer[kState].close = createDeferredPromise();
}
}
function writableStreamAbort(stream, reason) {
const { state, controller } = stream[kState];
if (state === "closed" || state === "errored") return Promise.resolve();
controller[kState].abortController.abort(reason);
if (stream[kState].pendingAbortRequest.abort.promise !== undefined)
return stream[kState].pendingAbortRequest.abort.promise;
assert(state === "writable" || state === "erroring");
let wasAlreadyErroring = false;
if (state === "erroring") {
wasAlreadyErroring = true;
reason = undefined;
}
const abort = createDeferredPromise();
stream[kState].pendingAbortRequest = {
abort,
reason,
wasAlreadyErroring,
};
if (!wasAlreadyErroring) writableStreamStartErroring(stream, reason);
return abort.promise;
}
function writableStreamClose(stream) {
const { state, writer, backpressure, controller } = stream[kState];
if (state === "closed" || state === "errored") {
return Promise.reject(
new ERR_INVALID_STATE.TypeError("WritableStream is closed"),
);
}
assert(state === "writable" || state === "erroring");
assert(!writableStreamCloseQueuedOrInFlight(stream));
stream[kState].closeRequest = createDeferredPromise();
const { promise } = stream[kState].closeRequest;
if (writer !== undefined && backpressure && state === "writable")
writer[kState].ready.resolve?.();
writableStreamDefaultControllerClose(controller);
return promise;
}
function writableStreamUpdateBackpressure(stream, backpressure) {
assert(stream[kState].state === "writable");
assert(!writableStreamCloseQueuedOrInFlight(stream));
const { writer } = stream[kState];
if (writer !== undefined && stream[kState].backpressure !== backpressure) {
if (backpressure) {
writer[kState].ready = createDeferredPromise();
} else {
writer[kState].ready.resolve?.();
}
}
stream[kState].backpressure = backpressure;
}
function writableStreamStartErroring(stream, reason) {
assert(stream[kState].storedError === undefined);
assert(stream[kState].state === "writable");
const { controller, writer } = stream[kState];
assert(controller !== undefined);
stream[kState].state = "erroring";
stream[kState].storedError = reason;
if (writer !== undefined) {
writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason);
}
if (
!writableStreamHasOperationMarkedInFlight(stream) &&
controller[kState].started
) {
writableStreamFinishErroring(stream);
}
}
function writableStreamRejectCloseAndClosedPromiseIfNeeded(stream) {
assert(stream[kState].state === "errored");
if (stream[kState].closeRequest.promise !== undefined) {
assert(stream[kState].inFlightCloseRequest.promise === undefined);
stream[kState].closeRequest.reject?.(stream[kState].storedError);
stream[kState].closeRequest = {
promise: undefined,
reject: undefined,
resolve: undefined,
};
}
setPromiseHandled(stream[kIsClosedPromise].promise);
stream[kIsClosedPromise].reject(stream[kState]?.storedError);
const { writer } = stream[kState];
if (writer !== undefined) {
setPromiseHandled(writer[kState].close.promise);
writer[kState].close.reject?.(stream[kState].storedError);
}
}
function writableStreamMarkFirstWriteRequestInFlight(stream) {
assert(stream[kState].inFlightWriteRequest.promise === undefined);
assert(stream[kState].writeRequests.length);
const writeRequest = Array.prototype.shift.call(stream[kState].writeRequests);
stream[kState].inFlightWriteRequest = writeRequest;
}
function writableStreamMarkCloseRequestInFlight(stream) {
assert(stream[kState].inFlightWriteRequest.promise === undefined);
assert(stream[kState].closeRequest.promise !== undefined);
stream[kState].inFlightCloseRequest = stream[kState].closeRequest;
stream[kState].closeRequest = {
promise: undefined,
resolve: undefined,
reject: undefined,
};
}
function writableStreamHasOperationMarkedInFlight(stream) {
const { inFlightWriteRequest, inFlightCloseRequest } = stream[kState];
if (
inFlightWriteRequest.promise === undefined &&
inFlightCloseRequest.promise === undefined
) {
return false;
}
return true;
}
function writableStreamFinishInFlightWriteWithError(stream, error) {
assert(stream[kState].inFlightWriteRequest.promise !== undefined);
stream[kState].inFlightWriteRequest.reject?.(error);
stream[kState].inFlightWriteRequest = {
promise: undefined,
resolve: undefined,
reject: undefined,
};
assert(
stream[kState].state === "writable" || stream[kState].state === "erroring",
);
writableStreamDealWithRejection(stream, error);
}
function writableStreamFinishInFlightWrite(stream) {
assert(stream[kState].inFlightWriteRequest.promise !== undefined);
stream[kState].inFlightWriteRequest.resolve?.();
stream[kState].inFlightWriteRequest = {
promise: undefined,
resolve: undefined,
reject: undefined,
};
}
function writableStreamFinishInFlightCloseWithError(stream, error) {
assert(stream[kState].inFlightCloseRequest.promise !== undefined);
stream[kState].inFlightCloseRequest.reject?.(error);
stream[kState].inFlightCloseRequest = {
promise: undefined,
resolve: undefined,
reject: undefined,
};
assert(
stream[kState].state === "writable" || stream[kState].state === "erroring",
);
if (stream[kState].pendingAbortRequest.abort.promise !== undefined) {
stream[kState].pendingAbortRequest.abort.reject?.(error);
stream[kState].pendingAbortRequest = {
abort: {
promise: undefined,
resolve: undefined,
reject: undefined,
},
reason: undefined,
wasAlreadyErroring: false,
};
}
writableStreamDealWithRejection(stream, error);
}
function writableStreamFinishInFlightClose(stream) {
assert(stream[kState].inFlightCloseRequest.promise !== undefined);
stream[kState].inFlightCloseRequest.resolve?.();
stream[kState].inFlightCloseRequest = {
promise: undefined,
resolve: undefined,
reject: undefined,
};
if (stream[kState].state === "erroring") {
stream[kState].storedError = undefined;
if (stream[kState].pendingAbortRequest.abort.promise !== undefined) {
stream[kState].pendingAbortRequest.abort.resolve?.();
stream[kState].pendingAbortRequest = {
abort: {
promise: undefined,
resolve: undefined,
reject: undefined,
},
reason: undefined,
wasAlreadyErroring: false,
};
}
}
stream[kState].state = "closed";
if (stream[kState].writer !== undefined)
stream[kState].writer[kState].close.resolve?.();
stream[kIsClosedPromise].resolve?.();
assert(stream[kState].pendingAbortRequest.abort.promise === undefined);
assert(stream[kState].storedError === undefined);
}
function writableStreamFinishErroring(stream) {
assert(stream[kState].state === "erroring");
assert(!writableStreamHasOperationMarkedInFlight(stream));
stream[kState].state = "errored";
stream[kState].controller[kError]();
const storedError = stream[kState].storedError;
for (let n = 0; n < stream[kState].writeRequests.length; n++)
stream[kState].writeRequests[n].reject?.(storedError);
stream[kState].writeRequests = [];
if (stream[kState].pendingAbortRequest.abort.promise === undefined) {
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
return;
}
const abortRequest = stream[kState].pendingAbortRequest;
stream[kState].pendingAbortRequest = {
abort: {
promise: undefined,
resolve: undefined,
reject: undefined,
},
reason: undefined,
wasAlreadyErroring: false,
};
if (abortRequest.wasAlreadyErroring) {
abortRequest.abort.reject?.(storedError);
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
return;
}
Promise.prototype.then.call(
stream[kState].controller[kAbort](abortRequest.reason),
() => {
abortRequest.abort.resolve?.();
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
},
(error) => {
abortRequest.abort.reject?.(error);
writableStreamRejectCloseAndClosedPromiseIfNeeded(stream);
},
);
}
function writableStreamDealWithRejection(stream, error) {
const { state } = stream[kState];
if (state === "writable") {
writableStreamStartErroring(stream, error);
return;
}
assert(state === "erroring");
writableStreamFinishErroring(stream);
}
function writableStreamCloseQueuedOrInFlight(stream) {
if (
stream[kState].closeRequest.promise === undefined &&
stream[kState].inFlightCloseRequest.promise === undefined
) {
return false;
}
return true;
}
function writableStreamAddWriteRequest(stream) {
assert(isWritableStreamLocked(stream));
assert(stream[kState].state === "writable");
const { promise, resolve, reject } = createDeferredPromise();
Array.prototype.push.call(stream[kState].writeRequests, {
promise,
resolve,
reject,
});
return promise;
}
function writableStreamDefaultWriterWrite(writer, chunk) {
const { stream } = writer[kState];
assert(stream !== undefined);
const { controller } = stream[kState];
const chunkSize = writableStreamDefaultControllerGetChunkSize(
controller,
chunk,
);
if (stream !== writer[kState].stream) {
return Promise.reject(
new ERR_INVALID_STATE.TypeError("Mismatched WritableStreams"),
);
}
const { state } = stream[kState];
if (state === "errored") return Promise.reject(stream[kState].storedError);
if (writableStreamCloseQueuedOrInFlight(stream) || state === "closed") {
return Promise.reject(
new ERR_INVALID_STATE.TypeError("WritableStream is closed"),
);
}
if (state === "erroring") return Promise.reject(stream[kState].storedError);
assert(state === "writable");
const promise = writableStreamAddWriteRequest(stream);
writableStreamDefaultControllerWrite(controller, chunk, chunkSize);
return promise;
}
function writableStreamDefaultWriterRelease(writer) {
const { stream } = writer[kState];
assert(stream !== undefined);
assert(stream[kState].writer === writer);
const releasedStateError = lazyWritableReleasedError();
writableStreamDefaultWriterEnsureReadyPromiseRejected(
writer,
releasedStateError,
);
writableStreamDefaultWriterEnsureClosedPromiseRejected(
writer,
releasedStateError,
);
stream[kState].writer = undefined;
writer[kState].stream = undefined;
}
function writableStreamDefaultWriterGetDesiredSize(writer) {
const { stream } = writer[kState];
switch (stream[kState].state) {
case "errored":
// Fall through
case "erroring":
return null;
case "closed":
return 0;
}
return writableStreamDefaultControllerGetDesiredSize(
stream[kState].controller,
);
}
function writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) {
if (isPromisePending(writer[kState].ready.promise)) {
writer[kState].ready.reject?.(error);
} else {
writer[kState].ready = {
promise: Promise.reject(error),
resolve: undefined,
reject: undefined,
};
}
setPromiseHandled(writer[kState].ready.promise);
}
function writableStreamDefaultWriterEnsureClosedPromiseRejected(writer, error) {
if (isPromisePending(writer[kState].close.promise)) {
writer[kState].close.reject?.(error);
} else {
writer[kState].close = {
promise: Promise.reject(error),
resolve: undefined,
reject: undefined,
};
}
setPromiseHandled(writer[kState].close.promise);
}
function writableStreamDefaultWriterCloseWithErrorPropagation(writer) {
const { stream } = writer[kState];
assert(stream !== undefined);
const { state } = stream[kState];
if (writableStreamCloseQueuedOrInFlight(stream) || state === "closed")
return Promise.resolve();
if (state === "errored") return Promise.reject(stream[kState].storedError);
assert(state === "writable" || state === "erroring");
return writableStreamDefaultWriterClose(writer);
}
function writableStreamDefaultWriterClose(writer) {
const { stream } = writer[kState];
assert(stream !== undefined);
return writableStreamClose(stream);
}
function writableStreamDefaultWriterAbort(writer, reason) {
const { stream } = writer[kState];
assert(stream !== undefined);
return writableStreamAbort(stream, reason);
}
function writableStreamDefaultControllerWrite(controller, chunk, chunkSize) {
try {
enqueueValueWithSize(controller, chunk, chunkSize);
} catch (error) {
writableStreamDefaultControllerErrorIfNeeded(controller, error);
return;
}
const { stream } = controller[kState];
if (
!writableStreamCloseQueuedOrInFlight(stream) &&
stream[kState].state === "writable"
) {
writableStreamUpdateBackpressure(
stream,
writableStreamDefaultControllerGetBackpressure(controller),
);
}
writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
}
function writableStreamDefaultControllerProcessWrite(controller, chunk) {
const { stream, writeAlgorithm } = controller[kState];
writableStreamMarkFirstWriteRequestInFlight(stream);
Promise.prototype.then.call(
writeAlgorithm(chunk, controller),
() => {
writableStreamFinishInFlightWrite(stream);
const { state } = stream[kState];
assert(state === "writable" || state === "erroring");
dequeueValue(controller);
if (
!writableStreamCloseQueuedOrInFlight(stream) &&
state === "writable"
) {
writableStreamUpdateBackpressure(
stream,
writableStreamDefaultControllerGetBackpressure(controller),
);
}
writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
},
(error) => {
if (stream[kState].state === "writable")
writableStreamDefaultControllerClearAlgorithms(controller);
writableStreamFinishInFlightWriteWithError(stream, error);
},
);
}
function writableStreamDefaultControllerProcessClose(controller) {
const { closeAlgorithm, queue, stream } = controller[kState];
writableStreamMarkCloseRequestInFlight(stream);
dequeueValue(controller);
assert(!queue.length);
const sinkClosePromise = closeAlgorithm();
writableStreamDefaultControllerClearAlgorithms(controller);
Promise.prototype.then.call(
sinkClosePromise,
() => writableStreamFinishInFlightClose(stream),
(error) => writableStreamFinishInFlightCloseWithError(stream, error),
);
}
function writableStreamDefaultControllerGetDesiredSize(controller) {
const { highWaterMark, queueTotalSize } = controller[kState];
return highWaterMark - queueTotalSize;
}
function writableStreamDefaultControllerGetChunkSize(controller, chunk) {
try {
return Function.prototype.call.call(
controller[kState].sizeAlgorithm,
undefined,
chunk,
);
} catch (error) {
writableStreamDefaultControllerErrorIfNeeded(controller, error);
return 1;
}
}
function writableStreamDefaultControllerErrorIfNeeded(controller, error) {
const { stream } = controller[kState];
if (stream[kState].state === "writable")
writableStreamDefaultControllerError(controller, error);
}
function writableStreamDefaultControllerError(controller, error) {
const { stream } = controller[kState];
assert(stream[kState].state === "writable");
writableStreamDefaultControllerClearAlgorithms(controller);
writableStreamStartErroring(stream, error);
}
function writableStreamDefaultControllerClose(controller) {
enqueueValueWithSize(controller, kCloseSentinel, 0);
writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
}
function writableStreamDefaultControllerClearAlgorithms(controller) {
controller[kState].writeAlgorithm = undefined;
controller[kState].closeAlgorithm = undefined;
controller[kState].abortAlgorithm = undefined;
controller[kState].sizeAlgorithm = undefined;
}
function writableStreamDefaultControllerGetBackpressure(controller) {
return writableStreamDefaultControllerGetDesiredSize(controller) <= 0;
}
function writableStreamDefaultControllerAdvanceQueueIfNeeded(controller) {
const { queue, started, stream } = controller[kState];
if (!started || stream[kState].inFlightWriteRequest.promise !== undefined)
return;
if (stream[kState].state === "erroring") {
writableStreamFinishErroring(stream);
return;
}
if (!queue.length) return;
const value = peekQueueValue(controller);
if (value === kCloseSentinel)
writableStreamDefaultControllerProcessClose(controller);
else writableStreamDefaultControllerProcessWrite(controller, value);
}
function setupWritableStreamDefaultControllerFromSink(
stream,
sink,
highWaterMark,
sizeAlgorithm,
) {
const controller = new WritableStreamDefaultController(kSkipThrow);
const start = sink?.start;
const write = sink?.write;
const close = sink?.close;
const abort = sink?.abort;
const startAlgorithm = start
? Function.prototype.bind.call(start, sink, controller)
: nonOpStart;
const writeAlgorithm = write
? createPromiseCallback("sink.write", write, sink)
: nonOpWrite;
const closeAlgorithm = close
? createPromiseCallback("sink.close", close, sink)
: nonOpCancel;
const abortAlgorithm = abort
? createPromiseCallback("sink.abort", abort, sink)
: nonOpCancel;
setupWritableStreamDefaultController(
stream,
controller,
startAlgorithm,
writeAlgorithm,
closeAlgorithm,
abortAlgorithm,
highWaterMark,
sizeAlgorithm,
);
}
function setupWritableStreamDefaultController(
stream,
controller,
startAlgorithm,
writeAlgorithm,
closeAlgorithm,
abortAlgorithm,
highWaterMark,
sizeAlgorithm,
) {
assert(isWritableStream(stream));
assert(stream[kState].controller === undefined);
controller[kState] = {
abortAlgorithm,
closeAlgorithm,
highWaterMark,
queue: [],
queueTotalSize: 0,
abortController: new AbortController(),
sizeAlgorithm,
started: false,
stream,
writeAlgorithm,
};
stream[kState].controller = controller;
stream[kControllerErrorFunction] = Function.prototype.bind.call(
controller.error,
controller,
);
writableStreamUpdateBackpressure(
stream,
writableStreamDefaultControllerGetBackpressure(controller),
);
const startResult = startAlgorithm();
Promise.prototype.then.call(
new Promise((r) => r(startResult)),
() => {
assert(
stream[kState].state === "writable" ||
stream[kState].state === "erroring",
);
controller[kState].started = true;
writableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
},
(error) => {
assert(
stream[kState].state === "writable" ||
stream[kState].state === "erroring",
);
controller[kState].started = true;
writableStreamDealWithRejection(stream, error);
},
);
}
export { WritableStream };
export { WritableStreamDefaultWriter };
export { WritableStreamDefaultController };
export { TransferredWritableStream };
export { isWritableStream };
export { isWritableStreamDefaultController };
export { isWritableStreamDefaultWriter };
export { isWritableStreamLocked };
export { setupWritableStreamDefaultWriter };
export { writableStreamAbort };
export { writableStreamClose };
export { writableStreamUpdateBackpressure };
export { writableStreamStartErroring };
export { writableStreamRejectCloseAndClosedPromiseIfNeeded };
export { writableStreamMarkFirstWriteRequestInFlight };
export { writableStreamMarkCloseRequestInFlight };
export { writableStreamHasOperationMarkedInFlight };
export { writableStreamFinishInFlightWriteWithError };
export { writableStreamFinishInFlightWrite };
export { writableStreamFinishInFlightCloseWithError };
export { writableStreamFinishInFlightClose };
export { writableStreamFinishErroring };
export { writableStreamDealWithRejection };
export { writableStreamCloseQueuedOrInFlight };
export { writableStreamAddWriteRequest };
export { writableStreamDefaultWriterWrite };
export { writableStreamDefaultWriterRelease };
export { writableStreamDefaultWriterGetDesiredSize };
export { writableStreamDefaultWriterEnsureReadyPromiseRejected };
export { writableStreamDefaultWriterEnsureClosedPromiseRejected };
export { writableStreamDefaultWriterCloseWithErrorPropagation };
export { writableStreamDefaultWriterClose };
export { writableStreamDefaultWriterAbort };
export { writableStreamDefaultControllerWrite };
export { writableStreamDefaultControllerProcessWrite };
export { writableStreamDefaultControllerProcessClose };
export { writableStreamDefaultControllerGetDesiredSize };
export { writableStreamDefaultControllerGetChunkSize };
export { writableStreamDefaultControllerErrorIfNeeded };
export { writableStreamDefaultControllerError };
export { writableStreamDefaultControllerClose };
export { writableStreamDefaultControllerClearAlgorithms };
export { writableStreamDefaultControllerGetBackpressure };
export { writableStreamDefaultControllerAdvanceQueueIfNeeded };
export { setupWritableStreamDefaultControllerFromSink };
export { setupWritableStreamDefaultController };
export { createWritableStream };