UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

1,818 lines (1,608 loc) 98.2 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/webstreams/readablestream.js import { AbortError, codes as __codes__ } from "nstdlib/lib/internal/errors"; import { DOMException } from "nstdlib/stub/binding/messaging"; import { isArrayBufferView, isDataView } from "nstdlib/lib/internal/util/types"; import { createDeferredPromise, customInspectSymbol as kInspect, isArrayBufferDetached, kEmptyObject, kEnumerableProperty, SideEffectFreeRegExpPrototypeSymbolReplace, } from "nstdlib/lib/internal/util"; import { validateAbortSignal, validateBuffer, 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 { queueMicrotask } from "nstdlib/lib/internal/process/task_queues"; import { kIsDisturbed, kIsErrored, kIsReadable, kIsClosedPromise, kControllerErrorFunction, } from "nstdlib/lib/internal/streams/utils"; import { structuredClone } from "nstdlib/stub/binding/messaging"; import { ArrayBufferViewGetBuffer, ArrayBufferViewGetByteLength, ArrayBufferViewGetByteOffset, AsyncIterator, cloneAsUint8Array, copyArrayBuffer, createPromiseCallback, customInspect, dequeueValue, enqueueValueWithSize, extractHighWaterMark, extractSizeAlgorithm, lazyTransfer, isViewedArrayBufferDetached, isBrandCheck, resetQueue, setPromiseHandled, transferArrayBuffer, nonOpCancel, nonOpPull, nonOpStart, getIterator, iteratorNext, kType, kState, } from "nstdlib/lib/internal/webstreams/util"; import { WritableStreamDefaultWriter, isWritableStream, isWritableStreamLocked, isWritableStreamDefaultController, isWritableStreamDefaultWriter, writableStreamAbort, writableStreamCloseQueuedOrInFlight, writableStreamDefaultWriterCloseWithErrorPropagation, writableStreamDefaultWriterRelease, writableStreamDefaultWriterWrite, } from "nstdlib/lib/internal/webstreams/writablestream"; import { Buffer } from "nstdlib/lib/buffer"; import * as assert from "nstdlib/lib/internal/assert"; import * as __hoisted_internal_events_abort_listener__ from "nstdlib/lib/internal/events/abort_listener"; const { ERR_ILLEGAL_CONSTRUCTOR, ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_INVALID_STATE, ERR_INVALID_THIS, ERR_OUT_OF_RANGE, } = __codes__; const kCancel = Symbol("kCancel"); const kClose = Symbol("kClose"); const kChunk = Symbol("kChunk"); const kError = Symbol("kError"); const kPull = Symbol("kPull"); const kRelease = Symbol("kRelease"); const kSkipThrow = Symbol("kSkipThrow"); let releasedError; let releasingError; let addAbortListener; const userModuleRegExp = /^ {4}at (?:[^/\\(]+ \()(?!node:(.+):\d+:\d+\)$).*/gm; function lazyReadableReleasedError() { if (releasedError) { return releasedError; } releasedError = new ERR_INVALID_STATE.TypeError("Reader released"); // Avoid V8 leak and remove userland stackstrace releasedError.stack = SideEffectFreeRegExpPrototypeSymbolReplace( userModuleRegExp, releasedError.stack, "", ); return releasedError; } function lazyReadableReleasingError() { if (releasingError) { return releasingError; } releasingError = new ERR_INVALID_STATE.TypeError("Releasing reader"); // Avoid V8 leak and remove userland stackstrace releasingError.stack = SideEffectFreeRegExpPrototypeSymbolReplace( userModuleRegExp, releasingError.stack, "", ); return releasingError; } 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 * @typedef {import('./writablestream').WritableStream} WritableStream */ /** * @typedef {ReadableStreamDefaultController | ReadableByteStreamController * } ReadableStreamController */ /** * @typedef {ReadableStreamDefaultReader | ReadableStreamBYOBReader * } ReadableStreamReader */ /** * @callback UnderlyingSourceStartCallback * @param {ReadableStreamController} controller * @returns { any | Promise<void> } */ /** * @callback UnderlyingSourcePullCallback * @param {ReadableStreamController} controller * @returns { Promise<void> } */ /** * @callback UnderlyingSourceCancelCallback * @param {any} reason * @returns { Promise<void> } */ /** * @typedef {{ * readable: ReadableStream, * writable: WritableStream, * }} ReadableWritablePair */ /** * @typedef {{ * preventClose? : boolean, * preventAbort? : boolean, * preventCancel? : boolean, * signal? : AbortSignal, * }} StreamPipeOptions */ /** * @typedef {{ * start? : UnderlyingSourceStartCallback, * pull? : UnderlyingSourcePullCallback, * cancel? : UnderlyingSourceCancelCallback, * type? : "bytes", * autoAllocateChunkSize? : number * }} UnderlyingSource */ class ReadableStream { [kType] = "ReadableStream"; /** * @param {UnderlyingSource} [source] * @param {QueuingStrategy} [strategy] */ constructor(source = kEmptyObject, strategy = kEmptyObject) { markTransferMode(this, false, true); validateObject(source, "source", kValidateObjectAllowObjects); validateObject(strategy, "strategy", kValidateObjectAllowObjectsAndNull); this[kState] = createReadableStreamState(); this[kIsClosedPromise] = createDeferredPromise(); this[kControllerErrorFunction] = () => {}; // The spec requires handling of the strategy first // here. Specifically, if getting the size and // highWaterMark from the strategy fail, that has // to trigger a throw before getting the details // from the source. So be sure to keep these in // this order. const size = strategy?.size; const highWaterMark = strategy?.highWaterMark; const type = source.type; if (`${type}` === "bytes") { if (size !== undefined) throw new ERR_INVALID_ARG_VALUE.RangeError("strategy.size", size); setupReadableByteStreamControllerFromSource( this, source, extractHighWaterMark(highWaterMark, 0), ); } else { if (type !== undefined) throw new ERR_INVALID_ARG_VALUE("source.type", type); setupReadableStreamDefaultControllerFromSource( this, source, extractHighWaterMark(highWaterMark, 1), extractSizeAlgorithm(size), ); } } get [kIsDisturbed]() { return this[kState].disturbed; } get [kIsErrored]() { return this[kState].state === "errored"; } get [kIsReadable]() { return this[kState].state === "readable"; } /** * @readonly * @type {boolean} */ get locked() { if (!isReadableStream(this)) throw new ERR_INVALID_THIS("ReadableStream"); return isReadableStreamLocked(this); } static from(iterable) { return readableStreamFromIterable(iterable); } /** * @param {any} [reason] * @returns { Promise<void> } */ cancel(reason = undefined) { if (!isReadableStream(this)) return Promise.reject(new ERR_INVALID_THIS("ReadableStream")); if (isReadableStreamLocked(this)) { return Promise.reject( new ERR_INVALID_STATE.TypeError("ReadableStream is locked"), ); } return readableStreamCancel(this, reason); } /** * @param {{ * mode? : "byob" * }} [options] * @returns {ReadableStreamReader} */ getReader(options = kEmptyObject) { if (!isReadableStream(this)) throw new ERR_INVALID_THIS("ReadableStream"); validateObject(options, "options", kValidateObjectAllowObjectsAndNull); const mode = options?.mode; if (mode === undefined) // eslint-disable-next-line no-use-before-define return new ReadableStreamDefaultReader(this); if (`${mode}` !== "byob") throw new ERR_INVALID_ARG_VALUE("options.mode", mode); // eslint-disable-next-line no-use-before-define return new ReadableStreamBYOBReader(this); } /** * @param {ReadableWritablePair} transform * @param {StreamPipeOptions} [options] * @returns {ReadableStream} */ pipeThrough(transform, options = kEmptyObject) { if (!isReadableStream(this)) throw new ERR_INVALID_THIS("ReadableStream"); const readable = transform?.readable; if (!isReadableStream(readable)) { throw new ERR_INVALID_ARG_TYPE( "transform.readable", "ReadableStream", readable, ); } const writable = transform?.writable; if (!isWritableStream(writable)) { throw new ERR_INVALID_ARG_TYPE( "transform.writable", "WritableStream", writable, ); } // The web platform tests require that these be handled one at a // time and in a specific order. options can be null or undefined. validateObject(options, "options", kValidateObjectAllowObjectsAndNull); const preventAbort = options?.preventAbort; const preventCancel = options?.preventCancel; const preventClose = options?.preventClose; const signal = options?.signal; if (signal !== undefined) { validateAbortSignal(signal, "options.signal"); } if (isReadableStreamLocked(this)) throw new ERR_INVALID_STATE.TypeError("The ReadableStream is locked"); if (isWritableStreamLocked(writable)) throw new ERR_INVALID_STATE.TypeError("The WritableStream is locked"); const promise = readableStreamPipeTo( this, writable, !!preventClose, !!preventAbort, !!preventCancel, signal, ); setPromiseHandled(promise); return readable; } /** * @param {WritableStream} destination * @param {StreamPipeOptions} [options] * @returns {Promise<void>} */ pipeTo(destination, options = kEmptyObject) { try { if (!isReadableStream(this)) throw new ERR_INVALID_THIS("ReadableStream"); if (!isWritableStream(destination)) { throw new ERR_INVALID_ARG_TYPE( "transform.writable", "WritableStream", destination, ); } validateObject(options, "options", kValidateObjectAllowObjectsAndNull); const preventAbort = options?.preventAbort; const preventCancel = options?.preventCancel; const preventClose = options?.preventClose; const signal = options?.signal; if (signal !== undefined) { validateAbortSignal(signal, "options.signal"); } if (isReadableStreamLocked(this)) throw new ERR_INVALID_STATE.TypeError("The ReadableStream is locked"); if (isWritableStreamLocked(destination)) throw new ERR_INVALID_STATE.TypeError("The WritableStream is locked"); return readableStreamPipeTo( this, destination, !!preventClose, !!preventAbort, !!preventCancel, signal, ); } catch (error) { return Promise.reject(error); } } /** * @returns {ReadableStream[]} */ tee() { if (!isReadableStream(this)) throw new ERR_INVALID_THIS("ReadableStream"); return readableStreamTee(this, false); } /** * @param {{ * preventCancel? : boolean, * }} [options] * @returns {AsyncIterable} */ values(options = kEmptyObject) { if (!isReadableStream(this)) throw new ERR_INVALID_THIS("ReadableStream"); validateObject(options, "options", kValidateObjectAllowObjectsAndNull); const preventCancel = !!options?.preventCancel; // eslint-disable-next-line no-use-before-define const reader = new ReadableStreamDefaultReader(this); // No __proto__ here to avoid the performance hit. const state = { done: false, current: undefined, }; let started = false; // The nextSteps function is not an async function in order // to make it more efficient. Because nextSteps explicitly // creates a Promise and returns it in the common case, // making it an async function just causes two additional // unnecessary Promise allocations to occur, which just add // cost. function nextSteps() { if (state.done) return Promise.resolve({ done: true, value: undefined }); if (reader[kState].stream === undefined) { return Promise.reject( new ERR_INVALID_STATE.TypeError( "The reader is not bound to a ReadableStream", ), ); } const promise = createDeferredPromise(); // eslint-disable-next-line no-use-before-define readableStreamDefaultReaderRead( reader, new ReadableStreamAsyncIteratorReadRequest(reader, state, promise), ); return promise.promise; } async function returnSteps(value) { if (state.done) return { done: true, value }; // eslint-disable-line node-core/avoid-prototype-pollution state.done = true; if (reader[kState].stream === undefined) { throw new ERR_INVALID_STATE.TypeError( "The reader is not bound to a ReadableStream", ); } assert(!reader[kState].readRequests.length); if (!preventCancel) { const result = readableStreamReaderGenericCancel(reader, value); readableStreamReaderGenericRelease(reader); await result; return { done: true, value }; // eslint-disable-line node-core/avoid-prototype-pollution } readableStreamReaderGenericRelease(reader); return { done: true, value }; // eslint-disable-line node-core/avoid-prototype-pollution } // TODO(@jasnell): Explore whether an async generator // can be used here instead of a custom iterator object. return Object.setPrototypeOf( { // Changing either of these functions (next or return) // to async functions causes a failure in the streams // Web Platform Tests that check for use of a modified // Promise.prototype.then. Since the await keyword // uses Promise.prototype.then, it is open to prototype // pollution, which causes the test to fail. The other // await uses here do not trigger that failure because // the test that fails does not trigger those code paths. next() { // If this is the first read, delay by one microtask // to ensure that the controller has had an opportunity // to properly start and perform the initial pull. // TODO(@jasnell): The spec doesn't call this out so // need to investigate if it's a bug in our impl or // the spec. if (!started) { state.current = Promise.resolve(); started = true; } state.current = state.current !== undefined ? Promise.prototype.then.call(state.current, nextSteps, nextSteps) : nextSteps(); return state.current; }, return(error) { started = true; state.current = state.current !== undefined ? Promise.prototype.then.call( state.current, () => returnSteps(error), () => returnSteps(error), ) : returnSteps(error); return state.current; }, [SymbolAsyncIterator]() { return this; }, }, AsyncIterator, ); } [kInspect](depth, options) { return customInspect(depth, options, this[kType], { locked: this.locked, state: this[kState].state, supportsBYOB: // eslint-disable-next-line no-use-before-define this[kState].controller instanceof ReadableByteStreamController, }); } [kTransfer]() { if (!isReadableStream(this)) throw new ERR_INVALID_THIS("ReadableStream"); 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 ReadableStream", "DataCloneError", ); } const { writable, promise } = lazyTransfer().newCrossRealmWritableSink( this, this[kState].transfer.port1, ); this[kState].transfer.writable = writable; this[kState].transfer.promise = promise; return { data: { port: this[kState].transfer.port2 }, deserializeInfo: "internal/webstreams/readablestream:TransferredReadableStream", }; } [kTransferList]() { const { port1, port2 } = new MessageChannel(); this[kState].transfer.port1 = port1; this[kState].transfer.port2 = port2; return [port2]; } [kDeserialize]({ port }) { const transfer = lazyTransfer(); setupReadableStreamDefaultControllerFromSource( 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.CrossRealmTransformReadableSource(port, true), 0, () => 1, ); } } Object.defineProperties(ReadableStream.prototype, { [SymbolAsyncIterator]: { __proto__: null, configurable: true, enumerable: false, writable: true, value: ReadableStream.prototype.values, }, locked: kEnumerableProperty, cancel: kEnumerableProperty, getReader: kEnumerableProperty, pipeThrough: kEnumerableProperty, pipeTo: kEnumerableProperty, tee: kEnumerableProperty, values: kEnumerableProperty, [Symbol.toStringTag]: getNonWritablePropertyDescriptor(ReadableStream.name), }); Object.defineProperties(ReadableStream, { from: kEnumerableProperty, }); function InternalTransferredReadableStream() { markTransferMode(this, false, true); this[kType] = "ReadableStream"; this[kState] = createReadableStreamState(); this[kIsClosedPromise] = createDeferredPromise(); } Object.setPrototypeOf( InternalTransferredReadableStream.prototype, ReadableStream.prototype, ); Object.setPrototypeOf(InternalTransferredReadableStream, ReadableStream); function TransferredReadableStream() { const stream = new InternalTransferredReadableStream(); stream.constructor = ReadableStream; return stream; } TransferredReadableStream.prototype[kDeserialize] = () => {}; class ReadableStreamBYOBRequest { [kType] = "ReadableStreamBYOBRequest"; constructor(skipThrowSymbol = undefined) { if (skipThrowSymbol !== kSkipThrow) { throw new ERR_ILLEGAL_CONSTRUCTOR(); } } /** * @readonly * @type {ArrayBufferView} */ get view() { if (!isReadableStreamBYOBRequest(this)) throw new ERR_INVALID_THIS("ReadableStreamBYOBRequest"); return this[kState].view; } /** * @param {number} bytesWritten */ respond(bytesWritten) { if (!isReadableStreamBYOBRequest(this)) throw new ERR_INVALID_THIS("ReadableStreamBYOBRequest"); const { view, controller } = this[kState]; if (controller === undefined) { throw new ERR_INVALID_STATE.TypeError( "This BYOB request has been invalidated", ); } const viewByteLength = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "byteLength", ).get(view); const viewBuffer = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "buffer", ).get(view); const viewBufferByteLength = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "byteLength", ).get(viewBuffer); if (isArrayBufferDetached(viewBuffer)) { throw new ERR_INVALID_STATE.TypeError("Viewed ArrayBuffer is detached"); } assert(viewByteLength > 0); assert(viewBufferByteLength > 0); readableByteStreamControllerRespond(controller, bytesWritten); } /** * @param {ArrayBufferView} view */ respondWithNewView(view) { if (!isReadableStreamBYOBRequest(this)) throw new ERR_INVALID_THIS("ReadableStreamBYOBRequest"); const { controller } = this[kState]; if (controller === undefined) { throw new ERR_INVALID_STATE.TypeError( "This BYOB request has been invalidated", ); } validateBuffer(view, "view"); if (isViewedArrayBufferDetached(view)) { throw new ERR_INVALID_STATE.TypeError("Viewed ArrayBuffer is detached"); } readableByteStreamControllerRespondWithNewView(controller, view); } [kInspect](depth, options) { return customInspect(depth, options, this[kType], { view: this.view, controller: this[kState].controller, }); } } Object.defineProperties(ReadableStreamBYOBRequest.prototype, { view: kEnumerableProperty, respond: kEnumerableProperty, respondWithNewView: kEnumerableProperty, [Symbol.toStringTag]: getNonWritablePropertyDescriptor( ReadableStreamBYOBRequest.name, ), }); function createReadableStreamBYOBRequest(controller, view) { const stream = new ReadableStreamBYOBRequest(kSkipThrow); stream[kState] = { controller, view, }; return stream; } class ReadableStreamAsyncIteratorReadRequest { constructor(reader, state, promise) { this.reader = reader; this.state = state; this.promise = promise; } [kChunk](chunk) { this.state.current = undefined; this.promise.resolve({ value: chunk, done: false }); } [kClose]() { this.state.current = undefined; this.state.done = true; readableStreamReaderGenericRelease(this.reader); this.promise.resolve({ done: true, value: undefined }); } [kError](error) { this.state.current = undefined; this.state.done = true; readableStreamReaderGenericRelease(this.reader); this.promise.reject(error); } } class DefaultReadRequest { constructor() { this[kState] = createDeferredPromise(); } [kChunk](value) { this[kState].resolve?.({ value, done: false }); } [kClose]() { this[kState].resolve?.({ value: undefined, done: true }); } [kError](error) { this[kState].reject?.(error); } get promise() { return this[kState].promise; } } class ReadIntoRequest { constructor() { this[kState] = createDeferredPromise(); } [kChunk](value) { this[kState].resolve?.({ value, done: false }); } [kClose](value) { this[kState].resolve?.({ value, done: true }); } [kError](error) { this[kState].reject?.(error); } get promise() { return this[kState].promise; } } class ReadableStreamDefaultReader { [kType] = "ReadableStreamDefaultReader"; /** * @param {ReadableStream} stream */ constructor(stream) { if (!isReadableStream(stream)) throw new ERR_INVALID_ARG_TYPE("stream", "ReadableStream", stream); this[kState] = { readRequests: [], stream: undefined, close: { promise: undefined, resolve: undefined, reject: undefined, }, }; setupReadableStreamDefaultReader(this, stream); } /** * @returns {Promise<{ * value : any, * done : boolean * }>} */ read() { if (!isReadableStreamDefaultReader(this)) return Promise.reject( new ERR_INVALID_THIS("ReadableStreamDefaultReader"), ); if (this[kState].stream === undefined) { return Promise.reject( new ERR_INVALID_STATE.TypeError( "The reader is not attached to a stream", ), ); } const readRequest = new DefaultReadRequest(); readableStreamDefaultReaderRead(this, readRequest); return readRequest.promise; } releaseLock() { if (!isReadableStreamDefaultReader(this)) throw new ERR_INVALID_THIS("ReadableStreamDefaultReader"); if (this[kState].stream === undefined) return; readableStreamDefaultReaderRelease(this); } /** * @readonly * @type {Promise<void>} */ get closed() { if (!isReadableStreamDefaultReader(this)) return Promise.reject( new ERR_INVALID_THIS("ReadableStreamDefaultReader"), ); return this[kState].close.promise; } /** * @param {any} [reason] * @returns {Promise<void>} */ cancel(reason = undefined) { if (!isReadableStreamDefaultReader(this)) return Promise.reject( new ERR_INVALID_THIS("ReadableStreamDefaultReader"), ); if (this[kState].stream === undefined) { return Promise.reject( new ERR_INVALID_STATE.TypeError( "The reader is not attached to a stream", ), ); } return readableStreamReaderGenericCancel(this, reason); } [kInspect](depth, options) { return customInspect(depth, options, this[kType], { stream: this[kState].stream, readRequests: this[kState].readRequests.length, close: this[kState].close.promise, }); } } Object.defineProperties(ReadableStreamDefaultReader.prototype, { closed: kEnumerableProperty, read: kEnumerableProperty, releaseLock: kEnumerableProperty, cancel: kEnumerableProperty, [Symbol.toStringTag]: getNonWritablePropertyDescriptor( ReadableStreamDefaultReader.name, ), }); class ReadableStreamBYOBReader { [kType] = "ReadableStreamBYOBReader"; /** * @param {ReadableStream} stream */ constructor(stream) { if (!isReadableStream(stream)) throw new ERR_INVALID_ARG_TYPE("stream", "ReadableStream", stream); this[kState] = { stream: undefined, requestIntoRequests: [], close: { promise: undefined, resolve: undefined, reject: undefined, }, }; setupReadableStreamBYOBReader(this, stream); } /** * @param {ArrayBufferView} view * @param {{ * min? : number * }} [options] * @returns {Promise<{ * value : ArrayBufferView, * done : boolean, * }>} */ async read(view, options = kEmptyObject) { if (!isReadableStreamBYOBReader(this)) throw new ERR_INVALID_THIS("ReadableStreamBYOBReader"); if (!isArrayBufferView(view)) { throw new ERR_INVALID_ARG_TYPE( "view", ["Buffer", "TypedArray", "DataView"], view, ); } validateObject(options, "options", kValidateObjectAllowObjectsAndNull); const viewByteLength = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "byteLength", ).get(view); const viewBuffer = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "buffer", ).get(view); const viewBufferByteLength = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "byteLength", ).get(viewBuffer); if (viewByteLength === 0 || viewBufferByteLength === 0) { throw new ERR_INVALID_STATE.TypeError( "View or Viewed ArrayBuffer is zero-length or detached", ); } // Supposed to assert here that the view's buffer is not // detached, but there's no API available to use to check that. const min = options?.min ?? 1; if (typeof min !== "number") throw new ERR_INVALID_ARG_TYPE("options.min", "number", min); if (!Number.isInteger(min)) throw new ERR_INVALID_ARG_VALUE("options.min", min, "must be an integer"); if (min <= 0) throw new ERR_INVALID_ARG_VALUE( "options.min", min, "must be greater than 0", ); if (!isDataView(view)) { if (min > TypedArrayPrototypeGetLength(view)) { throw new ERR_OUT_OF_RANGE("options.min", "<= view.length", min); } } else if (min > viewByteLength) { throw new ERR_OUT_OF_RANGE("options.min", "<= view.byteLength", min); } if (this[kState].stream === undefined) { throw new ERR_INVALID_STATE.TypeError( "The reader is not attached to a stream", ); } const readIntoRequest = new ReadIntoRequest(); readableStreamBYOBReaderRead(this, view, min, readIntoRequest); return readIntoRequest.promise; } releaseLock() { if (!isReadableStreamBYOBReader(this)) throw new ERR_INVALID_THIS("ReadableStreamBYOBReader"); if (this[kState].stream === undefined) return; readableStreamBYOBReaderRelease(this); } /** * @readonly * @type {Promise<void>} */ get closed() { if (!isReadableStreamBYOBReader(this)) return Promise.reject(new ERR_INVALID_THIS("ReadableStreamBYOBReader")); return this[kState].close.promise; } /** * @param {any} [reason] * @returns {Promise<void>} */ cancel(reason = undefined) { if (!isReadableStreamBYOBReader(this)) return Promise.reject(new ERR_INVALID_THIS("ReadableStreamBYOBReader")); if (this[kState].stream === undefined) { return Promise.reject( new ERR_INVALID_STATE.TypeError( "The reader is not attached to a stream", ), ); } return readableStreamReaderGenericCancel(this, reason); } [kInspect](depth, options) { return customInspect(depth, options, this[kType], { stream: this[kState].stream, requestIntoRequests: this[kState].requestIntoRequests.length, close: this[kState].close.promise, }); } } Object.defineProperties(ReadableStreamBYOBReader.prototype, { closed: kEnumerableProperty, read: kEnumerableProperty, releaseLock: kEnumerableProperty, cancel: kEnumerableProperty, [Symbol.toStringTag]: getNonWritablePropertyDescriptor( ReadableStreamBYOBReader.name, ), }); class ReadableStreamDefaultController { [kType] = "ReadableStreamDefaultController"; [kState] = {}; constructor(skipThrowSymbol = undefined) { if (skipThrowSymbol !== kSkipThrow) { throw new ERR_ILLEGAL_CONSTRUCTOR(); } } /** * @readonly * @type {number} */ get desiredSize() { return readableStreamDefaultControllerGetDesiredSize(this); } close() { if (!readableStreamDefaultControllerCanCloseOrEnqueue(this)) throw new ERR_INVALID_STATE.TypeError("Controller is already closed"); readableStreamDefaultControllerClose(this); } /** * @param {any} [chunk] */ enqueue(chunk = undefined) { if (!readableStreamDefaultControllerCanCloseOrEnqueue(this)) throw new ERR_INVALID_STATE.TypeError("Controller is already closed"); readableStreamDefaultControllerEnqueue(this, chunk); } /** * @param {any} [error] */ error(error = undefined) { readableStreamDefaultControllerError(this, error); } [kCancel](reason) { return readableStreamDefaultControllerCancelSteps(this, reason); } [kPull](readRequest) { readableStreamDefaultControllerPullSteps(this, readRequest); } [kRelease]() {} [kInspect](depth, options) { return customInspect(depth, options, this[kType], {}); } } Object.defineProperties(ReadableStreamDefaultController.prototype, { desiredSize: kEnumerableProperty, close: kEnumerableProperty, enqueue: kEnumerableProperty, error: kEnumerableProperty, [Symbol.toStringTag]: getNonWritablePropertyDescriptor( ReadableStreamDefaultController.name, ), }); class ReadableByteStreamController { [kType] = "ReadableByteStreamController"; [kState] = {}; constructor(skipThrowSymbol = undefined) { if (skipThrowSymbol !== kSkipThrow) { throw new ERR_ILLEGAL_CONSTRUCTOR(); } } /** * @readonly * @type {ReadableStreamBYOBRequest} */ get byobRequest() { if (!isReadableByteStreamController(this)) throw new ERR_INVALID_THIS("ReadableByteStreamController"); if ( this[kState].byobRequest === null && this[kState].pendingPullIntos.length ) { const { buffer, byteOffset, bytesFilled, byteLength } = this[kState].pendingPullIntos[0]; const view = new Uint8Array( buffer, byteOffset + bytesFilled, byteLength - bytesFilled, ); this[kState].byobRequest = createReadableStreamBYOBRequest(this, view); } return this[kState].byobRequest; } /** * @readonly * @type {number} */ get desiredSize() { if (!isReadableByteStreamController(this)) throw new ERR_INVALID_THIS("ReadableByteStreamController"); return readableByteStreamControllerGetDesiredSize(this); } close() { if (!isReadableByteStreamController(this)) throw new ERR_INVALID_THIS("ReadableByteStreamController"); if (this[kState].closeRequested) throw new ERR_INVALID_STATE.TypeError("Controller is already closed"); if (this[kState].stream[kState].state !== "readable") throw new ERR_INVALID_STATE.TypeError("ReadableStream is already closed"); readableByteStreamControllerClose(this); } /** * @param {ArrayBufferView} chunk */ enqueue(chunk) { if (!isReadableByteStreamController(this)) throw new ERR_INVALID_THIS("ReadableByteStreamController"); validateBuffer(chunk); const chunkByteLength = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "byteLength", ).get(chunk); const chunkBuffer = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "buffer", ).get(chunk); const chunkBufferByteLength = Object.getOwnPropertyDescriptor( ArrayBufferView.prototype, "byteLength", ).get(chunkBuffer); if (chunkByteLength === 0 || chunkBufferByteLength === 0) { throw new ERR_INVALID_STATE.TypeError( "chunk ArrayBuffer is zero-length or detached", ); } if (this[kState].closeRequested) throw new ERR_INVALID_STATE.TypeError("Controller is already closed"); if (this[kState].stream[kState].state !== "readable") throw new ERR_INVALID_STATE.TypeError("ReadableStream is already closed"); readableByteStreamControllerEnqueue(this, chunk); } /** * @param {any} [error] */ error(error = undefined) { if (!isReadableByteStreamController(this)) throw new ERR_INVALID_THIS("ReadableByteStreamController"); readableByteStreamControllerError(this, error); } [kCancel](reason) { return readableByteStreamControllerCancelSteps(this, reason); } [kPull](readRequest) { readableByteStreamControllerPullSteps(this, readRequest); } [kRelease]() { const { pendingPullIntos } = this[kState]; if (pendingPullIntos.length > 0) { const firstPendingPullInto = pendingPullIntos[0]; firstPendingPullInto.type = "none"; this[kState].pendingPullIntos = [firstPendingPullInto]; } } [kInspect](depth, options) { return customInspect(depth, options, this[kType], {}); } } Object.defineProperties(ReadableByteStreamController.prototype, { byobRequest: kEnumerableProperty, desiredSize: kEnumerableProperty, close: kEnumerableProperty, enqueue: kEnumerableProperty, error: kEnumerableProperty, [Symbol.toStringTag]: getNonWritablePropertyDescriptor( ReadableByteStreamController.name, ), }); function InternalReadableStream(start, pull, cancel, highWaterMark, size) { markTransferMode(this, false, true); this[kType] = "ReadableStream"; this[kState] = createReadableStreamState(); this[kIsClosedPromise] = createDeferredPromise(); const controller = new ReadableStreamDefaultController(kSkipThrow); setupReadableStreamDefaultController( this, controller, start, pull, cancel, highWaterMark, size, ); } Object.setPrototypeOf( InternalReadableStream.prototype, ReadableStream.prototype, ); Object.setPrototypeOf(InternalReadableStream, ReadableStream); function createReadableStream( start, pull, cancel, highWaterMark = 1, size = () => 1, ) { const stream = new InternalReadableStream( start, pull, cancel, highWaterMark, size, ); // For spec compliance the InternalReadableStream must be a ReadableStream stream.constructor = ReadableStream; return stream; } function InternalReadableByteStream(start, pull, cancel) { markTransferMode(this, false, true); this[kType] = "ReadableStream"; this[kState] = createReadableStreamState(); this[kIsClosedPromise] = createDeferredPromise(); const controller = new ReadableByteStreamController(kSkipThrow); setupReadableByteStreamController( this, controller, start, pull, cancel, 0, undefined, ); } Object.setPrototypeOf( InternalReadableByteStream.prototype, ReadableStream.prototype, ); Object.setPrototypeOf(InternalReadableByteStream, ReadableStream); function createReadableByteStream(start, pull, cancel) { const stream = new InternalReadableByteStream(start, pull, cancel); // For spec compliance the InternalReadableByteStream must be a ReadableStream stream.constructor = ReadableStream; return stream; } const isReadableStream = isBrandCheck("ReadableStream"); const isReadableByteStreamController = isBrandCheck( "ReadableByteStreamController", ); const isReadableStreamBYOBRequest = isBrandCheck("ReadableStreamBYOBRequest"); const isReadableStreamDefaultReader = isBrandCheck( "ReadableStreamDefaultReader", ); const isReadableStreamBYOBReader = isBrandCheck("ReadableStreamBYOBReader"); // ---- ReadableStream Implementation function createReadableStreamState() { return { __proto__: null, disturbed: false, reader: undefined, state: "readable", storedError: undefined, transfer: { __proto__: null, writable: undefined, port1: undefined, port2: undefined, promise: undefined, }, }; } function readableStreamFromIterable(iterable) { let stream; const iteratorRecord = getIterator(iterable, "async"); const startAlgorithm = nonOpStart; async function pullAlgorithm() { const nextResult = iteratorNext(iteratorRecord); const nextPromise = Promise.resolve(nextResult); return Promise.prototype.then.call(nextPromise, (iterResult) => { if (typeof iterResult !== "object" || iterResult === null) { throw new ERR_INVALID_STATE.TypeError( "The promise returned by the iterator.next() method must fulfill with an object", ); } if (iterResult.done) { readableStreamDefaultControllerClose(stream[kState].controller); } else { readableStreamDefaultControllerEnqueue( stream[kState].controller, iterResult.value, ); } }); } async function cancelAlgorithm(reason) { const iterator = iteratorRecord.iterator; const returnMethod = iterator.return; if (returnMethod === undefined) { return Promise.resolve(); } const returnResult = Function.prototype.call.call( returnMethod, iterator, reason, ); const returnPromise = Promise.resolve(returnResult); return Promise.prototype.then.call(returnPromise, (iterResult) => { if (typeof iterResult !== "object" || iterResult === null) { throw new ERR_INVALID_STATE.TypeError( "The promise returned by the iterator.return() method must fulfill with an object", ); } return undefined; }); } stream = createReadableStream( startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, ); return stream; } function readableStreamPipeTo( source, dest, preventClose, preventAbort, preventCancel, signal, ) { let reader; let writer; let disposable; // Both of these can throw synchronously. We want to capture // the error and return a rejected promise instead. try { reader = new ReadableStreamDefaultReader(source); writer = new WritableStreamDefaultWriter(dest); } catch (error) { return Promise.reject(error); } source[kState].disturbed = true; let shuttingDown = false; if (signal !== undefined) { try { validateAbortSignal(signal, "options.signal"); } catch (error) { return Promise.reject(error); } } const promise = createDeferredPromise(); const state = { currentWrite: Promise.resolve(), }; // The error here can be undefined. The rejected arg // tells us that the promise must be rejected even // when error is undefine. function finalize(rejected, error) { writableStreamDefaultWriterRelease(writer); readableStreamReaderGenericRelease(reader); if (signal !== undefined) disposable?.[Symbol.for("nodejs.dispose")](); if (rejected) promise.reject(error); else promise.resolve(); } async function waitForCurrentWrite() { const write = state.currentWrite; await write; if (write !== state.currentWrite) await waitForCurrentWrite(); } function shutdownWithAnAction(action, rejected, originalError) { if (shuttingDown) return; shuttingDown = true; if ( dest[kState].state === "writable" && !writableStreamCloseQueuedOrInFlight(dest) ) { Promise.prototype.then.call(waitForCurrentWrite(), complete, (error) => finalize(true, error), ); return; } complete(); function complete() { Promise.prototype.then.call( action(), () => finalize(rejected, originalError), (error) => finalize(true, error), ); } } function shutdown(rejected, error) { if (shuttingDown) return; shuttingDown = true; if ( dest[kState].state === "writable" && !writableStreamCloseQueuedOrInFlight(dest) ) { Promise.prototype.then.call( waitForCurrentWrite(), () => finalize(rejected, error), (error) => finalize(true, error), ); return; } finalize(rejected, error); } function abortAlgorithm() { let error; if (signal.reason instanceof AbortError) { // Cannot use the AbortError class here. It must be a DOMException. error = new DOMException(signal.reason.message, "AbortError"); } else { error = signal.reason; } const actions = []; if (!preventAbort) { Array.prototype.push.call(actions, () => { if (dest[kState].state === "writable") return writableStreamAbort(dest, error); return Promise.resolve(); }); } if (!preventCancel) { Array.prototype.push.call(actions, () => { if (source[kState].state === "readable") return readableStreamCancel(source, error); return Promise.resolve(); }); } shutdownWithAnAction( () => Promise.all(actions, (action) => action()), true, error, ); } function watchErrored(stream, promise, action) { if (stream[kState].state === "errored") action(stream[kState].storedError); else Promise.prototype.then.call(promise, undefined, action); } function watchClosed(stream, promise, action) { if (stream[kState].state === "closed") action(); else Promise.prototype.then.call(promise, action, () => {}); } async function step() { if (shuttingDown) return true; await writer[kState].ready.promise; const promise = createDeferredPromise(); // eslint-disable-next-line no-use-before-define readableStreamDefaultReaderRead( reader, new PipeToReadableStreamReadRequest(writer, state, promise), ); return promise.promise; } async function run() { // Run until step resolves as true while (!(await step())); } if (signal !== undefined) { if (signal.aborted) { abortAlgorithm(); return promise.promise; } addAbortListener ??= __hoisted_internal_events_abort_listener__.addAbortListener; disposable = addAbortListener(signal, abortAlgorithm); } setPromiseHandled(run()); watchErrored(source, reader[kState].close.promise, (error) => { if (!preventAbort) { return shutdownWithAnAction( () => writableStreamAbort(dest, error), true, error, ); } shutdown(true, error); }); watchErrored(dest, writer[kState].close.promise, (error) => { if (!preventCancel) { return shutdownWithAnAction( () => readableStreamCancel(source, error), true, error, ); } shutdown(true, error); }); watchClosed(source, reader[kState].close.promise, () => { if (!preventClose) { return shutdownWithAnAction(() => writableStreamDefaultWriterCloseWithErrorPropagation(writer), ); } shutdown(); }); if ( writableStreamCloseQueuedOrInFlight(dest) || dest[kState].state === "closed" ) { const error = new ERR_INVALID_STATE.TypeError( "Destination WritableStream is closed", ); if (!preventCancel) { shutdownWithAnAction( () => readableStreamCancel(source, error), true, error, ); } else { shutdown(true, error); } } return promise.promise; } class PipeToReadableStreamReadRequest { constructor(writer, state, promise) { this.writer = writer; this.state = state; this.promise = promise; } [kChunk](chunk) { this.state.currentWrite = writableStreamDefaultWriterWrite( this.writer, chunk, ); setPromiseHandled(this.state.currentWrite); this.promise.resolve(false); } [kClose]() { this.promise.resolve(true); } [kError](error) { this.promise.reject(error); } } function readableStreamTee(stream, cloneForBranch2) { if (isReadableByteStreamController(stream[kState].controller)) { return readableByteStreamTee(stream); } return readableStreamDefaultTee(stream, cloneForBranch2); } function readableStreamDefaultTee(stream, cloneForBranch2) { const reader = new ReadableStreamDefaultReader(stream); let reading = false; let canceled1 = false; let canceled2 = false; let reason1; let reason2; let branch1; let branch2; const cancelPromise = createDeferredPromise(); async function pullAlgorithm() { if (reading) return; reading = true; const readRequest = { [kChunk](value) { queueMicrotask(() => { reading = false; const value1 = value; let value2 = value; if (!canceled2 && cloneForBranch2) { value2 = structuredClone(value2); } if (!canceled1) { readableStreamDefaultControllerEnqueue( branch1[kState].controller, value1, ); } if (!canceled2) { readableStreamDefaultControllerEnqueue( branch2[kState].controller, value2, ); } }); }, [kClose]() { // The `process.nextTick()` is not part of the spec. // This approach was needed to avoid a race condition working with esm // Further information, see: https://github.com/nodejs/node/issues/39758 process.nextTick(() => { reading = false; if (!canceled1) readableStreamDefaultControllerClose(branch1[kState].controller); if (!canceled2) readableStreamDefaultControllerClose(branch2[kState].controller); if (!canceled1 || !canceled2) cancelPromise.resolve(); }); }, [kError]() { reading = false; }, }; readableStreamDefaultReaderRead(reader, readRequest); } function cancel1Algorithm(reason) { canceled1 = true; reason1 = reason; if (canceled2) { const compositeReason = [reason1, reason2]; cancelPromise.resolve(readableStreamCancel(stream, compositeReason)); } return cancelPromise.promise; } function cancel2Algorithm(reason) { canceled2 = true; reason2 = reason; if (canceled1) { const compositeReason = [reason1, reason2]; cancelPromise.resolve(readableStreamCancel(stream, compositeReason)); } return cancelPromise.promise; } branch1 = createReadableStream(nonOpStart, pullAlgorithm, cancel1Algorithm); branch2 = createReadableStream(nonOpStart, pullAlgorithm, cancel2Algorithm); Promise.prototype.then.call( reader[kState].close.promise, undefined, (error) => { readableStreamDefaultControllerError(branch1[kState].controller, error); readableStreamDefaultControllerError(branch2[kState].controller, error); if (!canceled1 || !canceled2) cancelPromise.resolve(); }, ); return [branch1, branch2]; } function readableByteStreamTee(stream) { assert(isReadableStream(stream)); assert(isReadableByteStreamController(stream[kState].controller)); let reader = new ReadableStreamDefaultReader(stream); let reading = false; let readAgainForBranch1 = false; let readAgainForBranch2 = false; let canceled1 = false; let canceled2 = false; let reason1; let reason2; let branch1; let branch2; const cancelDeferred = createDeferredPromise(); function forwardReaderError(thisReader) { Promise.prototype.then.call( thisReader[kState].close.promise, undefined, (error) => { if (thisReader !== reader) { return; } readableStreamDefaultControllerError(branch1[kState].controller, error); readableStreamDefaultControllerError(branch2[kState].controller, error); if (!canceled1 || !canceled2) { cancelDeferred.resolve(); } }, ); } function pullWithDefaultReader() { if (isReadableStreamBYOBReader(reader)) { readableStreamBYOBReaderRelease(reader); reader = new ReadableStreamDefaultReader(stream); forwardReaderError(reader); } const readRequest = { [kChunk](chunk) { queueMicrotask(() => { readAgainForBranch1 = false; readAgainForBranch2 = false; const chunk1 = chunk; let chunk2 = chunk; if (!canceled1 && !canceled2) { try { chunk2 = cloneAsUint8Array(chunk); } catch (error) { readableByteStreamControllerError( branch1[kState].controller, error, ); readableByteStreamControllerError( branch2[kState]