UNPKG

puppeteer-core

Version:

A high-level API to control headless Chrome over the DevTools Protocol

336 lines 10.1 kB
"use strict"; /** * @license * Copyright 2023 Google Inc. * SPDX-License-Identifier: Apache-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SuppressedError = exports.AsyncDisposableStack = exports.DisposableStack = exports.asyncDisposeSymbol = exports.disposeSymbol = void 0; Symbol.dispose ??= Symbol('dispose'); Symbol.asyncDispose ??= Symbol('asyncDispose'); /** * @internal */ exports.disposeSymbol = Symbol.dispose; /** * @internal */ exports.asyncDisposeSymbol = Symbol.asyncDispose; /** * @internal */ class DisposableStack { #disposed = false; #stack = []; /** * Returns a value indicating whether the stack has been disposed. */ get disposed() { return this.#disposed; } /** * Alias for `[Symbol.dispose]()`. */ dispose() { this[exports.disposeSymbol](); } /** * Adds a disposable resource to the top of stack, returning the resource. * Has no effect if provided `null` or `undefined`. * * @param value - A `Disposable` object, `null`, or `undefined`. * `null` and `undefined` will not be added, but will be returned. * @returns The provided `value`. */ use(value) { if (value && typeof value[exports.disposeSymbol] === 'function') { this.#stack.push(value); } return value; } /** * Adds a non-disposable resource and a disposal callback to the top of the stack. * * @param value - A resource to be disposed. * @param onDispose - A callback invoked to dispose the provided value. * Will be invoked with `value` as the first parameter. * @returns The provided `value`. */ adopt(value, onDispose) { this.#stack.push({ [exports.disposeSymbol]() { onDispose(value); }, }); return value; } /** * Add a disposal callback to the top of the stack to be invoked when stack is disposed. * @param onDispose - A callback to invoke when this object is disposed. */ defer(onDispose) { this.#stack.push({ [exports.disposeSymbol]() { onDispose(); }, }); } /** * Move all resources out of this stack and into a new `DisposableStack`, and * marks this stack as disposed. * @returns The new `DisposableStack`. * * @example * * ```ts * class C { * #res1: Disposable; * #res2: Disposable; * #disposables: DisposableStack; * constructor() { * // stack will be disposed when exiting constructor for any reason * using stack = new DisposableStack(); * * // get first resource * this.#res1 = stack.use(getResource1()); * * // get second resource. If this fails, both `stack` and `#res1` will be disposed. * this.#res2 = stack.use(getResource2()); * * // all operations succeeded, move resources out of `stack` so that * // they aren't disposed when constructor exits * this.#disposables = stack.move(); * } * * [disposeSymbol]() { * this.#disposables.dispose(); * } * } * ``` */ move() { if (this.#disposed) { throw new ReferenceError('A disposed stack can not use anything new'); } const stack = new DisposableStack(); stack.#stack = this.#stack; this.#stack = []; this.#disposed = true; return stack; } /** * Disposes each resource in the stack in last-in-first-out (LIFO) manner. */ [exports.disposeSymbol]() { if (this.#disposed) { return; } this.#disposed = true; const errors = []; for (const resource of this.#stack.reverse()) { try { resource[exports.disposeSymbol](); } catch (e) { errors.push(e); } } if (errors.length === 1) { throw errors[0]; } else if (errors.length > 1) { let suppressed = null; for (const error of errors.reverse()) { if (suppressed === null) { suppressed = error; } else { suppressed = new SuppressedError(error, suppressed); } } throw suppressed; } } [Symbol.toStringTag] = 'DisposableStack'; } exports.DisposableStack = DisposableStack; /** * @internal */ class AsyncDisposableStack { #disposed = false; #stack = []; /** * Returns a value indicating whether the stack has been disposed. */ get disposed() { return this.#disposed; } /** * Alias for `[Symbol.asyncDispose]()`. */ async dispose() { await this[exports.asyncDisposeSymbol](); } /** * Adds a AsyncDisposable resource to the top of stack, returning the resource. * Has no effect if provided `null` or `undefined`. * * @param value - A `AsyncDisposable` object, `null`, or `undefined`. * `null` and `undefined` will not be added, but will be returned. * @returns The provided `value`. */ use(value) { if (value) { const asyncDispose = value[exports.asyncDisposeSymbol]; const dispose = value[exports.disposeSymbol]; if (typeof asyncDispose === 'function') { this.#stack.push(value); } else if (typeof dispose === 'function') { this.#stack.push({ [exports.asyncDisposeSymbol]: async () => { value[exports.disposeSymbol](); }, }); } } return value; } /** * Adds a non-disposable resource and a disposal callback to the top of the stack. * * @param value - A resource to be disposed. * @param onDispose - A callback invoked to dispose the provided value. * Will be invoked with `value` as the first parameter. * @returns The provided `value`. */ adopt(value, onDispose) { this.#stack.push({ [exports.asyncDisposeSymbol]() { return onDispose(value); }, }); return value; } /** * Add a disposal callback to the top of the stack to be invoked when stack is disposed. * @param onDispose - A callback to invoke when this object is disposed. */ defer(onDispose) { this.#stack.push({ [exports.asyncDisposeSymbol]() { return onDispose(); }, }); } /** * Move all resources out of this stack and into a new `DisposableStack`, and * marks this stack as disposed. * @returns The new `AsyncDisposableStack`. * * @example * * ```ts * class C { * #res1: Disposable; * #res2: Disposable; * #disposables: DisposableStack; * constructor() { * // stack will be disposed when exiting constructor for any reason * using stack = new DisposableStack(); * * // get first resource * this.#res1 = stack.use(getResource1()); * * // get second resource. If this fails, both `stack` and `#res1` will be disposed. * this.#res2 = stack.use(getResource2()); * * // all operations succeeded, move resources out of `stack` so that * // they aren't disposed when constructor exits * this.#disposables = stack.move(); * } * * [disposeSymbol]() { * this.#disposables.dispose(); * } * } * ``` */ move() { if (this.#disposed) { throw new ReferenceError('A disposed stack can not use anything new'); } const stack = new AsyncDisposableStack(); stack.#stack = this.#stack; this.#stack = []; this.#disposed = true; return stack; } /** * Disposes each resource in the stack in last-in-first-out (LIFO) manner. */ async [exports.asyncDisposeSymbol]() { if (this.#disposed) { return; } this.#disposed = true; const errors = []; for (const resource of this.#stack.reverse()) { try { await resource[exports.asyncDisposeSymbol](); } catch (e) { errors.push(e); } } if (errors.length === 1) { throw errors[0]; } else if (errors.length > 1) { let suppressed = null; for (const error of errors.reverse()) { if (suppressed === null) { suppressed = error; } else { suppressed = new SuppressedError(error, suppressed); } } throw suppressed; } } [Symbol.toStringTag] = 'AsyncDisposableStack'; } exports.AsyncDisposableStack = AsyncDisposableStack; /** * @internal * Represents an error that occurs when multiple errors are thrown during * the disposal of resources. This class encapsulates the primary error and * any suppressed errors that occurred subsequently. */ class SuppressedError extends Error { #error; #suppressed; constructor(error, suppressed, message = 'An error was suppressed during disposal') { super(message); this.name = 'SuppressedError'; this.#error = error; this.#suppressed = suppressed; } /** * The primary error that occurred during disposal. */ get error() { return this.#error; } /** * The suppressed error i.e. the error that was suppressed * because it occurred later in the flow after the original error. */ get suppressed() { return this.#suppressed; } } exports.SuppressedError = SuppressedError; //# sourceMappingURL=disposable.js.map