UNPKG

cloudworker-proxy

Version:
1,218 lines (1,208 loc) 119 kB
const SymbolPolyfill = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? Symbol : description => `Symbol(${description})`; /// <reference lib="dom" /> function noop() { // do nothing } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN#Polyfill const NumberIsNaN = Number.isNaN || function (x) { // eslint-disable-next-line no-self-compare return x !== x; }; function typeIsObject(x) { return (typeof x === 'object' && x !== null) || typeof x === 'function'; } function createDataProperty(o, p, v) { Object.defineProperty(o, p, { value: v, writable: true, enumerable: true, configurable: true }); } function createArrayFromList(elements) { // We use arrays to represent lists, so this is basically a no-op. // Do a slice though just in case we happen to depend on the unique-ness. return elements.slice(); } function ArrayBufferCopy(dest, destOffset, src, srcOffset, n) { new Uint8Array(dest).set(new Uint8Array(src, srcOffset, n), destOffset); } function IsFiniteNonNegativeNumber(v) { if (IsNonNegativeNumber(v) === false) { return false; } if (v === Infinity) { return false; } return true; } function IsNonNegativeNumber(v) { if (typeof v !== 'number') { return false; } if (NumberIsNaN(v)) { return false; } if (v < 0) { return false; } return true; } function Call(F, V, args) { if (typeof F !== 'function') { throw new TypeError('Argument is not a function'); } return Function.prototype.apply.call(F, V, args); } function CreateAlgorithmFromUnderlyingMethod(underlyingObject, methodName, algoArgCount, extraArgs) { const method = underlyingObject[methodName]; if (method !== undefined) { if (typeof method !== 'function') { throw new TypeError(`${method} is not a method`); } switch (algoArgCount) { case 0: { return () => { return PromiseCall(method, underlyingObject, extraArgs); }; } case 1: { return arg => { const fullArgs = [arg].concat(extraArgs); return PromiseCall(method, underlyingObject, fullArgs); }; } } } return () => Promise.resolve(); } function InvokeOrNoop(O, P, args) { const method = O[P]; if (method === undefined) { return undefined; } return Call(method, O, args); } function PromiseCall(F, V, args) { try { return Promise.resolve(Call(F, V, args)); } catch (value) { return Promise.reject(value); } } // Not implemented correctly function TransferArrayBuffer(O) { return O; } // Not implemented correctly function IsDetachedBuffer(O) { return false; } function ValidateAndNormalizeHighWaterMark(highWaterMark) { highWaterMark = Number(highWaterMark); if (NumberIsNaN(highWaterMark) || highWaterMark < 0) { throw new RangeError('highWaterMark property of a queuing strategy must be non-negative and non-NaN'); } return highWaterMark; } function MakeSizeAlgorithmFromSizeFunction(size) { if (size === undefined) { return () => 1; } if (typeof size !== 'function') { throw new TypeError('size property of a queuing strategy must be a function'); } return chunk => size(chunk); } function PerformPromiseThen(promise, onFulfilled, onRejected) { // There doesn't appear to be any way to correctly emulate the behaviour from JavaScript, so this is just an // approximation. return Promise.prototype.then.call(promise, onFulfilled, onRejected); } function WaitForAll(promises, successSteps, failureSteps) { let rejected = false; const rejectionHandler = (arg) => { if (rejected === false) { rejected = true; failureSteps(arg); } }; let index = 0; let fulfilledCount = 0; const total = promises.length; const result = new Array(total); for (const promise of promises) { const promiseIndex = index; const fulfillmentHandler = (arg) => { result[promiseIndex] = arg; ++fulfilledCount; if (fulfilledCount === total) { successSteps(result); } }; PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler); ++index; } } function WaitForAllPromise(promises, successSteps, failureSteps = undefined) { let resolvePromise; let rejectPromise; const promise = new Promise((resolve, reject) => { resolvePromise = resolve; rejectPromise = reject; }); if (failureSteps === undefined) { failureSteps = arg => { throw arg; }; } const successStepsWrapper = (results) => { try { const stepsResult = successSteps(results); resolvePromise(stepsResult); } catch (e) { rejectPromise(e); } }; const failureStepsWrapper = (reason) => { try { const stepsResult = failureSteps(reason); resolvePromise(stepsResult); } catch (e) { rejectPromise(e); } }; WaitForAll(promises, successStepsWrapper, failureStepsWrapper); return promise; } const rethrowAssertionErrorRejection = noop; function DequeueValue(container) { const pair = container._queue.shift(); container._queueTotalSize -= pair.size; if (container._queueTotalSize < 0) { container._queueTotalSize = 0; } return pair.value; } function EnqueueValueWithSize(container, value, size) { size = Number(size); if (!IsFiniteNonNegativeNumber(size)) { throw new RangeError('Size must be a finite, non-NaN, non-negative number.'); } container._queue.push({ value, size }); container._queueTotalSize += size; } function PeekQueueValue(container) { const pair = container._queue[0]; return pair.value; } function ResetQueue(container) { container._queue = []; container._queueTotalSize = 0; } const AbortSteps = SymbolPolyfill('[[AbortSteps]]'); const ErrorSteps = SymbolPolyfill('[[ErrorSteps]]'); class WritableStream { constructor(underlyingSink = {}, strategy = {}) { InitializeWritableStream(this); const size = strategy.size; let highWaterMark = strategy.highWaterMark; const type = underlyingSink.type; if (type !== undefined) { throw new RangeError('Invalid type is specified'); } const sizeAlgorithm = MakeSizeAlgorithmFromSizeFunction(size); if (highWaterMark === undefined) { highWaterMark = 1; } highWaterMark = ValidateAndNormalizeHighWaterMark(highWaterMark); SetUpWritableStreamDefaultControllerFromUnderlyingSink(this, underlyingSink, highWaterMark, sizeAlgorithm); } get locked() { if (IsWritableStream(this) === false) { throw streamBrandCheckException('locked'); } return IsWritableStreamLocked(this); } abort(reason) { if (IsWritableStream(this) === false) { return Promise.reject(streamBrandCheckException('abort')); } if (IsWritableStreamLocked(this) === true) { return Promise.reject(new TypeError('Cannot abort a stream that already has a writer')); } return WritableStreamAbort(this, reason); } getWriter() { if (IsWritableStream(this) === false) { throw streamBrandCheckException('getWriter'); } return AcquireWritableStreamDefaultWriter(this); } } // Abstract operations for the WritableStream. function AcquireWritableStreamDefaultWriter(stream) { return new WritableStreamDefaultWriter(stream); } // Throws if and only if startAlgorithm throws. function CreateWritableStream(startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark = 1, sizeAlgorithm = () => 1) { const stream = Object.create(WritableStream.prototype); InitializeWritableStream(stream); const controller = Object.create(WritableStreamDefaultController.prototype); SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm); return stream; } function InitializeWritableStream(stream) { stream._state = 'writable'; // The error that will be reported by new method calls once the state becomes errored. Only set when [[state]] is // 'erroring' or 'errored'. May be set to an undefined value. stream._storedError = undefined; stream._writer = undefined; // Initialize to undefined first because the constructor of the controller checks this // variable to validate the caller. stream._writableStreamController = undefined; // This queue is placed here instead of the writer class in order to allow for passing a writer to the next data // producer without waiting for the queued writes to finish. stream._writeRequests = []; // Write requests are removed from _writeRequests when write() is called on the underlying sink. This prevents // them from being erroneously rejected on error. If a write() call is in-flight, the request is stored here. stream._inFlightWriteRequest = undefined; // The promise that was returned from writer.close(). Stored here because it may be fulfilled after the writer // has been detached. stream._closeRequest = undefined; // Close request is removed from _closeRequest when close() is called on the underlying sink. This prevents it // from being erroneously rejected on error. If a close() call is in-flight, the request is stored here. stream._inFlightCloseRequest = undefined; // The promise that was returned from writer.abort(). This may also be fulfilled after the writer has detached. stream._pendingAbortRequest = undefined; // The backpressure signal set by the controller. stream._backpressure = false; } function IsWritableStream(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_writableStreamController')) { return false; } return true; } function IsWritableStreamLocked(stream) { if (stream._writer === undefined) { return false; } return true; } function WritableStreamAbort(stream, reason) { const state = stream._state; if (state === 'closed' || state === 'errored') { return Promise.resolve(undefined); } if (stream._pendingAbortRequest !== undefined) { return stream._pendingAbortRequest._promise; } let wasAlreadyErroring = false; if (state === 'erroring') { wasAlreadyErroring = true; // reason will not be used, so don't keep a reference to it. reason = undefined; } const promise = new Promise((resolve, reject) => { stream._pendingAbortRequest = { _promise: undefined, _resolve: resolve, _reject: reject, _reason: reason, _wasAlreadyErroring: wasAlreadyErroring }; }); stream._pendingAbortRequest._promise = promise; if (wasAlreadyErroring === false) { WritableStreamStartErroring(stream, reason); } return promise; } // WritableStream API exposed for controllers. function WritableStreamAddWriteRequest(stream) { const promise = new Promise((resolve, reject) => { const writeRequest = { _resolve: resolve, _reject: reject }; stream._writeRequests.push(writeRequest); }); return promise; } function WritableStreamDealWithRejection(stream, error) { const state = stream._state; if (state === 'writable') { WritableStreamStartErroring(stream, error); return; } WritableStreamFinishErroring(stream); } function WritableStreamStartErroring(stream, reason) { const controller = stream._writableStreamController; stream._state = 'erroring'; stream._storedError = reason; const writer = stream._writer; if (writer !== undefined) { WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason); } if (WritableStreamHasOperationMarkedInFlight(stream) === false && controller._started === true) { WritableStreamFinishErroring(stream); } } function WritableStreamFinishErroring(stream) { stream._state = 'errored'; stream._writableStreamController[ErrorSteps](); const storedError = stream._storedError; for (const writeRequest of stream._writeRequests) { writeRequest._reject(storedError); } stream._writeRequests = []; if (stream._pendingAbortRequest === undefined) { WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); return; } const abortRequest = stream._pendingAbortRequest; stream._pendingAbortRequest = undefined; if (abortRequest._wasAlreadyErroring === true) { abortRequest._reject(storedError); WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); return; } const promise = stream._writableStreamController[AbortSteps](abortRequest._reason); promise.then(() => { abortRequest._resolve(); WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); }, (reason) => { abortRequest._reject(reason); WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); }); } function WritableStreamFinishInFlightWrite(stream) { stream._inFlightWriteRequest._resolve(undefined); stream._inFlightWriteRequest = undefined; } function WritableStreamFinishInFlightWriteWithError(stream, error) { stream._inFlightWriteRequest._reject(error); stream._inFlightWriteRequest = undefined; WritableStreamDealWithRejection(stream, error); } function WritableStreamFinishInFlightClose(stream) { stream._inFlightCloseRequest._resolve(undefined); stream._inFlightCloseRequest = undefined; const state = stream._state; if (state === 'erroring') { // The error was too late to do anything, so it is ignored. stream._storedError = undefined; if (stream._pendingAbortRequest !== undefined) { stream._pendingAbortRequest._resolve(); stream._pendingAbortRequest = undefined; } } stream._state = 'closed'; const writer = stream._writer; if (writer !== undefined) { defaultWriterClosedPromiseResolve(writer); } } function WritableStreamFinishInFlightCloseWithError(stream, error) { stream._inFlightCloseRequest._reject(error); stream._inFlightCloseRequest = undefined; // Never execute sink abort() after sink close(). if (stream._pendingAbortRequest !== undefined) { stream._pendingAbortRequest._reject(error); stream._pendingAbortRequest = undefined; } WritableStreamDealWithRejection(stream, error); } // TODO(ricea): Fix alphabetical order. function WritableStreamCloseQueuedOrInFlight(stream) { if (stream._closeRequest === undefined && stream._inFlightCloseRequest === undefined) { return false; } return true; } function WritableStreamHasOperationMarkedInFlight(stream) { if (stream._inFlightWriteRequest === undefined && stream._inFlightCloseRequest === undefined) { return false; } return true; } function WritableStreamMarkCloseRequestInFlight(stream) { stream._inFlightCloseRequest = stream._closeRequest; stream._closeRequest = undefined; } function WritableStreamMarkFirstWriteRequestInFlight(stream) { stream._inFlightWriteRequest = stream._writeRequests.shift(); } function WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream) { if (stream._closeRequest !== undefined) { stream._closeRequest._reject(stream._storedError); stream._closeRequest = undefined; } const writer = stream._writer; if (writer !== undefined) { defaultWriterClosedPromiseReject(writer, stream._storedError); } } function WritableStreamUpdateBackpressure(stream, backpressure) { const writer = stream._writer; if (writer !== undefined && backpressure !== stream._backpressure) { if (backpressure === true) { defaultWriterReadyPromiseReset(writer); } else { defaultWriterReadyPromiseResolve(writer); } } stream._backpressure = backpressure; } class WritableStreamDefaultWriter { constructor(stream) { if (IsWritableStream(stream) === false) { throw new TypeError('WritableStreamDefaultWriter can only be constructed with a WritableStream instance'); } if (IsWritableStreamLocked(stream) === true) { throw new TypeError('This stream has already been locked for exclusive writing by another writer'); } this._ownerWritableStream = stream; stream._writer = this; const state = stream._state; if (state === 'writable') { if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._backpressure === true) { defaultWriterReadyPromiseInitialize(this); } else { defaultWriterReadyPromiseInitializeAsResolved(this); } defaultWriterClosedPromiseInitialize(this); } else if (state === 'erroring') { defaultWriterReadyPromiseInitializeAsRejected(this, stream._storedError); defaultWriterClosedPromiseInitialize(this); } else if (state === 'closed') { defaultWriterReadyPromiseInitializeAsResolved(this); defaultWriterClosedPromiseInitializeAsResolved(this); } else { const storedError = stream._storedError; defaultWriterReadyPromiseInitializeAsRejected(this, storedError); defaultWriterClosedPromiseInitializeAsRejected(this, storedError); } } get closed() { if (IsWritableStreamDefaultWriter(this) === false) { return Promise.reject(defaultWriterBrandCheckException('closed')); } return this._closedPromise; } get desiredSize() { if (IsWritableStreamDefaultWriter(this) === false) { throw defaultWriterBrandCheckException('desiredSize'); } if (this._ownerWritableStream === undefined) { throw defaultWriterLockException('desiredSize'); } return WritableStreamDefaultWriterGetDesiredSize(this); } get ready() { if (IsWritableStreamDefaultWriter(this) === false) { return Promise.reject(defaultWriterBrandCheckException('ready')); } return this._readyPromise; } abort(reason) { if (IsWritableStreamDefaultWriter(this) === false) { return Promise.reject(defaultWriterBrandCheckException('abort')); } if (this._ownerWritableStream === undefined) { return Promise.reject(defaultWriterLockException('abort')); } return WritableStreamDefaultWriterAbort(this, reason); } close() { if (IsWritableStreamDefaultWriter(this) === false) { return Promise.reject(defaultWriterBrandCheckException('close')); } const stream = this._ownerWritableStream; if (stream === undefined) { return Promise.reject(defaultWriterLockException('close')); } if (WritableStreamCloseQueuedOrInFlight(stream) === true) { return Promise.reject(new TypeError('cannot close an already-closing stream')); } return WritableStreamDefaultWriterClose(this); } releaseLock() { if (IsWritableStreamDefaultWriter(this) === false) { throw defaultWriterBrandCheckException('releaseLock'); } const stream = this._ownerWritableStream; if (stream === undefined) { return; } WritableStreamDefaultWriterRelease(this); } write(chunk) { if (IsWritableStreamDefaultWriter(this) === false) { return Promise.reject(defaultWriterBrandCheckException('write')); } if (this._ownerWritableStream === undefined) { return Promise.reject(defaultWriterLockException('write to')); } return WritableStreamDefaultWriterWrite(this, chunk); } } // Abstract operations for the WritableStreamDefaultWriter. function IsWritableStreamDefaultWriter(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_ownerWritableStream')) { return false; } return true; } // A client of WritableStreamDefaultWriter may use these functions directly to bypass state check. function WritableStreamDefaultWriterAbort(writer, reason) { const stream = writer._ownerWritableStream; return WritableStreamAbort(stream, reason); } function WritableStreamDefaultWriterClose(writer) { const stream = writer._ownerWritableStream; const state = stream._state; if (state === 'closed' || state === 'errored') { return Promise.reject(new TypeError(`The stream (in ${state} state) is not in the writable state and cannot be closed`)); } const promise = new Promise((resolve, reject) => { const closeRequest = { _resolve: resolve, _reject: reject }; stream._closeRequest = closeRequest; }); if (stream._backpressure === true && state === 'writable') { defaultWriterReadyPromiseResolve(writer); } WritableStreamDefaultControllerClose(stream._writableStreamController); return promise; } function WritableStreamDefaultWriterCloseWithErrorPropagation(writer) { const stream = writer._ownerWritableStream; const state = stream._state; if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') { return Promise.resolve(); } if (state === 'errored') { return Promise.reject(stream._storedError); } return WritableStreamDefaultWriterClose(writer); } function WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, error) { if (writer._closedPromiseState === 'pending') { defaultWriterClosedPromiseReject(writer, error); } else { defaultWriterClosedPromiseResetToRejected(writer, error); } } function WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) { if (writer._readyPromiseState === 'pending') { defaultWriterReadyPromiseReject(writer, error); } else { defaultWriterReadyPromiseResetToRejected(writer, error); } } function WritableStreamDefaultWriterGetDesiredSize(writer) { const stream = writer._ownerWritableStream; const state = stream._state; if (state === 'errored' || state === 'erroring') { return null; } if (state === 'closed') { return 0; } return WritableStreamDefaultControllerGetDesiredSize(stream._writableStreamController); } function WritableStreamDefaultWriterRelease(writer) { const stream = writer._ownerWritableStream; const releasedError = new TypeError('Writer was released and can no longer be used to monitor the stream\'s closedness'); WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError); // The state transitions to "errored" before the sink abort() method runs, but the writer.closed promise is not // rejected until afterwards. This means that simply testing state will not work. WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError); stream._writer = undefined; writer._ownerWritableStream = undefined; } function WritableStreamDefaultWriterWrite(writer, chunk) { const stream = writer._ownerWritableStream; const controller = stream._writableStreamController; const chunkSize = WritableStreamDefaultControllerGetChunkSize(controller, chunk); if (stream !== writer._ownerWritableStream) { return Promise.reject(defaultWriterLockException('write to')); } const state = stream._state; if (state === 'errored') { return Promise.reject(stream._storedError); } if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') { return Promise.reject(new TypeError('The stream is closing or closed and cannot be written to')); } if (state === 'erroring') { return Promise.reject(stream._storedError); } const promise = WritableStreamAddWriteRequest(stream); WritableStreamDefaultControllerWrite(controller, chunk, chunkSize); return promise; } class WritableStreamDefaultController { /** @internal */ constructor() { throw new TypeError('WritableStreamDefaultController cannot be constructed explicitly'); } error(e) { if (IsWritableStreamDefaultController(this) === false) { throw new TypeError('WritableStreamDefaultController.prototype.error can only be used on a WritableStreamDefaultController'); } const state = this._controlledWritableStream._state; if (state !== 'writable') { // The stream is closed, errored or will be soon. The sink can't do anything useful if it gets an error here, so // just treat it as a no-op. return; } WritableStreamDefaultControllerError(this, e); } /** @internal */ [AbortSteps](reason) { const result = this._abortAlgorithm(reason); WritableStreamDefaultControllerClearAlgorithms(this); return result; } /** @internal */ [ErrorSteps]() { ResetQueue(this); } } // Abstract operations implementing interface required by the WritableStream. function IsWritableStreamDefaultController(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_controlledWritableStream')) { return false; } return true; } function SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm) { controller._controlledWritableStream = stream; stream._writableStreamController = controller; // Need to set the slots so that the assert doesn't fire. In the spec the slots already exist implicitly. controller._queue = undefined; controller._queueTotalSize = undefined; ResetQueue(controller); controller._started = false; controller._strategySizeAlgorithm = sizeAlgorithm; controller._strategyHWM = highWaterMark; controller._writeAlgorithm = writeAlgorithm; controller._closeAlgorithm = closeAlgorithm; controller._abortAlgorithm = abortAlgorithm; const backpressure = WritableStreamDefaultControllerGetBackpressure(controller); WritableStreamUpdateBackpressure(stream, backpressure); const startResult = startAlgorithm(); const startPromise = Promise.resolve(startResult); startPromise.then(() => { controller._started = true; WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); }, r => { controller._started = true; WritableStreamDealWithRejection(stream, r); }).catch(rethrowAssertionErrorRejection); } function SetUpWritableStreamDefaultControllerFromUnderlyingSink(stream, underlyingSink, highWaterMark, sizeAlgorithm) { const controller = Object.create(WritableStreamDefaultController.prototype); function startAlgorithm() { return InvokeOrNoop(underlyingSink, 'start', [controller]); } const writeAlgorithm = CreateAlgorithmFromUnderlyingMethod(underlyingSink, 'write', 1, [controller]); const closeAlgorithm = CreateAlgorithmFromUnderlyingMethod(underlyingSink, 'close', 0, []); const abortAlgorithm = CreateAlgorithmFromUnderlyingMethod(underlyingSink, 'abort', 1, []); SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm); } // ClearAlgorithms may be called twice. Erroring the same stream in multiple ways will often result in redundant calls. function WritableStreamDefaultControllerClearAlgorithms(controller) { controller._writeAlgorithm = undefined; controller._closeAlgorithm = undefined; controller._abortAlgorithm = undefined; controller._strategySizeAlgorithm = undefined; } function WritableStreamDefaultControllerClose(controller) { EnqueueValueWithSize(controller, 'close', 0); WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); } function WritableStreamDefaultControllerGetChunkSize(controller, chunk) { try { return controller._strategySizeAlgorithm(chunk); } catch (chunkSizeE) { WritableStreamDefaultControllerErrorIfNeeded(controller, chunkSizeE); return 1; } } function WritableStreamDefaultControllerGetDesiredSize(controller) { return controller._strategyHWM - controller._queueTotalSize; } function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) { const writeRecord = { chunk }; try { EnqueueValueWithSize(controller, writeRecord, chunkSize); } catch (enqueueE) { WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueE); return; } const stream = controller._controlledWritableStream; if (WritableStreamCloseQueuedOrInFlight(stream) === false && stream._state === 'writable') { const backpressure = WritableStreamDefaultControllerGetBackpressure(controller); WritableStreamUpdateBackpressure(stream, backpressure); } WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); } // Abstract operations for the WritableStreamDefaultController. function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) { const stream = controller._controlledWritableStream; if (controller._started === false) { return; } if (stream._inFlightWriteRequest !== undefined) { return; } const state = stream._state; if (state === 'erroring') { WritableStreamFinishErroring(stream); return; } if (controller._queue.length === 0) { return; } const writeRecord = PeekQueueValue(controller); if (writeRecord === 'close') { WritableStreamDefaultControllerProcessClose(controller); } else { WritableStreamDefaultControllerProcessWrite(controller, writeRecord.chunk); } } function WritableStreamDefaultControllerErrorIfNeeded(controller, error) { if (controller._controlledWritableStream._state === 'writable') { WritableStreamDefaultControllerError(controller, error); } } function WritableStreamDefaultControllerProcessClose(controller) { const stream = controller._controlledWritableStream; WritableStreamMarkCloseRequestInFlight(stream); DequeueValue(controller); const sinkClosePromise = controller._closeAlgorithm(); WritableStreamDefaultControllerClearAlgorithms(controller); sinkClosePromise.then(() => { WritableStreamFinishInFlightClose(stream); }, reason => { WritableStreamFinishInFlightCloseWithError(stream, reason); }).catch(rethrowAssertionErrorRejection); } function WritableStreamDefaultControllerProcessWrite(controller, chunk) { const stream = controller._controlledWritableStream; WritableStreamMarkFirstWriteRequestInFlight(stream); const sinkWritePromise = controller._writeAlgorithm(chunk); sinkWritePromise.then(() => { WritableStreamFinishInFlightWrite(stream); const state = stream._state; DequeueValue(controller); if (WritableStreamCloseQueuedOrInFlight(stream) === false && state === 'writable') { const backpressure = WritableStreamDefaultControllerGetBackpressure(controller); WritableStreamUpdateBackpressure(stream, backpressure); } WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); }, reason => { if (stream._state === 'writable') { WritableStreamDefaultControllerClearAlgorithms(controller); } WritableStreamFinishInFlightWriteWithError(stream, reason); }).catch(rethrowAssertionErrorRejection); } function WritableStreamDefaultControllerGetBackpressure(controller) { const desiredSize = WritableStreamDefaultControllerGetDesiredSize(controller); return desiredSize <= 0; } // A client of WritableStreamDefaultController may use these functions directly to bypass state check. function WritableStreamDefaultControllerError(controller, error) { const stream = controller._controlledWritableStream; WritableStreamDefaultControllerClearAlgorithms(controller); WritableStreamStartErroring(stream, error); } // Helper functions for the WritableStream. function streamBrandCheckException(name) { return new TypeError(`WritableStream.prototype.${name} can only be used on a WritableStream`); } // Helper functions for the WritableStreamDefaultWriter. function defaultWriterBrandCheckException(name) { return new TypeError(`WritableStreamDefaultWriter.prototype.${name} can only be used on a WritableStreamDefaultWriter`); } function defaultWriterLockException(name) { return new TypeError('Cannot ' + name + ' a stream using a released writer'); } function defaultWriterClosedPromiseInitialize(writer) { writer._closedPromise = new Promise((resolve, reject) => { writer._closedPromise_resolve = resolve; writer._closedPromise_reject = reject; writer._closedPromiseState = 'pending'; }); } function defaultWriterClosedPromiseInitializeAsRejected(writer, reason) { defaultWriterClosedPromiseInitialize(writer); defaultWriterClosedPromiseReject(writer, reason); } function defaultWriterClosedPromiseInitializeAsResolved(writer) { defaultWriterClosedPromiseInitialize(writer); defaultWriterClosedPromiseResolve(writer); } function defaultWriterClosedPromiseReject(writer, reason) { writer._closedPromise.catch(() => { }); writer._closedPromise_reject(reason); writer._closedPromise_resolve = undefined; writer._closedPromise_reject = undefined; writer._closedPromiseState = 'rejected'; } function defaultWriterClosedPromiseResetToRejected(writer, reason) { defaultWriterClosedPromiseInitializeAsRejected(writer, reason); } function defaultWriterClosedPromiseResolve(writer) { writer._closedPromise_resolve(undefined); writer._closedPromise_resolve = undefined; writer._closedPromise_reject = undefined; writer._closedPromiseState = 'resolved'; } function defaultWriterReadyPromiseInitialize(writer) { writer._readyPromise = new Promise((resolve, reject) => { writer._readyPromise_resolve = resolve; writer._readyPromise_reject = reject; }); writer._readyPromiseState = 'pending'; } function defaultWriterReadyPromiseInitializeAsRejected(writer, reason) { defaultWriterReadyPromiseInitialize(writer); defaultWriterReadyPromiseReject(writer, reason); } function defaultWriterReadyPromiseInitializeAsResolved(writer) { defaultWriterReadyPromiseInitialize(writer); defaultWriterReadyPromiseResolve(writer); } function defaultWriterReadyPromiseReject(writer, reason) { writer._readyPromise.catch(() => { }); writer._readyPromise_reject(reason); writer._readyPromise_resolve = undefined; writer._readyPromise_reject = undefined; writer._readyPromiseState = 'rejected'; } function defaultWriterReadyPromiseReset(writer) { defaultWriterReadyPromiseInitialize(writer); } function defaultWriterReadyPromiseResetToRejected(writer, reason) { defaultWriterReadyPromiseInitializeAsRejected(writer, reason); } function defaultWriterReadyPromiseResolve(writer) { writer._readyPromise_resolve(undefined); writer._readyPromise_resolve = undefined; writer._readyPromise_reject = undefined; writer._readyPromiseState = 'fulfilled'; } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger#Polyfill const NumberIsInteger = Number.isInteger || function (value) { return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; }; let AsyncIteratorPrototype; if (typeof SymbolPolyfill.asyncIterator === 'symbol') { // We're running inside a ES2018+ environment, but we're compiling to an older syntax. // We cannot access %AsyncIteratorPrototype% without non-ES2018 syntax, but we can re-create it. AsyncIteratorPrototype = { // 25.1.3.1 %AsyncIteratorPrototype% [ @@asyncIterator ] ( ) // https://tc39.github.io/ecma262/#sec-asynciteratorprototype-asynciterator [SymbolPolyfill.asyncIterator]() { return this; } }; Object.defineProperty(AsyncIteratorPrototype, SymbolPolyfill.asyncIterator, { enumerable: false }); } const CancelSteps = SymbolPolyfill('[[CancelSteps]]'); const PullSteps = SymbolPolyfill('[[PullSteps]]'); class ReadableStream { constructor(underlyingSource = {}, strategy = {}) { InitializeReadableStream(this); const size = strategy.size; let highWaterMark = strategy.highWaterMark; const type = underlyingSource.type; const typeString = String(type); if (typeString === 'bytes') { if (size !== undefined) { throw new RangeError('The strategy for a byte stream cannot have a size function'); } if (highWaterMark === undefined) { highWaterMark = 0; } highWaterMark = ValidateAndNormalizeHighWaterMark(highWaterMark); SetUpReadableByteStreamControllerFromUnderlyingSource(this, underlyingSource, highWaterMark); } else if (type === undefined) { const sizeAlgorithm = MakeSizeAlgorithmFromSizeFunction(size); if (highWaterMark === undefined) { highWaterMark = 1; } highWaterMark = ValidateAndNormalizeHighWaterMark(highWaterMark); SetUpReadableStreamDefaultControllerFromUnderlyingSource(this, underlyingSource, highWaterMark, sizeAlgorithm); } else { throw new RangeError('Invalid type is specified'); } } get locked() { if (IsReadableStream(this) === false) { throw streamBrandCheckException$1('locked'); } return IsReadableStreamLocked(this); } cancel(reason) { if (IsReadableStream(this) === false) { return Promise.reject(streamBrandCheckException$1('cancel')); } if (IsReadableStreamLocked(this) === true) { return Promise.reject(new TypeError('Cannot cancel a stream that already has a reader')); } return ReadableStreamCancel(this, reason); } getReader({ mode } = {}) { if (IsReadableStream(this) === false) { throw streamBrandCheckException$1('getReader'); } if (mode === undefined) { return AcquireReadableStreamDefaultReader(this, true); } mode = String(mode); if (mode === 'byob') { return AcquireReadableStreamBYOBReader(this, true); } throw new RangeError('Invalid mode is specified'); } pipeThrough({ writable, readable }, { preventClose, preventAbort, preventCancel, signal } = {}) { if (IsReadableStream(this) === false) { throw streamBrandCheckException$1('pipeThrough'); } if (IsWritableStream(writable) === false) { throw new TypeError('writable argument to pipeThrough must be a WritableStream'); } if (IsReadableStream(readable) === false) { throw new TypeError('readable argument to pipeThrough must be a ReadableStream'); } preventClose = Boolean(preventClose); preventAbort = Boolean(preventAbort); preventCancel = Boolean(preventCancel); if (signal !== undefined && !isAbortSignal(signal)) { throw new TypeError('ReadableStream.prototype.pipeThrough\'s signal option must be an AbortSignal'); } if (IsReadableStreamLocked(this) === true) { throw new TypeError('ReadableStream.prototype.pipeThrough cannot be used on a locked ReadableStream'); } if (IsWritableStreamLocked(writable) === true) { throw new TypeError('ReadableStream.prototype.pipeThrough cannot be used on a locked WritableStream'); } const promise = ReadableStreamPipeTo(this, writable, preventClose, preventAbort, preventCancel, signal); promise.catch(() => { }); return readable; } pipeTo(dest, { preventClose, preventAbort, preventCancel, signal } = {}) { if (IsReadableStream(this) === false) { return Promise.reject(streamBrandCheckException$1('pipeTo')); } if (IsWritableStream(dest) === false) { return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo\'s first argument must be a WritableStream')); } preventClose = Boolean(preventClose); preventAbort = Boolean(preventAbort); preventCancel = Boolean(preventCancel); if (signal !== undefined && !isAbortSignal(signal)) { return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo\'s signal option must be an AbortSignal')); } if (IsReadableStreamLocked(this) === true) { return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked ReadableStream')); } if (IsWritableStreamLocked(dest) === true) { return Promise.reject(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked WritableStream')); } return ReadableStreamPipeTo(this, dest, preventClose, preventAbort, preventCancel, signal); } tee() { if (IsReadableStream(this) === false) { throw streamBrandCheckException$1('tee'); } const branches = ReadableStreamTee(this, false); return createArrayFromList(branches); } getIterator({ preventCancel } = {}) { if (IsReadableStream(this) === false) { throw streamBrandCheckException$1('getIterator'); } const reader = AcquireReadableStreamDefaultReader(this); const iterator = Object.create(ReadableStreamAsyncIteratorPrototype); iterator._asyncIteratorReader = reader; iterator._preventCancel = Boolean(preventCancel); return iterator; } } const ReadableStreamAsyncIteratorPrototype = { next() { if (IsReadableStreamAsyncIterator(this) === false) { return Promise.reject(streamAsyncIteratorBrandCheckException('next')); } const reader = this._asyncIteratorReader; if (reader._ownerReadableStream === undefined) { return Promise.reject(readerLockException('iterate')); } return ReadableStreamDefaultReaderRead(reader).then(result => { const done = result.done; if (done) { ReadableStreamReaderGenericRelease(reader); } const value = result.value; return ReadableStreamCreateReadResult(value, done, true); }); }, return(value) { if (IsReadableStreamAsyncIterator(this) === false) { return Promise.reject(streamAsyncIteratorBrandCheckException('next')); } const reader = this._asyncIteratorReader; if (reader._ownerReadableStream === undefined) { return Promise.reject(readerLockException('finish iterating')); } if (reader._readRequests.length > 0) { return Promise.reject(new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled')); } if (this._preventCancel === false) { const result = ReadableStreamReaderGenericCancel(reader, value); ReadableStreamReaderGenericRelease(reader); return result.then(() => ReadableStreamCreateReadResult(value, true, true)); } ReadableStreamReaderGenericRelease(reader); return Promise.resolve(ReadableStreamCreateReadResult(value, true, true)); } }; if (AsyncIteratorPrototype !== undefined) { Object.setPrototypeOf(ReadableStreamAsyncIteratorPrototype, AsyncIteratorPrototype); } Object.defineProperty(ReadableStreamAsyncIteratorPrototype, 'next', { enumerable: false }); Object.defineProperty(ReadableStreamAsyncIteratorPrototype, 'return', { enumerable: false }); if (typeof SymbolPolyfill.asyncIterator === 'symbol') { Object.defineProperty(ReadableStream.prototype, SymbolPolyfill.asyncIterator, { value: ReadableStream.prototype.getIterator, enumerable: false, writable: true, configurable: true }); } // Abstract operations for the ReadableStream. function AcquireReadableStreamBYOBReader(stream, forAuthorCode = false) { const reader = new ReadableStreamBYOBReader(stream); reader._forAuthorCode = forAuthorCode; return reader; } function AcquireReadableStreamDefaultReader(stream, forAuthorCode = false) { const reader = new ReadableStreamDefaultReader(stream); reader._forAuthorCode = forAuthorCode; return reader; } // Throws if and only if startAlgorithm throws. function CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark = 1, sizeAlgorithm = () => 1) { const stream = Object.create(ReadableStream.prototype); InitializeReadableStream(stream); const controller = Object.create(ReadableStreamDefaultController.prototype); SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm); return stream; } function InitializeReadableStream(stream) { stream._state = 'readable'; stream._reader = undefined; stream._storedError = undefined; stream._disturbed = false; } function IsReadableStream(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_readableStreamController')) { return false; } return true; } function IsReadableStreamLocked(stream) { if (stream._reader === undefined) { return false; } return true; } function IsReadableStreamAsyncIterator(x) { if (!typeIsObject(x)) { return false; } if (!Object.prototype.hasOwnProperty.call(x, '_asyncIteratorReader')) { return false; } return true; } function ReadableStreamPipeTo(source, dest, preventClose, preventAbort, preventCancel, signal) { const reader = AcquireReadableStreamDefaultReader(source); const writer = AcquireWritableStreamDefaultWriter(dest); let shuttingDown = false; // This is used to keep track of the spec's requirement that we wait for ongoing writes during shutdown. let currentWrite = Promise.resolve(); return new Promise((resolve, reject) => { let abortAlgorithm; if (signal !== undefined) { abortAlgorithm = () => { const error = new DOMException('Aborted', 'AbortError'); const actions = []; if (preventAbort === false) { actions.push(() => { if (dest._state === 'writable') { return WritableStreamAbort(dest, error); } return Promise.resolve(); }); } if (preventCancel === false) { actions.push(() => { if (source._state === 'readable') { return ReadableStreamCancel(source, error); } return Promise.resolve(); }); } shutdownWithAction(() => WaitForAllPromise(actions.map(action => action()), results => results), true, error); }; if (signal.aborted === true) { abortAlgorithm(); return; } signal.addEventListener('abort', abortAlgorithm); } // Using reader and writer, read all chunks from this and write them to dest // - Backpressure must be enforced // - Shutdown must stop all activity function pipeLoop() { return new Promise((resolveLoop, rejectLoop) => { function next(done) { if (done) {