openpgp
Version:
OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.
1,154 lines (1,139 loc) • 1.53 MB
JavaScript
/*! OpenPGP.js v5.0.0-1 - 2021-03-03 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
var openpgp = (function (exports) {
'use strict';
const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
/**
* web-streams-polyfill v2.1.1
*/
/// <reference lib="es2015.symbol" />
const SymbolPolyfill = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ?
Symbol :
description => `Symbol(${description})`;
/// <reference lib="dom" />
function noop() {
// do nothing
}
/// <reference lib="es2015.core" />
// 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;
};
const rethrowAssertionErrorRejection = noop;
function typeIsObject(x) {
return (typeof x === 'object' && x !== null) || typeof x === 'function';
}
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 () => promiseResolvedWith(undefined);
}
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 promiseResolvedWith(Call(F, V, args));
}
catch (value) {
return promiseRejectedWith(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);
}
const originalPromise = Promise;
const originalPromiseThen = Promise.prototype.then;
const originalPromiseResolve = Promise.resolve.bind(originalPromise);
const originalPromiseReject = Promise.reject.bind(originalPromise);
function newPromise(executor) {
return new originalPromise(executor);
}
function promiseResolvedWith(value) {
return originalPromiseResolve(value);
}
function promiseRejectedWith(reason) {
return originalPromiseReject(reason);
}
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 originalPromiseThen.call(promise, onFulfilled, onRejected);
}
function uponPromise(promise, onFulfilled, onRejected) {
PerformPromiseThen(PerformPromiseThen(promise, onFulfilled, onRejected), undefined, rethrowAssertionErrorRejection);
}
function uponFulfillment(promise, onFulfilled) {
uponPromise(promise, onFulfilled);
}
function uponRejection(promise, onRejected) {
uponPromise(promise, undefined, onRejected);
}
function transformPromiseWith(promise, fulfillmentHandler, rejectionHandler) {
return PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler);
}
function setPromiseIsHandledToTrue(promise) {
PerformPromiseThen(promise, undefined, rethrowAssertionErrorRejection);
}
// Original from Chromium
// https://chromium.googlesource.com/chromium/src/+/0aee4434a4dba42a42abaea9bfbc0cd196a63bc1/third_party/blink/renderer/core/streams/SimpleQueue.js
const QUEUE_MAX_ARRAY_SIZE = 16384;
/**
* Simple queue structure.
*
* Avoids scalability issues with using a packed array directly by using
* multiple arrays in a linked list and keeping the array size bounded.
*/
class SimpleQueue {
constructor() {
this._cursor = 0;
this._size = 0;
// _front and _back are always defined.
this._front = {
_elements: [],
_next: undefined
};
this._back = this._front;
// The cursor is used to avoid calling Array.shift().
// It contains the index of the front element of the array inside the
// front-most node. It is always in the range [0, QUEUE_MAX_ARRAY_SIZE).
this._cursor = 0;
// When there is only one node, size === elements.length - cursor.
this._size = 0;
}
get length() {
return this._size;
}
// For exception safety, this method is structured in order:
// 1. Read state
// 2. Calculate required state mutations
// 3. Perform state mutations
push(element) {
const oldBack = this._back;
let newBack = oldBack;
if (oldBack._elements.length === QUEUE_MAX_ARRAY_SIZE - 1) {
newBack = {
_elements: [],
_next: undefined
};
}
// push() is the mutation most likely to throw an exception, so it
// goes first.
oldBack._elements.push(element);
if (newBack !== oldBack) {
this._back = newBack;
oldBack._next = newBack;
}
++this._size;
}
// Like push(), shift() follows the read -> calculate -> mutate pattern for
// exception safety.
shift() { // must not be called on an empty queue
const oldFront = this._front;
let newFront = oldFront;
const oldCursor = this._cursor;
let newCursor = oldCursor + 1;
const elements = oldFront._elements;
const element = elements[oldCursor];
if (newCursor === QUEUE_MAX_ARRAY_SIZE) {
newFront = oldFront._next;
newCursor = 0;
}
// No mutations before this point.
--this._size;
this._cursor = newCursor;
if (oldFront !== newFront) {
this._front = newFront;
}
// Permit shifted element to be garbage collected.
elements[oldCursor] = undefined;
return element;
}
// The tricky thing about forEach() is that it can be called
// re-entrantly. The queue may be mutated inside the callback. It is easy to
// see that push() within the callback has no negative effects since the end
// of the queue is checked for on every iteration. If shift() is called
// repeatedly within the callback then the next iteration may return an
// element that has been removed. In this case the callback will be called
// with undefined values until we either "catch up" with elements that still
// exist or reach the back of the queue.
forEach(callback) {
let i = this._cursor;
let node = this._front;
let elements = node._elements;
while (i !== elements.length || node._next !== undefined) {
if (i === elements.length) {
node = node._next;
elements = node._elements;
i = 0;
if (elements.length === 0) {
break;
}
}
callback(elements[i]);
++i;
}
}
// Return the element that would be returned if shift() was called now,
// without modifying the queue.
peek() { // must not be called on an empty queue
const front = this._front;
const cursor = this._cursor;
return front._elements[cursor];
}
}
function ReadableStreamCreateReadResult(value, done, forAuthorCode) {
let prototype = null;
if (forAuthorCode === true) {
prototype = Object.prototype;
}
const obj = Object.create(prototype);
obj.value = value;
obj.done = done;
return obj;
}
function ReadableStreamReaderGenericInitialize(reader, stream) {
reader._forAuthorCode = true;
reader._ownerReadableStream = stream;
stream._reader = reader;
if (stream._state === 'readable') {
defaultReaderClosedPromiseInitialize(reader);
}
else if (stream._state === 'closed') {
defaultReaderClosedPromiseInitializeAsResolved(reader);
}
else {
defaultReaderClosedPromiseInitializeAsRejected(reader, stream._storedError);
}
}
// A client of ReadableStreamDefaultReader and ReadableStreamBYOBReader may use these functions directly to bypass state
// check.
function ReadableStreamReaderGenericCancel(reader, reason) {
const stream = reader._ownerReadableStream;
return ReadableStreamCancel(stream, reason);
}
function ReadableStreamReaderGenericRelease(reader) {
if (reader._ownerReadableStream._state === 'readable') {
defaultReaderClosedPromiseReject(reader, new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
}
else {
defaultReaderClosedPromiseResetToRejected(reader, new TypeError('Reader was released and can no longer be used to monitor the stream\'s closedness'));
}
reader._ownerReadableStream._reader = undefined;
reader._ownerReadableStream = undefined;
}
// Helper functions for the readers.
function readerLockException(name) {
return new TypeError('Cannot ' + name + ' a stream using a released reader');
}
// Helper functions for the ReadableStreamDefaultReader.
function defaultReaderClosedPromiseInitialize(reader) {
reader._closedPromise = newPromise((resolve, reject) => {
reader._closedPromise_resolve = resolve;
reader._closedPromise_reject = reject;
});
}
function defaultReaderClosedPromiseInitializeAsRejected(reader, reason) {
defaultReaderClosedPromiseInitialize(reader);
defaultReaderClosedPromiseReject(reader, reason);
}
function defaultReaderClosedPromiseInitializeAsResolved(reader) {
defaultReaderClosedPromiseInitialize(reader);
defaultReaderClosedPromiseResolve(reader);
}
function defaultReaderClosedPromiseReject(reader, reason) {
setPromiseIsHandledToTrue(reader._closedPromise);
reader._closedPromise_reject(reason);
reader._closedPromise_resolve = undefined;
reader._closedPromise_reject = undefined;
}
function defaultReaderClosedPromiseResetToRejected(reader, reason) {
defaultReaderClosedPromiseInitializeAsRejected(reader, reason);
}
function defaultReaderClosedPromiseResolve(reader) {
reader._closedPromise_resolve(undefined);
reader._closedPromise_resolve = undefined;
reader._closedPromise_reject = undefined;
}
const CancelSteps = SymbolPolyfill('[[CancelSteps]]');
const PullSteps = SymbolPolyfill('[[PullSteps]]');
// Abstract operations for the ReadableStream.
function AcquireReadableStreamDefaultReader(stream, forAuthorCode = false) {
const reader = new ReadableStreamDefaultReader(stream);
reader._forAuthorCode = forAuthorCode;
return reader;
}
// ReadableStream API exposed for controllers.
function ReadableStreamAddReadRequest(stream) {
const promise = newPromise((resolve, reject) => {
const readRequest = {
_resolve: resolve,
_reject: reject
};
stream._reader._readRequests.push(readRequest);
});
return promise;
}
function ReadableStreamFulfillReadRequest(stream, chunk, done) {
const reader = stream._reader;
const readRequest = reader._readRequests.shift();
readRequest._resolve(ReadableStreamCreateReadResult(chunk, done, reader._forAuthorCode));
}
function ReadableStreamGetNumReadRequests(stream) {
return stream._reader._readRequests.length;
}
function ReadableStreamHasDefaultReader(stream) {
const reader = stream._reader;
if (reader === undefined) {
return false;
}
if (!IsReadableStreamDefaultReader(reader)) {
return false;
}
return true;
}
class ReadableStreamDefaultReader {
constructor(stream) {
if (IsReadableStream(stream) === false) {
throw new TypeError('ReadableStreamDefaultReader can only be constructed with a ReadableStream instance');
}
if (IsReadableStreamLocked(stream) === true) {
throw new TypeError('This stream has already been locked for exclusive reading by another reader');
}
ReadableStreamReaderGenericInitialize(this, stream);
this._readRequests = new SimpleQueue();
}
get closed() {
if (!IsReadableStreamDefaultReader(this)) {
return promiseRejectedWith(defaultReaderBrandCheckException('closed'));
}
return this._closedPromise;
}
cancel(reason) {
if (!IsReadableStreamDefaultReader(this)) {
return promiseRejectedWith(defaultReaderBrandCheckException('cancel'));
}
if (this._ownerReadableStream === undefined) {
return promiseRejectedWith(readerLockException('cancel'));
}
return ReadableStreamReaderGenericCancel(this, reason);
}
read() {
if (!IsReadableStreamDefaultReader(this)) {
return promiseRejectedWith(defaultReaderBrandCheckException('read'));
}
if (this._ownerReadableStream === undefined) {
return promiseRejectedWith(readerLockException('read from'));
}
return ReadableStreamDefaultReaderRead(this);
}
releaseLock() {
if (!IsReadableStreamDefaultReader(this)) {
throw defaultReaderBrandCheckException('releaseLock');
}
if (this._ownerReadableStream === undefined) {
return;
}
if (this._readRequests.length > 0) {
throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled');
}
ReadableStreamReaderGenericRelease(this);
}
}
// Abstract operations for the readers.
function IsReadableStreamDefaultReader(x) {
if (!typeIsObject(x)) {
return false;
}
if (!Object.prototype.hasOwnProperty.call(x, '_readRequests')) {
return false;
}
return true;
}
function ReadableStreamDefaultReaderRead(reader) {
const stream = reader._ownerReadableStream;
stream._disturbed = true;
if (stream._state === 'closed') {
return promiseResolvedWith(ReadableStreamCreateReadResult(undefined, true, reader._forAuthorCode));
}
if (stream._state === 'errored') {
return promiseRejectedWith(stream._storedError);
}
return stream._readableStreamController[PullSteps]();
}
// Helper functions for the ReadableStreamDefaultReader.
function defaultReaderBrandCheckException(name) {
return new TypeError(`ReadableStreamDefaultReader.prototype.${name} can only be used on a ReadableStreamDefaultReader`);
}
/// <reference lib="es2018.asynciterable" />
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 });
}
/// <reference lib="es2018.asynciterable" />
const ReadableStreamAsyncIteratorPrototype = {
next() {
if (IsReadableStreamAsyncIterator(this) === false) {
return promiseRejectedWith(streamAsyncIteratorBrandCheckException('next'));
}
const reader = this._asyncIteratorReader;
if (reader._ownerReadableStream === undefined) {
return promiseRejectedWith(readerLockException('iterate'));
}
return transformPromiseWith(ReadableStreamDefaultReaderRead(reader), 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 promiseRejectedWith(streamAsyncIteratorBrandCheckException('next'));
}
const reader = this._asyncIteratorReader;
if (reader._ownerReadableStream === undefined) {
return promiseRejectedWith(readerLockException('finish iterating'));
}
if (reader._readRequests.length > 0) {
return promiseRejectedWith(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 transformPromiseWith(result, () => ReadableStreamCreateReadResult(value, true, true));
}
ReadableStreamReaderGenericRelease(reader);
return promiseResolvedWith(ReadableStreamCreateReadResult(value, true, true));
}
};
if (AsyncIteratorPrototype !== undefined) {
Object.setPrototypeOf(ReadableStreamAsyncIteratorPrototype, AsyncIteratorPrototype);
}
Object.defineProperty(ReadableStreamAsyncIteratorPrototype, 'next', { enumerable: false });
Object.defineProperty(ReadableStreamAsyncIteratorPrototype, 'return', { enumerable: false });
// Abstract operations for the ReadableStream.
function AcquireReadableStreamAsyncIterator(stream, preventCancel = false) {
const reader = AcquireReadableStreamDefaultReader(stream);
const iterator = Object.create(ReadableStreamAsyncIteratorPrototype);
iterator._asyncIteratorReader = reader;
iterator._preventCancel = Boolean(preventCancel);
return iterator;
}
function IsReadableStreamAsyncIterator(x) {
if (!typeIsObject(x)) {
return false;
}
if (!Object.prototype.hasOwnProperty.call(x, '_asyncIteratorReader')) {
return false;
}
return true;
}
// Helper functions for the ReadableStream.
function streamAsyncIteratorBrandCheckException(name) {
return new TypeError(`ReadableStreamAsyncIterator.${name} can only be used on a ReadableSteamAsyncIterator`);
}
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.peek();
return pair.value;
}
function ResetQueue(container) {
container._queue = new SimpleQueue();
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 promiseRejectedWith(streamBrandCheckException('abort'));
}
if (IsWritableStreamLocked(this) === true) {
return promiseRejectedWith(new TypeError('Cannot abort a stream that already has a writer'));
}
return WritableStreamAbort(this, reason);
}
close() {
if (IsWritableStream(this) === false) {
return promiseRejectedWith(streamBrandCheckException('close'));
}
if (IsWritableStreamLocked(this) === true) {
return promiseRejectedWith(new TypeError('Cannot close a stream that already has a writer'));
}
if (WritableStreamCloseQueuedOrInFlight(this) === true) {
return promiseRejectedWith(new TypeError('Cannot close an already-closing stream'));
}
return WritableStreamClose(this);
}
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 = new SimpleQueue();
// 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 promiseResolvedWith(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 = newPromise((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;
}
function WritableStreamClose(stream) {
const state = stream._state;
if (state === 'closed' || state === 'errored') {
return promiseRejectedWith(new TypeError(`The stream (in ${state} state) is not in the writable state and cannot be closed`));
}
const promise = newPromise((resolve, reject) => {
const closeRequest = {
_resolve: resolve,
_reject: reject
};
stream._closeRequest = closeRequest;
});
const writer = stream._writer;
if (writer !== undefined && stream._backpressure === true && state === 'writable') {
defaultWriterReadyPromiseResolve(writer);
}
WritableStreamDefaultControllerClose(stream._writableStreamController);
return promise;
}
// WritableStream API exposed for controllers.
function WritableStreamAddWriteRequest(stream) {
const promise = newPromise((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;
stream._writeRequests.forEach(writeRequest => {
writeRequest._reject(storedError);
});
stream._writeRequests = new SimpleQueue();
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);
uponPromise(promise, () => {
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 promiseRejectedWith(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 promiseRejectedWith(defaultWriterBrandCheckException('ready'));
}
return this._readyPromise;
}
abort(reason) {
if (IsWritableStreamDefaultWriter(this) === false) {
return promiseRejectedWith(defaultWriterBrandCheckException('abort'));
}
if (this._ownerWritableStream === undefined) {
return promiseRejectedWith(defaultWriterLockException('abort'));
}
return WritableStreamDefaultWriterAbort(this, reason);
}
close() {
if (IsWritableStreamDefaultWriter(this) === false) {
return promiseRejectedWith(defaultWriterBrandCheckException('close'));
}
const stream = this._ownerWritableStream;
if (stream === undefined) {
return promiseRejectedWith(defaultWriterLockException('close'));
}
if (WritableStreamCloseQueuedOrInFlight(stream) === true) {
return promiseRejectedWith(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 promiseRejectedWith(defaultWriterBrandCheckException('write'));
}
if (this._ownerWritableStream === undefined) {
return promiseRejectedWith(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;
return WritableStreamClose(stream);
}
function WritableStreamDefaultWriterCloseWithErrorPropagation(writer) {
const stream = writer._ownerWritableStream;
const state = stream._state;
if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') {
return promiseResolvedWith(undefined);
}
if (state === 'errored') {
return promiseRejectedWith(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 promiseRejectedWith(defaultWriterLockException('write to'));
}
const state = stream._state;
if (state === 'errored') {
return promiseRejectedWith(stream._storedError);
}
if (WritableStreamCloseQueuedOrInFlight(stream) === true || state === 'closed') {
return promiseRejectedWith(new TypeError('The stream is closing or closed and cannot be written to'));
}
if (state === 'erroring') {
return promiseRejectedWith(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 = promiseResolvedWith(startResult);
uponPromise(startPromise, () => {
controller._started = true;
WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller);
}, r => {
controller._started = true;
WritableStreamDealWithRejection(stream, r);
});
}
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) {