Update dashboard, kb, memory +4 more (+28 ~18 -1)

This commit is contained in:
Echo
2026-02-06 14:25:10 +00:00
parent 7f64d5054a
commit 19d178268a
6767 changed files with 1346472 additions and 1282 deletions

View File

@@ -0,0 +1,89 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {ElementHandle} from '../api/ElementHandle.js';
import {assert} from '../util/assert.js';
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
import {QueryHandler, type QuerySelector} from './QueryHandler.js';
import type {AwaitableIterable} from './types.js';
interface ARIASelector {
name?: string;
role?: string;
}
const isKnownAttribute = (
attribute: string,
): attribute is keyof ARIASelector => {
return ['name', 'role'].includes(attribute);
};
/**
* The selectors consist of an accessible name to query for and optionally
* further aria attributes on the form `[<attribute>=<value>]`.
* Currently, we only support the `name` and `role` attribute.
* The following examples showcase how the syntax works wrt. querying:
*
* - 'title[role="heading"]' queries for elements with name 'title' and role 'heading'.
* - '[role="image"]' queries for elements with role 'image' and any name.
* - 'label' queries for elements with name 'label' and any role.
* - '[name=""][role="button"]' queries for elements with no name and role 'button'.
*/
const ATTRIBUTE_REGEXP =
/\[\s*(?<attribute>\w+)\s*=\s*(?<quote>"|')(?<value>\\.|.*?(?=\k<quote>))\k<quote>\s*\]/g;
const parseARIASelector = (selector: string): ARIASelector => {
if (selector.length > 10_000) {
throw new Error(`Selector ${selector} is too long`);
}
const queryOptions: ARIASelector = {};
const defaultName = selector.replace(
ATTRIBUTE_REGEXP,
(_, attribute, __, value) => {
assert(
isKnownAttribute(attribute),
`Unknown aria attribute "${attribute}" in selector`,
);
queryOptions[attribute] = value;
return '';
},
);
if (defaultName && !queryOptions.name) {
queryOptions.name = defaultName;
}
return queryOptions;
};
/**
* @internal
*/
export class ARIAQueryHandler extends QueryHandler {
static override querySelector: QuerySelector = async (
node,
selector,
{ariaQuerySelector},
) => {
return await ariaQuerySelector(node, selector);
};
static override async *queryAll(
element: ElementHandle<Node>,
selector: string,
): AwaitableIterable<ElementHandle<Node>> {
const {name, role} = parseARIASelector(selector);
yield* element.queryAXTree(name, role);
}
static override queryOne = async (
element: ElementHandle<Node>,
selector: string,
): Promise<ElementHandle<Node> | null> => {
return (
(await AsyncIterableUtil.first(this.queryAll(element, selector))) ?? null
);
};
}

View File

@@ -0,0 +1,174 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {Browser} from '../api/Browser.js';
import {_connectToBiDiBrowser} from '../bidi/BrowserConnector.js';
import {_connectToCdpBrowser} from '../cdp/BrowserConnector.js';
import {environment, isNode} from '../environment.js';
import {assert} from '../util/assert.js';
import {isErrorLike} from '../util/ErrorLike.js';
import type {ConnectionTransport} from './ConnectionTransport.js';
import type {ConnectOptions} from './ConnectOptions.js';
const getWebSocketTransportClass = async () => {
return isNode
? (await import('../node/NodeWebSocketTransport.js')).NodeWebSocketTransport
: (await import('../common/BrowserWebSocketTransport.js'))
.BrowserWebSocketTransport;
};
/**
* Users should never call this directly; it's called when calling
* `puppeteer.connect`. This method attaches Puppeteer to an existing browser instance.
*
* @internal
*/
export async function _connectToBrowser(
options: ConnectOptions,
): Promise<Browser> {
const {connectionTransport, endpointUrl} =
await getConnectionTransport(options);
if (options.protocol === 'webDriverBiDi') {
const bidiBrowser = await _connectToBiDiBrowser(
connectionTransport,
endpointUrl,
options,
);
return bidiBrowser;
} else {
const cdpBrowser = await _connectToCdpBrowser(
connectionTransport,
endpointUrl,
options,
);
return cdpBrowser;
}
}
/**
* Establishes a websocket connection by given options and returns both transport and
* endpoint url the transport is connected to.
*/
async function getConnectionTransport(
options: ConnectOptions,
): Promise<{connectionTransport: ConnectionTransport; endpointUrl: string}> {
const {
browserWSEndpoint,
browserURL,
channel,
transport,
headers = {},
} = options;
assert(
Number(!!browserWSEndpoint) +
Number(!!browserURL) +
Number(!!transport) +
Number(!!channel) ===
1,
'Exactly one of browserWSEndpoint, browserURL, transport or channel must be passed to puppeteer.connect',
);
if (transport) {
return {connectionTransport: transport, endpointUrl: ''};
} else if (browserWSEndpoint) {
const WebSocketClass = await getWebSocketTransportClass();
const connectionTransport: ConnectionTransport =
await WebSocketClass.create(browserWSEndpoint, headers);
return {
connectionTransport: connectionTransport,
endpointUrl: browserWSEndpoint,
};
} else if (browserURL) {
const connectionURL = await getWSEndpoint(browserURL);
const WebSocketClass = await getWebSocketTransportClass();
const connectionTransport: ConnectionTransport =
await WebSocketClass.create(connectionURL);
return {
connectionTransport: connectionTransport,
endpointUrl: connectionURL,
};
} else if (options.channel && isNode) {
const {detectBrowserPlatform, resolveDefaultUserDataDir, Browser} =
await import('@puppeteer/browsers');
const platform = detectBrowserPlatform();
if (!platform) {
throw new Error('Could not detect required browser platform');
}
const {convertPuppeteerChannelToBrowsersChannel} =
await import('../node/LaunchOptions.js');
const {join} = await import('node:path');
const userDataDir = resolveDefaultUserDataDir(
Browser.CHROME,
platform,
convertPuppeteerChannelToBrowsersChannel(options.channel),
);
const portPath = join(userDataDir, 'DevToolsActivePort');
try {
const fileContent = await environment.value.fs.promises.readFile(
portPath,
'ascii',
);
const [rawPort, rawPath] = fileContent
.split('\n')
.map(line => {
return line.trim();
})
.filter(line => {
return !!line;
});
if (!rawPort || !rawPath) {
throw new Error(`Invalid DevToolsActivePort '${fileContent}' found`);
}
const port = parseInt(rawPort, 10);
if (isNaN(port) || port <= 0 || port > 65535) {
throw new Error(`Invalid port '${rawPort}' found`);
}
const browserWSEndpoint = `ws://localhost:${port}${rawPath}`;
const WebSocketClass = await getWebSocketTransportClass();
const connectionTransport = await WebSocketClass.create(
browserWSEndpoint,
headers,
);
return {
connectionTransport: connectionTransport,
endpointUrl: browserWSEndpoint,
};
} catch (error) {
throw new Error(
`Could not find DevToolsActivePort for ${options.channel} at ${portPath}`,
{
cause: error,
},
);
}
}
throw new Error('Invalid connection options');
}
async function getWSEndpoint(browserURL: string): Promise<string> {
const endpointURL = new URL('/json/version', browserURL);
try {
const result = await globalThis.fetch(endpointURL.toString(), {
method: 'GET',
});
if (!result.ok) {
throw new Error(`HTTP ${result.statusText}`);
}
const data = await result.json();
return data.webSocketDebuggerUrl;
} catch (error) {
if (isErrorLike(error)) {
error.message =
`Failed to fetch browser webSocket URL from ${endpointURL}: ` +
error.message;
}
throw error;
}
}

View File

@@ -0,0 +1,50 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {ConnectionTransport} from './ConnectionTransport.js';
/**
* @internal
*/
export class BrowserWebSocketTransport implements ConnectionTransport {
static create(url: string): Promise<BrowserWebSocketTransport> {
return new Promise((resolve, reject) => {
const ws = new WebSocket(url);
ws.addEventListener('open', () => {
return resolve(new BrowserWebSocketTransport(ws));
});
ws.addEventListener('error', reject);
});
}
#ws: WebSocket;
onmessage?: (message: string) => void;
onclose?: () => void;
constructor(ws: WebSocket) {
this.#ws = ws;
this.#ws.addEventListener('message', event => {
if (this.onmessage) {
this.onmessage.call(null, event.data);
}
});
this.#ws.addEventListener('close', () => {
if (this.onclose) {
this.onclose.call(null);
}
});
// Silently ignore all errors - we don't know what to do with them.
this.#ws.addEventListener('error', () => {});
}
send(message: string): void {
this.#ws.send(message);
}
close(): void {
this.#ws.close();
}
}

View File

@@ -0,0 +1,29 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {PuppeteerInjectedUtil} from '../injected/injected.js';
import {QueryHandler} from './QueryHandler.js';
/**
* @internal
*/
export class CSSQueryHandler extends QueryHandler {
static override querySelector = (
element: Node,
selector: string,
{cssQuerySelector}: PuppeteerInjectedUtil,
): Node | null => {
return cssQuerySelector(element, selector);
};
static override querySelectorAll = (
element: Node,
selector: string,
{cssQuerySelectorAll}: PuppeteerInjectedUtil,
): Iterable<Node> => {
return cssQuerySelectorAll(element, selector);
};
}

View File

@@ -0,0 +1,174 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {Deferred} from '../util/Deferred.js';
import {rewriteError} from '../util/ErrorLike.js';
import type {GetIdFn} from '../util/incremental-id-generator.js';
import {ProtocolError, TargetCloseError} from './Errors.js';
import {debugError} from './util.js';
/**
* Manages callbacks and their IDs for the protocol request/response communication.
*
* @internal
*/
export class CallbackRegistry {
readonly #callbacks = new Map<number, Callback>();
readonly #idGenerator: GetIdFn;
constructor(idGenerator: GetIdFn) {
this.#idGenerator = idGenerator;
}
create(
label: string,
timeout: number | undefined,
request: (id: number) => void,
): Promise<unknown> {
const callback = new Callback(this.#idGenerator(), label, timeout);
this.#callbacks.set(callback.id, callback);
try {
request(callback.id);
} catch (error) {
// We still throw sync errors synchronously and clean up the scheduled
// callback.
callback.promise.catch(debugError).finally(() => {
this.#callbacks.delete(callback.id);
});
callback.reject(error as Error);
throw error;
}
// Must only have sync code up until here.
return callback.promise.finally(() => {
this.#callbacks.delete(callback.id);
});
}
reject(id: number, message: string, originalMessage?: string): void {
const callback = this.#callbacks.get(id);
if (!callback) {
return;
}
this._reject(callback, message, originalMessage);
}
rejectRaw(id: number, error: object): void {
const callback = this.#callbacks.get(id);
if (!callback) {
return;
}
callback.reject(error as any);
}
_reject(
callback: Callback,
errorMessage: string | ProtocolError,
originalMessage?: string,
): void {
let error: ProtocolError;
let message: string;
if (errorMessage instanceof ProtocolError) {
error = errorMessage;
error.cause = callback.error;
message = errorMessage.message;
} else {
error = callback.error;
message = errorMessage;
}
callback.reject(
rewriteError(
error,
`Protocol error (${callback.label}): ${message}`,
originalMessage,
),
);
}
resolve(id: number, value: unknown): void {
const callback = this.#callbacks.get(id);
if (!callback) {
return;
}
callback.resolve(value);
}
clear(): void {
for (const callback of this.#callbacks.values()) {
// TODO: probably we can accept error messages as params.
this._reject(callback, new TargetCloseError('Target closed'));
}
this.#callbacks.clear();
}
/**
* @internal
*/
getPendingProtocolErrors(): Error[] {
const result: Error[] = [];
for (const callback of this.#callbacks.values()) {
result.push(
new Error(
`${callback.label} timed out. Trace: ${callback.error.stack}`,
),
);
}
return result;
}
}
/**
* @internal
*/
export class Callback {
#id: number;
#error = new ProtocolError();
#deferred = Deferred.create<unknown>();
#timer?: ReturnType<typeof setTimeout>;
#label: string;
constructor(id: number, label: string, timeout?: number) {
this.#id = id;
this.#label = label;
if (timeout) {
this.#timer = setTimeout(() => {
this.#deferred.reject(
rewriteError(
this.#error,
`${label} timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed.`,
),
);
}, timeout);
}
}
resolve(value: unknown): void {
clearTimeout(this.#timer);
this.#deferred.resolve(value);
}
reject(error: Error): void {
clearTimeout(this.#timer);
this.#deferred.reject(error);
}
get id(): number {
return this.#id;
}
get promise(): Promise<unknown> {
return this.#deferred.valueOrThrow();
}
get error(): ProtocolError {
return this.#error;
}
get label(): string {
return this.#label;
}
}

199
node_modules/puppeteer-core/src/common/Configuration.ts generated vendored Normal file
View File

@@ -0,0 +1,199 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {SupportedBrowser} from './SupportedBrowser.js';
/**
* Defines experiment options for Puppeteer.
*
* See individual properties for more information.
*
* @public
*/
export type ExperimentsConfiguration = Record<string, never>;
/**
* Defines options to configure Puppeteer's behavior during installation and
* runtime.
*
* See individual properties for more information.
*
* @public
*/
export interface Configuration {
/**
* Defines the directory to be used by Puppeteer for caching.
*
* Can be overridden by `PUPPETEER_CACHE_DIR`.
*
* @defaultValue `path.join(os.homedir(), '.cache', 'puppeteer')`
*/
cacheDirectory?: string;
/**
* Specifies an executable path to be used in
* {@link PuppeteerNode.launch | puppeteer.launch}.
*
* Can be overridden by `PUPPETEER_EXECUTABLE_PATH`.
*
* @defaultValue **Auto-computed.**
*/
executablePath?: string;
/**
* Specifies which browser you'd like Puppeteer to use.
*
* Can be overridden by `PUPPETEER_BROWSER`.
*
* @defaultValue `chrome`
*/
defaultBrowser?: SupportedBrowser;
/**
* Defines the directory to be used by Puppeteer for creating temporary files.
*
* Can be overridden by `PUPPETEER_TMP_DIR`.
*
* @defaultValue `os.tmpdir()`
*/
temporaryDirectory?: string;
/**
* Tells Puppeteer to not download during installation.
*
* Can be overridden by `PUPPETEER_SKIP_DOWNLOAD`.
*/
skipDownload?: boolean;
/**
* Tells Puppeteer to log at the given level.
*
* @defaultValue `warn`
*/
logLevel?: 'silent' | 'error' | 'warn';
/**
* Defines experimental options for Puppeteer.
*/
experiments?: ExperimentsConfiguration;
chrome?: ChromeSettings;
['chrome-headless-shell']?: ChromeHeadlessShellSettings;
firefox?: FirefoxSettings;
}
/**
* @public
*/
export interface ChromeSettings {
/**
* Tells Puppeteer to not download the browser during installation.
*
* Can be overridden by `PUPPETEER_CHROME_SKIP_DOWNLOAD`.
*
* @defaultValue false
*/
skipDownload?: boolean;
/**
* Specifies the URL prefix that is used to download the browser.
*
* Can be overridden by `PUPPETEER_CHROME_DOWNLOAD_BASE_URL`.
*
* @remarks
* This must include the protocol and may even need a path prefix.
* This must **not** include a trailing slash similar to the default.
*
* @defaultValue https://storage.googleapis.com/chrome-for-testing-public
*/
downloadBaseUrl?: string;
/**
* Specifies a certain version of the browser you'd like Puppeteer to use.
*
* Can be overridden by `PUPPETEER_CHROME_VERSION`
* or `PUPPETEER_SKIP_CHROME_DOWNLOAD`.
*
* See {@link PuppeteerNode.launch | puppeteer.launch} on how executable path
* is inferred.
*
* @example 119.0.6045.105
* @defaultValue The pinned browser version supported by the current Puppeteer
* version.
*/
version?: string;
}
/**
* @public
*/
export interface ChromeHeadlessShellSettings {
/**
* Tells Puppeteer to not download the browser during installation.
*
* Can be overridden by `PUPPETEER_CHROME_HEADLESS_SHELL_SKIP_DOWNLOAD`
* or `PUPPETEER_SKIP_CHROME_HEADLESS_SHELL_DOWNLOAD`.
*
* @defaultValue false
*/
skipDownload?: boolean;
/**
* Specifies the URL prefix that is used to download the browser.
*
* Can be overridden by `PUPPETEER_CHROME_HEADLESS_SHELL_DOWNLOAD_BASE_URL`.
*
* @remarks
* This must include the protocol and may even need a path prefix.
* This must **not** include a trailing slash similar to the default.
*
* @defaultValue https://storage.googleapis.com/chrome-for-testing-public
*/
downloadBaseUrl?: string;
/**
* Specifies a certain version of the browser you'd like Puppeteer to use.
*
* Can be overridden by `PUPPETEER_CHROME_HEADLESS_SHELL_VERSION`.
*
* See {@link PuppeteerNode.launch | puppeteer.launch} on how executable path
* is inferred.
*
* @example 119.0.6045.105
* @defaultValue The pinned browser version supported by the current Puppeteer
* version.
*/
version?: string;
}
/**
* @public
*/
export interface FirefoxSettings {
/**
* Tells Puppeteer to not download the browser during installation.
*
* Can be overridden by `PUPPETEER_FIREFOX_SKIP_DOWNLOAD`.
*
* @defaultValue true
*/
skipDownload?: boolean;
/**
* Specifies the URL prefix that is used to download the browser.
*
* Can be overridden by `PUPPETEER_FIREFOX_DOWNLOAD_BASE_URL`.
*
* @remarks
* This must include the protocol and may even need a path prefix.
* This must **not** include a trailing slash similar to the default.
*
* @defaultValue https://archive.mozilla.org/pub/firefox/releases
*/
downloadBaseUrl?: string;
/**
* Specifies a certain version of the browser you'd like Puppeteer to use.
*
* Can be overridden by `PUPPETEER_FIREFOX_VERSION`.
*
* See {@link PuppeteerNode.launch | puppeteer.launch} on how executable path
* is inferred.
*
* @example stable_129.0
* @defaultValue The pinned browser version supported by the current Puppeteer
* version.
*/
version?: string;
}

View File

@@ -0,0 +1,157 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {Session} from 'webdriver-bidi-protocol';
import type {
IsPageTargetCallback,
TargetFilterCallback,
} from '../api/Browser.js';
import type {ConnectionTransport} from './ConnectionTransport.js';
import type {DownloadBehavior} from './DownloadBehavior.js';
import type {Viewport} from './Viewport.js';
/**
* @public
*/
export type ProtocolType = 'cdp' | 'webDriverBiDi';
/**
* @public
*/
export type SupportedWebDriverCapability = Exclude<
Session.CapabilityRequest,
'unhandledPromptBehavior' | 'acceptInsecureCerts'
>;
/**
* WebDriver BiDi capabilities that are not set by Puppeteer itself.
*
* @public
*/
export interface SupportedWebDriverCapabilities {
firstMatch?: SupportedWebDriverCapability[];
alwaysMatch?: SupportedWebDriverCapability;
}
/**
* @public
*/
export type ChromeReleaseChannel =
| 'chrome'
| 'chrome-beta'
| 'chrome-canary'
| 'chrome-dev';
/**
* Generic browser options that can be passed when launching any browser or when
* connecting to an existing browser instance.
* @public
*/
export interface ConnectOptions {
/**
* Whether to ignore HTTPS errors during navigation.
* @defaultValue `false`
*/
acceptInsecureCerts?: boolean;
/**
* If specified, puppeteer looks for an open WebSocket at the well-known
* default user data directory for the specified channel and attempts to
* connect to it using ws://localhost:$ActivePort/devtools/browser. Only works
* for Chrome and when run in Node.js.
*
* This option is experimental when used with puppeteer.connect().
*
* @experimental
*/
channel?: ChromeReleaseChannel;
/**
* Experimental setting to disable monitoring network events by default. When
* set to `false`, parts of Puppeteer that depend on network events would not
* work such as HTTPRequest and HTTPResponse.
*
* @experimental
* @defaultValue `true`
*/
networkEnabled?: boolean;
/**
* Sets the viewport for each page.
*
* @defaultValue '\{width: 800, height: 600\}'
*/
defaultViewport?: Viewport | null;
/**
* Sets the download behavior for the context.
*/
downloadBehavior?: DownloadBehavior;
/**
* Slows down Puppeteer operations by the specified amount of milliseconds to
* aid debugging.
*/
slowMo?: number;
/**
* Callback to decide if Puppeteer should connect to a given target or not.
*/
targetFilter?: TargetFilterCallback;
/**
* @internal
*/
_isPageTarget?: IsPageTargetCallback;
/**
* Whether to handle the DevTools windows as pages in Puppeteer. Supported
* only in Chrome with CDP.
*
* @defaultValue 'false'
*/
handleDevToolsAsPage?: boolean;
/**
* @defaultValue Determined at run time:
*
* - Launching Chrome - 'cdp'.
*
* - Launching Firefox - 'webDriverBiDi'.
*
* - Connecting to a browser - 'cdp'.
*
* @public
*/
protocol?: ProtocolType;
/**
* Timeout setting for individual protocol (CDP) calls.
*
* @defaultValue `180_000`
*/
protocolTimeout?: number;
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;
/**
* @internal
*
* Custom ID generator for CDP / BiDi messages. Useful if the same transport
* is shared for multiple connections.
*/
idGenerator?: () => number;
/**
* Headers to use for the web socket connection.
* @remarks
* Only works in the Node.js environment.
*/
headers?: Record<string, string>;
/**
* WebDriver BiDi capabilities passed to BiDi `session.new`.
*
* @remarks
* Only works for `protocol="webDriverBiDi"` and {@link Puppeteer.connect}.
*/
capabilities?: SupportedWebDriverCapabilities;
}

View File

@@ -0,0 +1,15 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @public
*/
export interface ConnectionTransport {
send(message: string): void;
close(): void;
onmessage?: (message: string) => void;
onclose?: () => void;
}

View File

@@ -0,0 +1,146 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {Protocol} from 'devtools-protocol';
import type {Frame} from '../api/Frame.js';
import type {JSHandle} from '../api/JSHandle.js';
/**
* @public
*/
export interface ConsoleMessageLocation {
/**
* URL of the resource if known or `undefined` otherwise.
*/
url?: string;
/**
* 0-based line number in the resource if known or `undefined` otherwise.
*/
lineNumber?: number;
/**
* 0-based column number in the resource if known or `undefined` otherwise.
*/
columnNumber?: number;
}
/**
* The supported types for console messages.
* @public
*/
export type ConsoleMessageType =
| 'log'
| 'debug'
| 'info'
| 'error'
| 'warn'
| 'dir'
| 'dirxml'
| 'table'
| 'trace'
| 'clear'
| 'startGroup'
| 'startGroupCollapsed'
| 'endGroup'
| 'assert'
| 'profile'
| 'profileEnd'
| 'count'
| 'timeEnd'
| 'verbose';
/**
* ConsoleMessage objects are dispatched by page via the 'console' event.
* @public
*/
export class ConsoleMessage {
#type: ConsoleMessageType;
#text: string;
#args: JSHandle[];
#stackTraceLocations: ConsoleMessageLocation[];
#frame?: Frame;
#rawStackTrace?: Protocol.Runtime.StackTrace;
#targetId?: string;
/**
* @internal
*/
constructor(
type: ConsoleMessageType,
text: string,
args: JSHandle[],
stackTraceLocations: ConsoleMessageLocation[],
frame?: Frame,
rawStackTrace?: Protocol.Runtime.StackTrace,
targetId?: string,
) {
this.#type = type;
this.#text = text;
this.#args = args;
this.#stackTraceLocations = stackTraceLocations;
this.#frame = frame;
this.#rawStackTrace = rawStackTrace;
this.#targetId = targetId;
}
/**
* The type of the console message.
*/
type(): ConsoleMessageType {
return this.#type;
}
/**
* The text of the console message.
*/
text(): string {
return this.#text;
}
/**
* An array of arguments passed to the console.
*/
args(): JSHandle[] {
return this.#args;
}
/**
* The location of the console message.
*/
location(): ConsoleMessageLocation {
return (
this.#stackTraceLocations[0] ??
(this.#frame ? {url: this.#frame.url()} : {})
);
}
/**
* The array of locations on the stack of the console message.
*/
stackTrace(): ConsoleMessageLocation[] {
return this.#stackTraceLocations;
}
/**
* The underlying protocol stack trace if available.
*
* @internal
*/
_rawStackTrace(): Protocol.Runtime.StackTrace | undefined {
return this.#rawStackTrace;
}
/**
* The targetId from which this console message originated.
*
* @internal
*/
_targetId(): string | undefined {
return this.#targetId;
}
}

241
node_modules/puppeteer-core/src/common/Cookie.ts generated vendored Normal file
View File

@@ -0,0 +1,241 @@
/**
* @license
* Copyright 2024 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Represents the cookie's 'SameSite' status:
* https://tools.ietf.org/html/draft-west-first-party-cookies
*
* @public
*/
export type CookieSameSite = 'Strict' | 'Lax' | 'None' | 'Default';
/**
* Represents the cookie's 'Priority' status:
* https://tools.ietf.org/html/draft-west-cookie-priority-00
*
* @public
*/
export type CookiePriority = 'Low' | 'Medium' | 'High';
/**
* Represents the source scheme of the origin that originally set the cookie. A value of
* "Unset" allows protocol clients to emulate legacy cookie scope for the scheme.
* This is a temporary ability and it will be removed in the future.
*
* @public
*/
export type CookieSourceScheme = 'Unset' | 'NonSecure' | 'Secure';
/**
* Represents a cookie partition key in Chrome.
*
* @public
*/
export interface CookiePartitionKey {
/**
* The site of the top-level URL the browser was visiting at the start of the request
* to the endpoint that set the cookie.
*
* In Chrome, maps to the CDP's `topLevelSite` partition key.
*/
sourceOrigin: string;
/**
* Indicates if the cookie has any ancestors that are cross-site to
* the topLevelSite.
*
* Supported only in Chrome.
*/
hasCrossSiteAncestor?: boolean;
}
/**
* Represents a cookie object.
*
* @public
*/
export interface Cookie extends CookieData {
/**
* Cookie path.
*/
path: string;
/**
* Cookie expiration date as the number of seconds since the UNIX epoch. Set to `-1` for
* session cookies
*/
expires: number;
/**
* Cookie size.
*/
size: number;
/**
* True if cookie is secure.
*/
secure: boolean;
/**
* True in case of session cookie.
*/
session: boolean;
/**
* True if cookie partition key is opaque. Supported only in Chrome.
*/
partitionKeyOpaque?: boolean;
}
/**
* Cookie parameter object used to set cookies in the page-level cookies
* API.
*
* @public
*/
export interface CookieParam {
/**
* Cookie name.
*/
name: string;
/**
* Cookie value.
*/
value: string;
/**
* The request-URI to associate with the setting of the cookie. This value can affect
* the default domain, path, and source scheme values of the created cookie.
*/
url?: string;
/**
* Cookie domain.
*/
domain?: string;
/**
* Cookie path.
*/
path?: string;
/**
* True if cookie is secure.
*/
secure?: boolean;
/**
* True if cookie is http-only.
*/
httpOnly?: boolean;
/**
* Cookie SameSite type.
*/
sameSite?: CookieSameSite;
/**
* Cookie expiration date, session cookie if not set
*/
expires?: number;
/**
* Cookie Priority. Supported only in Chrome.
*/
priority?: CookiePriority;
/**
* @deprecated Always ignored.
*/
sameParty?: boolean;
/**
* Cookie source scheme type. Supported only in Chrome.
*/
sourceScheme?: CookieSourceScheme;
/**
* Cookie partition key. In Chrome, it matches the top-level site the
* partitioned cookie is available in. In Firefox, it matches the
* source origin in the
* {@link https://w3c.github.io/webdriver-bidi/#type-storage-PartitionKey | PartitionKey }.
*/
partitionKey?: CookiePartitionKey | string;
}
/**
* Cookie parameter object used to set cookies in the browser-level cookies
* API.
*
* @public
*/
export interface CookieData {
/**
* Cookie name.
*/
name: string;
/**
* Cookie value.
*/
value: string;
/**
* Cookie domain.
*/
domain: string;
/**
* Cookie path.
*/
path?: string;
/**
* True if cookie is secure.
*/
secure?: boolean;
/**
* True if cookie is http-only.
*/
httpOnly?: boolean;
/**
* Cookie SameSite type.
*/
sameSite?: CookieSameSite;
/**
* Cookie expiration date, session cookie if not set
*/
expires?: number;
/**
* Cookie Priority. Supported only in Chrome.
*/
priority?: CookiePriority;
/**
* @deprecated Always set to false. Supported only in Chrome.
*/
sameParty?: boolean;
/**
* Cookie source scheme type. Supported only in Chrome.
*/
sourceScheme?: CookieSourceScheme;
/**
* Cookie partition key. In Chrome, it matches the top-level site the
* partitioned cookie is available in. In Firefox, it matches the
* source origin in the
* {@link https://w3c.github.io/webdriver-bidi/#type-storage-PartitionKey | PartitionKey }.
*/
partitionKey?: CookiePartitionKey | string;
}
/**
* @public
*/
export interface DeleteCookiesRequest {
/**
* Name of the cookies to remove.
*/
name: string;
/**
* If specified, deletes all the cookies with the given name where domain and path match
* provided URL. Otherwise, deletes only cookies related to the current page's domain.
*/
url?: string;
/**
* If specified, deletes only cookies with the exact domain.
*/
domain?: string;
/**
* If specified, deletes only cookies with the exact path.
*/
path?: string;
/**
* If specified, deletes cookies in the given partition key. In
* Chrome, partitionKey matches the top-level site the partitioned
* cookie is available in.
* In Firefox, it matches the source origin in the
* {@link https://w3c.github.io/webdriver-bidi/#type-storage-PartitionKey | PartitionKey }.
*/
partitionKey?: CookiePartitionKey | string;
}

View File

@@ -0,0 +1,164 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {PuppeteerInjectedUtil} from '../injected/injected.js';
import {assert} from '../util/assert.js';
import {interpolateFunction, stringifyFunction} from '../util/Function.js';
import {
QueryHandler,
type QuerySelector,
type QuerySelectorAll,
} from './QueryHandler.js';
import {scriptInjector} from './ScriptInjector.js';
/**
* @public
*/
export interface CustomQueryHandler {
/**
* Searches for a {@link https://developer.mozilla.org/en-US/docs/Web/API/Node | Node} matching the given `selector` from {@link https://developer.mozilla.org/en-US/docs/Web/API/Node | node}.
*/
queryOne?: (node: Node, selector: string) => Node | null;
/**
* Searches for some {@link https://developer.mozilla.org/en-US/docs/Web/API/Node | Nodes} matching the given `selector` from {@link https://developer.mozilla.org/en-US/docs/Web/API/Node | node}.
*/
queryAll?: (node: Node, selector: string) => Iterable<Node>;
}
/**
* The registry of {@link CustomQueryHandler | custom query handlers}.
*
* @example
*
* ```ts
* Puppeteer.customQueryHandlers.register('lit', { … });
* const aHandle = await page.$('lit/…');
* ```
*
* @internal
*/
export class CustomQueryHandlerRegistry {
#handlers = new Map<
string,
[registerScript: string, Handler: typeof QueryHandler]
>();
get(name: string): typeof QueryHandler | undefined {
const handler = this.#handlers.get(name);
return handler ? handler[1] : undefined;
}
/**
* Registers a {@link CustomQueryHandler | custom query handler}.
*
* @remarks
* After registration, the handler can be used everywhere where a selector is
* expected by prepending the selection string with `<name>/`. The name is
* only allowed to consist of lower- and upper case latin letters.
*
* @example
*
* ```ts
* Puppeteer.customQueryHandlers.register('lit', { … });
* const aHandle = await page.$('lit/…');
* ```
*
* @param name - Name to register under.
* @param queryHandler - {@link CustomQueryHandler | Custom query handler} to
* register.
*/
register(name: string, handler: CustomQueryHandler): void {
assert(
!this.#handlers.has(name),
`Cannot register over existing handler: ${name}`,
);
assert(
/^[a-zA-Z]+$/.test(name),
`Custom query handler names may only contain [a-zA-Z]`,
);
assert(
handler.queryAll || handler.queryOne,
`At least one query method must be implemented.`,
);
const Handler = class extends QueryHandler {
static override querySelectorAll: QuerySelectorAll = interpolateFunction(
(node, selector, PuppeteerUtil) => {
return PuppeteerUtil.customQuerySelectors
.get(PLACEHOLDER('name'))!
.querySelectorAll(node, selector);
},
{name: JSON.stringify(name)},
);
static override querySelector: QuerySelector = interpolateFunction(
(node, selector, PuppeteerUtil) => {
return PuppeteerUtil.customQuerySelectors
.get(PLACEHOLDER('name'))!
.querySelector(node, selector);
},
{name: JSON.stringify(name)},
);
};
const registerScript = interpolateFunction(
(PuppeteerUtil: PuppeteerInjectedUtil) => {
PuppeteerUtil.customQuerySelectors.register(PLACEHOLDER('name'), {
queryAll: PLACEHOLDER('queryAll'),
queryOne: PLACEHOLDER('queryOne'),
});
},
{
name: JSON.stringify(name),
queryAll: handler.queryAll
? stringifyFunction(handler.queryAll)
: String(undefined),
queryOne: handler.queryOne
? stringifyFunction(handler.queryOne)
: String(undefined),
},
).toString();
this.#handlers.set(name, [registerScript, Handler]);
scriptInjector.append(registerScript);
}
/**
* Unregisters the {@link CustomQueryHandler | custom query handler} for the
* given name.
*
* @throws `Error` if there is no handler under the given name.
*/
unregister(name: string): void {
const handler = this.#handlers.get(name);
if (!handler) {
throw new Error(`Cannot unregister unknown handler: ${name}`);
}
scriptInjector.pop(handler[0]);
this.#handlers.delete(name);
}
/**
* Gets the names of all {@link CustomQueryHandler | custom query handlers}.
*/
names(): string[] {
return [...this.#handlers.keys()];
}
/**
* Unregisters all custom query handlers.
*/
clear(): void {
for (const [registerScript] of this.#handlers) {
scriptInjector.pop(registerScript);
}
this.#handlers.clear();
}
}
/**
* @internal
*/
export const customQueryHandlers = new CustomQueryHandlerRegistry();

126
node_modules/puppeteer-core/src/common/Debug.ts generated vendored Normal file
View File

@@ -0,0 +1,126 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type Debug from 'debug';
import {isNode} from '../environment.js';
declare global {
const __PUPPETEER_DEBUG: string;
}
/**
* @internal
*/
let debugModule: typeof Debug | null = null;
/**
* @internal
*/
export async function importDebug(): Promise<typeof Debug> {
if (!debugModule) {
debugModule = (await import('debug')).default;
}
return debugModule;
}
/**
* A debug function that can be used in any environment.
*
* @remarks
* If used in Node, it falls back to the
* {@link https://www.npmjs.com/package/debug | debug module}. In the browser it
* uses `console.log`.
*
* In Node, use the `DEBUG` environment variable to control logging:
*
* ```
* DEBUG=* // logs all channels
* DEBUG=foo // logs the `foo` channel
* DEBUG=foo* // logs any channels starting with `foo`
* ```
*
* In the browser, set `window.__PUPPETEER_DEBUG` to a string:
*
* ```
* window.__PUPPETEER_DEBUG='*'; // logs all channels
* window.__PUPPETEER_DEBUG='foo'; // logs the `foo` channel
* window.__PUPPETEER_DEBUG='foo*'; // logs any channels starting with `foo`
* ```
*
* @example
*
* ```
* const log = debug('Page');
*
* log('new page created')
* // logs "Page: new page created"
* ```
*
* @param prefix - this will be prefixed to each log.
* @returns a function that can be called to log to that debug channel.
*
* @internal
*/
export const debug = (prefix: string): ((...args: unknown[]) => void) => {
if (isNode) {
return async (...logArgs: unknown[]) => {
if (captureLogs) {
capturedLogs.push(prefix + logArgs);
}
(await importDebug())(prefix)(logArgs);
};
}
return (...logArgs: unknown[]): void => {
const debugLevel = (globalThis as any).__PUPPETEER_DEBUG;
if (!debugLevel) {
return;
}
const everythingShouldBeLogged = debugLevel === '*';
const prefixMatchesDebugLevel =
everythingShouldBeLogged ||
/**
* If the debug level is `foo*`, that means we match any prefix that
* starts with `foo`. If the level is `foo`, we match only the prefix
* `foo`.
*/
(debugLevel.endsWith('*')
? prefix.startsWith(debugLevel)
: prefix === debugLevel);
if (!prefixMatchesDebugLevel) {
return;
}
console.log(`${prefix}:`, ...logArgs);
};
};
/**
* @internal
*/
let capturedLogs: string[] = [];
/**
* @internal
*/
let captureLogs = false;
/**
* @internal
*/
export function setLogCapture(value: boolean): void {
capturedLogs = [];
captureLogs = value;
}
/**
* @internal
*/
export function getCapturedLogs(): string[] {
return capturedLogs;
}

1751
node_modules/puppeteer-core/src/common/Device.ts generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
/**
* @license
* Copyright 2024 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @public
*/
export type DownloadPolicy = 'deny' | 'allow' | 'allowAndName' | 'default';
/**
* @public
*/
export interface DownloadBehavior {
/**
* Whether to allow all or deny all download requests, or use default behavior if
* available.
*
* @remarks
* Setting this to `allowAndName` will name all files according to their download guids.
*/
policy: DownloadPolicy;
/**
* The default path to save downloaded files to.
*
* @remarks
* Setting this is required if behavior is set to `allow` or `allowAndName`.
*/
downloadPath?: string;
}

98
node_modules/puppeteer-core/src/common/Errors.ts generated vendored Normal file
View File

@@ -0,0 +1,98 @@
/**
* @license
* Copyright 2018 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* The base class for all Puppeteer-specific errors
*
* @public
*/
export class PuppeteerError extends Error {
/**
* @internal
*/
constructor(message?: string, options?: ErrorOptions) {
super(message, options);
this.name = this.constructor.name;
}
/**
* @internal
*/
get [Symbol.toStringTag](): string {
return this.constructor.name;
}
}
/**
* TimeoutError is emitted whenever certain operations are terminated due to
* timeout.
*
* @remarks
* Example operations are {@link Page.waitForSelector | page.waitForSelector} or
* {@link PuppeteerNode.launch | puppeteer.launch}.
*
* @public
*/
export class TimeoutError extends PuppeteerError {}
/**
* TouchError is thrown when an attempt is made to move or end a touch that does
* not exist.
* @public
*/
export class TouchError extends PuppeteerError {}
/**
* ProtocolError is emitted whenever there is an error from the protocol.
*
* @public
*/
export class ProtocolError extends PuppeteerError {
#code?: number;
#originalMessage = '';
set code(code: number | undefined) {
this.#code = code;
}
/**
* @readonly
* @public
*/
get code(): number | undefined {
return this.#code;
}
set originalMessage(originalMessage: string) {
this.#originalMessage = originalMessage;
}
/**
* @readonly
* @public
*/
get originalMessage(): string {
return this.#originalMessage;
}
}
/**
* Puppeteer will throw this error if a method is not
* supported by the currently used protocol
*
* @public
*/
export class UnsupportedOperation extends PuppeteerError {}
/**
* @internal
*/
export class TargetCloseError extends ProtocolError {}
/**
* Thrown if underlying protocol connection has been closed.
*
* @public
*/
export class ConnectionClosedError extends ProtocolError {}

193
node_modules/puppeteer-core/src/common/EventEmitter.ts generated vendored Normal file
View File

@@ -0,0 +1,193 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import mitt, {type Emitter} from '../../third_party/mitt/mitt.js';
import {disposeSymbol} from '../util/disposable.js';
/**
* @public
*/
export type EventType = string | symbol;
/**
* @public
*/
export type Handler<T = unknown> = (event: T) => void;
/**
* @public
*/
export interface CommonEventEmitter<Events extends Record<EventType, unknown>> {
on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): this;
off<Key extends keyof Events>(
type: Key,
handler?: Handler<Events[Key]>,
): this;
emit<Key extends keyof Events>(type: Key, event: Events[Key]): boolean;
once<Key extends keyof Events>(
type: Key,
handler: Handler<Events[Key]>,
): this;
listenerCount(event: keyof Events): number;
removeAllListeners(event?: keyof Events): this;
}
/**
* @public
*/
export type EventsWithWildcard<Events extends Record<EventType, unknown>> =
Events & {
'*': Events[keyof Events];
};
/**
* The EventEmitter class that many Puppeteer classes extend.
*
* @remarks
*
* This allows you to listen to events that Puppeteer classes fire and act
* accordingly. Therefore you'll mostly use {@link EventEmitter.on | on} and
* {@link EventEmitter.off | off} to bind
* and unbind to event listeners.
*
* @public
*/
export class EventEmitter<
Events extends Record<EventType, unknown>,
> implements CommonEventEmitter<EventsWithWildcard<Events>> {
#emitter: Emitter<EventsWithWildcard<Events>> | EventEmitter<Events>;
#handlers = new Map<keyof Events | '*', Array<Handler<any>>>();
/**
* If you pass an emitter, the returned emitter will wrap the passed emitter.
*
* @internal
*/
constructor(
emitter: Emitter<EventsWithWildcard<Events>> | EventEmitter<Events> = mitt(
new Map(),
),
) {
this.#emitter = emitter;
}
/**
* Bind an event listener to fire when an event occurs.
* @param type - the event type you'd like to listen to. Can be a string or symbol.
* @param handler - the function to be called when the event occurs.
* @returns `this` to enable you to chain method calls.
*/
on<Key extends keyof EventsWithWildcard<Events>>(
type: Key,
handler: Handler<EventsWithWildcard<Events>[Key]>,
): this {
const handlers = this.#handlers.get(type);
if (handlers === undefined) {
this.#handlers.set(type, [handler]);
} else {
handlers.push(handler);
}
this.#emitter.on(type, handler);
return this;
}
/**
* Remove an event listener from firing.
* @param type - the event type you'd like to stop listening to.
* @param handler - the function that should be removed.
* @returns `this` to enable you to chain method calls.
*/
off<Key extends keyof EventsWithWildcard<Events>>(
type: Key,
handler?: Handler<EventsWithWildcard<Events>[Key]>,
): this {
const handlers = this.#handlers.get(type) ?? [];
if (handler === undefined) {
for (const handler of handlers) {
this.#emitter.off(type, handler);
}
this.#handlers.delete(type);
return this;
}
const index = handlers.lastIndexOf(handler);
if (index > -1) {
this.#emitter.off(type, ...handlers.splice(index, 1));
}
return this;
}
/**
* Emit an event and call any associated listeners.
*
* @param type - the event you'd like to emit
* @param eventData - any data you'd like to emit with the event
* @returns `true` if there are any listeners, `false` if there are not.
*/
emit<Key extends keyof EventsWithWildcard<Events>>(
type: Key,
event: EventsWithWildcard<Events>[Key],
): boolean {
this.#emitter.emit(type, event);
return this.listenerCount(type) > 0;
}
/**
* Like `on` but the listener will only be fired once and then it will be removed.
* @param type - the event you'd like to listen to
* @param handler - the handler function to run when the event occurs
* @returns `this` to enable you to chain method calls.
*/
once<Key extends keyof EventsWithWildcard<Events>>(
type: Key,
handler: Handler<EventsWithWildcard<Events>[Key]>,
): this {
const onceHandler: Handler<EventsWithWildcard<Events>[Key]> = eventData => {
handler(eventData);
this.off(type, onceHandler);
};
return this.on(type, onceHandler);
}
/**
* Gets the number of listeners for a given event.
*
* @param type - the event to get the listener count for
* @returns the number of listeners bound to the given event
*/
listenerCount(type: keyof EventsWithWildcard<Events>): number {
return this.#handlers.get(type)?.length || 0;
}
/**
* Removes all listeners. If given an event argument, it will remove only
* listeners for that event.
*
* @param type - the event to remove listeners for.
* @returns `this` to enable you to chain method calls.
*/
removeAllListeners(type?: keyof EventsWithWildcard<Events>): this {
if (type !== undefined) {
return this.off(type);
}
this[disposeSymbol]();
return this;
}
/**
* @internal
*/
[disposeSymbol](): void {
for (const [type, handlers] of this.#handlers) {
for (const handler of handlers) {
this.#emitter.off(type, handler);
}
}
this.#handlers.clear();
}
}

87
node_modules/puppeteer-core/src/common/FileChooser.ts generated vendored Normal file
View File

@@ -0,0 +1,87 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {ElementHandle} from '../api/ElementHandle.js';
import {assert} from '../util/assert.js';
/**
* File choosers let you react to the page requesting for a file.
*
* @remarks
* `FileChooser` instances are returned via the {@link Page.waitForFileChooser} method.
*
* In browsers, only one file chooser can be opened at a time.
* All file choosers must be accepted or canceled. Not doing so will prevent
* subsequent file choosers from appearing.
*
* @example
*
* ```ts
* const [fileChooser] = await Promise.all([
* page.waitForFileChooser(),
* page.click('#upload-file-button'), // some button that triggers file selection
* ]);
* await fileChooser.accept(['/tmp/myfile.pdf']);
* ```
*
* @public
*/
export class FileChooser {
#element: ElementHandle<HTMLInputElement>;
#multiple: boolean;
#handled = false;
/**
* @internal
*/
constructor(element: ElementHandle<HTMLInputElement>, multiple: boolean) {
this.#element = element;
this.#multiple = multiple;
}
/**
* Whether file chooser allow for
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#attr-multiple | multiple}
* file selection.
*/
isMultiple(): boolean {
return this.#multiple;
}
/**
* Accept the file chooser request with the given file paths.
*
* @remarks This will not validate whether the file paths exists. Also, if a
* path is relative, then it is resolved against the
* {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory}.
* For locals script connecting to remote chrome environments, paths must be
* absolute.
*/
async accept(paths: string[]): Promise<void> {
assert(
!this.#handled,
'Cannot accept FileChooser which is already handled!',
);
this.#handled = true;
await this.#element.uploadFile(...paths);
}
/**
* Closes the file chooser without selecting any files.
*/
async cancel(): Promise<void> {
assert(
!this.#handled,
'Cannot cancel FileChooser which is already handled!',
);
this.#handled = true;
// XXX: These events should converted to trusted events. Perhaps do this
// in `DOM.setFileInputFiles`?
await this.#element.evaluate(element => {
element.dispatchEvent(new Event('cancel', {bubbles: true}));
});
}
}

View File

@@ -0,0 +1,80 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {ARIAQueryHandler} from './AriaQueryHandler.js';
import {CSSQueryHandler} from './CSSQueryHandler.js';
import {customQueryHandlers} from './CustomQueryHandler.js';
import {PierceQueryHandler} from './PierceQueryHandler.js';
import {PQueryHandler} from './PQueryHandler.js';
import {parsePSelectors} from './PSelectorParser.js';
import type {QueryHandler} from './QueryHandler.js';
import {PollingOptions} from './QueryHandler.js';
import {TextQueryHandler} from './TextQueryHandler.js';
import {XPathQueryHandler} from './XPathQueryHandler.js';
const BUILTIN_QUERY_HANDLERS = {
aria: ARIAQueryHandler,
pierce: PierceQueryHandler,
xpath: XPathQueryHandler,
text: TextQueryHandler,
} as const;
const QUERY_SEPARATORS = ['=', '/'];
/**
* @internal
*/
export function getQueryHandlerAndSelector(selector: string): {
updatedSelector: string;
polling: PollingOptions;
QueryHandler: typeof QueryHandler;
} {
for (const handlerMap of [
customQueryHandlers.names().map(name => {
return [name, customQueryHandlers.get(name)!] as const;
}),
Object.entries(BUILTIN_QUERY_HANDLERS),
]) {
for (const [name, QueryHandler] of handlerMap) {
for (const separator of QUERY_SEPARATORS) {
const prefix = `${name}${separator}`;
if (selector.startsWith(prefix)) {
selector = selector.slice(prefix.length);
return {
updatedSelector: selector,
polling:
name === 'aria' ? PollingOptions.RAF : PollingOptions.MUTATION,
QueryHandler,
};
}
}
}
}
try {
const [pSelector, isPureCSS, hasPseudoClasses, hasAria] =
parsePSelectors(selector);
if (isPureCSS) {
return {
updatedSelector: selector,
polling: hasPseudoClasses
? PollingOptions.RAF
: PollingOptions.MUTATION,
QueryHandler: CSSQueryHandler,
};
}
return {
updatedSelector: JSON.stringify(pSelector),
polling: hasAria ? PollingOptions.RAF : PollingOptions.MUTATION,
QueryHandler: PQueryHandler,
};
} catch {
return {
updatedSelector: selector,
polling: PollingOptions.MUTATION,
QueryHandler: CSSQueryHandler,
};
}
}

View File

@@ -0,0 +1,76 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {JSHandle} from '../api/JSHandle.js';
import {DisposableStack, disposeSymbol} from '../util/disposable.js';
import type {AwaitableIterable, HandleFor} from './types.js';
const DEFAULT_BATCH_SIZE = 20;
/**
* This will transpose an iterator JSHandle into a fast, Puppeteer-side iterator
* of JSHandles.
*
* @param size - The number of elements to transpose. This should be something
* reasonable.
*/
async function* fastTransposeIteratorHandle<T>(
iterator: JSHandle<AwaitableIterator<T>>,
size: number,
) {
using array = await iterator.evaluateHandle(async (iterator, size) => {
const results = [];
while (results.length < size) {
const result = await iterator.next();
if (result.done) {
break;
}
results.push(result.value);
}
return results;
}, size);
const properties = (await array.getProperties()) as Map<string, HandleFor<T>>;
const handles = properties.values();
using stack = new DisposableStack();
stack.defer(() => {
for (using handle of handles) {
handle[disposeSymbol]();
}
});
yield* handles;
return properties.size === 0;
}
/**
* This will transpose an iterator JSHandle in batches based on the default size
* of {@link fastTransposeIteratorHandle}.
*/
async function* transposeIteratorHandle<T>(
iterator: JSHandle<AwaitableIterator<T>>,
) {
let size = DEFAULT_BATCH_SIZE;
while (!(yield* fastTransposeIteratorHandle(iterator, size))) {
size <<= 1;
}
}
type AwaitableIterator<T> = Iterator<T> | AsyncIterator<T>;
/**
* @internal
*/
export async function* transposeIterableHandle<T>(
handle: JSHandle<AwaitableIterable<T>>,
): AsyncIterableIterator<HandleFor<T>> {
using generatorHandle = await handle.evaluateHandle(iterable => {
return (async function* () {
yield* iterable;
})();
});
yield* transposeIteratorHandle(generatorHandle);
}

37
node_modules/puppeteer-core/src/common/LazyArg.ts generated vendored Normal file
View File

@@ -0,0 +1,37 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {JSHandle} from '../api/JSHandle.js';
import type {PuppeteerInjectedUtil} from '../injected/injected.js';
/**
* @internal
*/
export interface PuppeteerUtilWrapper {
puppeteerUtil: Promise<JSHandle<PuppeteerInjectedUtil>>;
}
/**
* @internal
*/
export class LazyArg<T, Context = PuppeteerUtilWrapper> {
static create = <T>(
get: (context: PuppeteerUtilWrapper) => Promise<T> | T,
): T => {
// We don't want to introduce LazyArg to the type system, otherwise we would
// have to make it public.
return new LazyArg(get) as unknown as T;
};
#get: (context: Context) => Promise<T> | T;
private constructor(get: (context: Context) => Promise<T> | T) {
this.#get = get;
}
async get(context: Context): Promise<T> {
return await this.#get(context);
}
}

View File

@@ -0,0 +1,38 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {HTTPRequest} from '../api/HTTPRequest.js';
import type {HTTPResponse} from '../api/HTTPResponse.js';
import type {EventType} from './EventEmitter.js';
/**
* We use symbols to prevent any external parties listening to these events.
* They are internal to Puppeteer.
*
* @internal
*/
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace NetworkManagerEvent {
export const Request = Symbol('NetworkManager.Request');
export const RequestServedFromCache = Symbol(
'NetworkManager.RequestServedFromCache',
);
export const Response = Symbol('NetworkManager.Response');
export const RequestFailed = Symbol('NetworkManager.RequestFailed');
export const RequestFinished = Symbol('NetworkManager.RequestFinished');
}
/**
* @internal
*/
export interface NetworkManagerEvents extends Record<EventType, unknown> {
[NetworkManagerEvent.Request]: HTTPRequest;
[NetworkManagerEvent.RequestServedFromCache]: HTTPRequest;
[NetworkManagerEvent.Response]: HTTPResponse;
[NetworkManagerEvent.RequestFailed]: HTTPRequest;
[NetworkManagerEvent.RequestFinished]: HTTPRequest;
}

274
node_modules/puppeteer-core/src/common/PDFOptions.ts generated vendored Normal file
View File

@@ -0,0 +1,274 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @public
*/
export interface PDFMargin {
top?: string | number;
bottom?: string | number;
left?: string | number;
right?: string | number;
}
/**
* @public
*/
export type LowerCasePaperFormat =
| 'letter'
| 'legal'
| 'tabloid'
| 'ledger'
| 'a0'
| 'a1'
| 'a2'
| 'a3'
| 'a4'
| 'a5'
| 'a6';
/**
* All the valid paper format types when printing a PDF.
*
* @remarks
*
* The sizes of each format are as follows:
*
* - `Letter`: 8.5in x 11in / 21.59cm x 27.94cm
*
* - `Legal`: 8.5in x 14in / 21.59cm x 35.56cm
*
* - `Tabloid`: 11in x 17in / 27.94cm x 43.18cm
*
* - `Ledger`: 17in x 11in / 43.18cm x 27.94cm
*
* - `A0`: 33.1102in x 46.811in / 84.1cm x 118.9cm
*
* - `A1`: 23.3858in x 33.1102in / 59.4cm x 84.1cm
*
* - `A2`: 16.5354in x 23.3858in / 42cm x 59.4cm
*
* - `A3`: 11.6929in x 16.5354in / 29.7cm x 42cm
*
* - `A4`: 8.2677in x 11.6929in / 21cm x 29.7cm
*
* - `A5`: 5.8268in x 8.2677in / 14.8cm x 21cm
*
* - `A6`: 4.1339in x 5.8268in / 10.5cm x 14.8cm
*
* @public
*/
export type PaperFormat =
| Uppercase<LowerCasePaperFormat>
| Capitalize<LowerCasePaperFormat>
| LowerCasePaperFormat;
/**
* Valid options to configure PDF generation via {@link Page.pdf}.
* @public
*/
export interface PDFOptions {
/**
* Scales the rendering of the web page. Amount must be between `0.1` and `2`.
* @defaultValue `1`
*/
scale?: number;
/**
* Whether to show the header and footer.
* @defaultValue `false`
*/
displayHeaderFooter?: boolean;
/**
* HTML template for the print header. Should be valid HTML with the following
* classes used to inject values into them:
*
* - `date` formatted print date
*
* - `title` document title
*
* - `url` document location
*
* - `pageNumber` current page number
*
* - `totalPages` total pages in the document
*/
headerTemplate?: string;
/**
* HTML template for the print footer. Has the same constraints and support
* for special classes as {@link PDFOptions.headerTemplate}.
*/
footerTemplate?: string;
/**
* Set to `true` to print background graphics.
* @defaultValue `false`
*/
printBackground?: boolean;
/**
* Whether to print in landscape orientation.
* @defaultValue `false`
*/
landscape?: boolean;
/**
* Paper ranges to print, e.g. `1-5, 8, 11-13`.
* @defaultValue The empty string, which means all pages are printed.
*/
pageRanges?: string;
/**
* @remarks
* If set, this takes priority over the `width` and `height` options.
* @defaultValue `letter`.
*/
format?: PaperFormat;
/**
* Sets the width of paper. You can pass in a number or a string with a unit.
*/
width?: string | number;
/**
* Sets the height of paper. You can pass in a number or a string with a unit.
*/
height?: string | number;
/**
* Give any CSS `@page` size declared in the page priority over what is
* declared in the `width` or `height` or `format` option.
* @defaultValue `false`, which will scale the content to fit the paper size.
*/
preferCSSPageSize?: boolean;
/**
* Set the PDF margins.
* @defaultValue `undefined` no margins are set.
*/
margin?: PDFMargin;
/**
* The path to save the file to.
*
* @remarks
*
* If the path is relative, it's resolved relative to the current working directory.
*
* @defaultValue `undefined`, which means the PDF will not be written to disk.
*/
path?: string;
/**
* Hides default white background and allows generating pdfs with transparency.
* @defaultValue `false`
*/
omitBackground?: boolean;
/**
* Generate tagged (accessible) PDF.
*
* @defaultValue `true`
* @experimental
*/
tagged?: boolean;
/**
* Generate document outline.
*
* @defaultValue `false`
* @experimental
*/
outline?: boolean;
/**
* Timeout in milliseconds. Pass `0` to disable timeout.
*
* The default value can be changed by using {@link Page.setDefaultTimeout}
*
* @defaultValue `30_000`
*/
timeout?: number;
/**
* If true, waits for `document.fonts.ready` to resolve. This might require
* activating the page using {@link Page.bringToFront} if the page is in the
* background.
*
* @defaultValue `true`
*/
waitForFonts?: boolean;
}
/**
* @internal
*/
export interface PaperFormatDimensions {
width: number;
height: number;
}
/**
* @internal
*/
export interface ParsedPDFOptionsInterface {
width: number;
height: number;
margin: {
top: number;
bottom: number;
left: number;
right: number;
};
}
/**
* @internal
*/
export type ParsedPDFOptions = Required<
Omit<PDFOptions, 'path' | 'format' | 'timeout'> & ParsedPDFOptionsInterface
>;
/**
* @internal
*
* @remarks All A series paper format sizes in inches are calculated from centimeters
* rounded mathematically to four decimal places.
*/
export const paperFormats: Record<
LowerCasePaperFormat,
Record<'cm' | 'in', PaperFormatDimensions>
> = {
letter: {
cm: {width: 21.59, height: 27.94},
in: {width: 8.5, height: 11},
},
legal: {
cm: {width: 21.59, height: 35.56},
in: {width: 8.5, height: 14},
},
tabloid: {
cm: {width: 27.94, height: 43.18},
in: {width: 11, height: 17},
},
ledger: {
cm: {width: 43.18, height: 27.94},
in: {width: 17, height: 11},
},
a0: {
cm: {width: 84.1, height: 118.9},
in: {width: 33.1102, height: 46.811},
},
a1: {
cm: {width: 59.4, height: 84.1},
in: {width: 23.3858, height: 33.1102},
},
a2: {
cm: {width: 42, height: 59.4},
in: {width: 16.5354, height: 23.3858},
},
a3: {
cm: {width: 29.7, height: 42},
in: {width: 11.6929, height: 16.5354},
},
a4: {
cm: {width: 21, height: 29.7},
in: {width: 8.2677, height: 11.6929},
},
a5: {
cm: {width: 14.8, height: 21},
in: {width: 5.8268, height: 8.2677},
},
a6: {
cm: {width: 10.5, height: 14.8},
in: {width: 4.1339, height: 5.8268},
},
} as const;

View File

@@ -0,0 +1,31 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {
QueryHandler,
type QuerySelector,
type QuerySelectorAll,
} from './QueryHandler.js';
/**
* @internal
*/
export class PQueryHandler extends QueryHandler {
static override querySelectorAll: QuerySelectorAll = (
element,
selector,
{pQuerySelectorAll},
) => {
return pQuerySelectorAll(element, selector);
};
static override querySelector: QuerySelector = (
element,
selector,
{pQuerySelector},
) => {
return pQuerySelector(element, selector);
};
}

View File

@@ -0,0 +1,121 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {
type Token,
tokenize,
TOKENS,
stringify,
} from '../../third_party/parsel-js/parsel-js.js';
import type {
ComplexPSelector,
ComplexPSelectorList,
CompoundPSelector,
} from '../injected/PQuerySelector.js';
import {PCombinator} from '../injected/PQuerySelector.js';
TOKENS['nesting'] = /&/g;
TOKENS['combinator'] = /\s*(>>>>?|[\s>+~])\s*/g;
const ESCAPE_REGEXP = /\\[\s\S]/g;
const unquote = (text: string): string => {
if (text.length <= 1) {
return text;
}
if ((text[0] === '"' || text[0] === "'") && text.endsWith(text[0])) {
text = text.slice(1, -1);
}
return text.replace(ESCAPE_REGEXP, match => {
return match[1] as string;
});
};
/**
* @internal
*/
export function parsePSelectors(
selector: string,
): [
selector: ComplexPSelectorList,
isPureCSS: boolean,
hasPseudoClasses: boolean,
hasAria: boolean,
] {
let isPureCSS = true;
let hasAria = false;
let hasPseudoClasses = false;
const tokens = tokenize(selector);
if (tokens.length === 0) {
return [[], isPureCSS, hasPseudoClasses, false];
}
let compoundSelector: CompoundPSelector = [];
let complexSelector: ComplexPSelector = [compoundSelector];
const selectors: ComplexPSelectorList = [complexSelector];
const storage: Token[] = [];
for (const token of tokens) {
switch (token.type) {
case 'combinator':
switch (token.content) {
case PCombinator.Descendent:
isPureCSS = false;
if (storage.length) {
compoundSelector.push(stringify(storage));
storage.splice(0);
}
compoundSelector = [];
complexSelector.push(PCombinator.Descendent);
complexSelector.push(compoundSelector);
continue;
case PCombinator.Child:
isPureCSS = false;
if (storage.length) {
compoundSelector.push(stringify(storage));
storage.splice(0);
}
compoundSelector = [];
complexSelector.push(PCombinator.Child);
complexSelector.push(compoundSelector);
continue;
}
break;
case 'pseudo-element':
if (!token.name.startsWith('-p-')) {
break;
}
isPureCSS = false;
if (storage.length) {
compoundSelector.push(stringify(storage));
storage.splice(0);
}
const name = token.name.slice(3);
if (name === 'aria') {
hasAria = true;
}
compoundSelector.push({
name,
value: unquote(token.argument ?? ''),
});
continue;
case 'pseudo-class':
hasPseudoClasses = true;
break;
case 'comma':
if (storage.length) {
compoundSelector.push(stringify(storage));
storage.splice(0);
}
compoundSelector = [];
complexSelector = [compoundSelector];
selectors.push(complexSelector);
continue;
}
storage.push(token);
}
if (storage.length) {
compoundSelector.push(stringify(storage));
}
return [selectors, isPureCSS, hasPseudoClasses, hasAria];
}

View File

@@ -0,0 +1,29 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {PuppeteerInjectedUtil} from '../injected/injected.js';
import {QueryHandler} from './QueryHandler.js';
/**
* @internal
*/
export class PierceQueryHandler extends QueryHandler {
static override querySelector = (
element: Node,
selector: string,
{pierceQuerySelector}: PuppeteerInjectedUtil,
): Node | null => {
return pierceQuerySelector(element, selector);
};
static override querySelectorAll = (
element: Node,
selector: string,
{pierceQuerySelectorAll}: PuppeteerInjectedUtil,
): Iterable<Node> => {
return pierceQuerySelectorAll(element, selector);
};
}

125
node_modules/puppeteer-core/src/common/Puppeteer.ts generated vendored Normal file
View File

@@ -0,0 +1,125 @@
/**
* @license
* Copyright 2017 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {Browser} from '../api/Browser.js';
import {_connectToBrowser} from './BrowserConnector.js';
import type {ConnectOptions} from './ConnectOptions.js';
import {
type CustomQueryHandler,
customQueryHandlers,
} from './CustomQueryHandler.js';
/**
* Settings that are common to the Puppeteer class, regardless of environment.
*
* @internal
*/
export interface CommonPuppeteerSettings {
isPuppeteerCore: boolean;
}
/**
* The main Puppeteer class.
*
* IMPORTANT: if you are using Puppeteer in a Node environment, you will get an
* instance of {@link PuppeteerNode} when you import or require `puppeteer`.
* That class extends `Puppeteer`, so has all the methods documented below as
* well as all that are defined on {@link PuppeteerNode}.
*
* @public
*/
export class Puppeteer {
/**
* Operations for {@link CustomQueryHandler | custom query handlers}. See
* {@link CustomQueryHandlerRegistry}.
*
* @internal
*/
static customQueryHandlers = customQueryHandlers;
/**
* Registers a {@link CustomQueryHandler | custom query handler}.
*
* @remarks
* After registration, the handler can be used everywhere where a selector is
* expected by prepending the selection string with `<name>/`. The name is only
* allowed to consist of lower- and upper case latin letters.
*
* @example
*
* ```
* import {Puppeteer}, puppeteer from 'puppeteer';
*
* Puppeteer.registerCustomQueryHandler('text', { … });
* const aHandle = await page.$('text/…');
* ```
*
* @param name - The name that the custom query handler will be registered
* under.
* @param queryHandler - The {@link CustomQueryHandler | custom query handler}
* to register.
*
* @public
*/
static registerCustomQueryHandler(
name: string,
queryHandler: CustomQueryHandler,
): void {
return this.customQueryHandlers.register(name, queryHandler);
}
/**
* Unregisters a custom query handler for a given name.
*/
static unregisterCustomQueryHandler(name: string): void {
return this.customQueryHandlers.unregister(name);
}
/**
* Gets the names of all custom query handlers.
*/
static customQueryHandlerNames(): string[] {
return this.customQueryHandlers.names();
}
/**
* Unregisters all custom query handlers.
*/
static clearCustomQueryHandlers(): void {
return this.customQueryHandlers.clear();
}
/**
* @internal
*/
_isPuppeteerCore: boolean;
/**
* @internal
*/
protected _changedBrowsers = false;
/**
* @internal
*/
constructor(settings: CommonPuppeteerSettings) {
this._isPuppeteerCore = settings.isPuppeteerCore;
this.connect = this.connect.bind(this);
}
/**
* This method attaches Puppeteer to an existing browser instance.
*
* @remarks
*
* @param options - Set of configurable options to set on the browser.
* @returns Promise which resolves to browser instance.
*/
connect(options: ConnectOptions): Promise<Browser> {
return _connectToBrowser(options);
}
}

220
node_modules/puppeteer-core/src/common/QueryHandler.ts generated vendored Normal file
View File

@@ -0,0 +1,220 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {ElementHandle} from '../api/ElementHandle.js';
import {_isElementHandle} from '../api/ElementHandleSymbol.js';
import type {Frame} from '../api/Frame.js';
import type {WaitForSelectorOptions} from '../api/Page.js';
import type {PuppeteerInjectedUtil} from '../injected/injected.js';
import {isErrorLike} from '../util/ErrorLike.js';
import {interpolateFunction, stringifyFunction} from '../util/Function.js';
import {TimeoutError} from './Errors.js';
import {transposeIterableHandle} from './HandleIterator.js';
import {LazyArg} from './LazyArg.js';
import type {Awaitable, AwaitableIterable} from './types.js';
/**
* @internal
*/
export type QuerySelectorAll = (
node: Node,
selector: string,
PuppeteerUtil: PuppeteerInjectedUtil,
) => AwaitableIterable<Node>;
/**
* @internal
*/
export type QuerySelector = (
node: Node,
selector: string,
PuppeteerUtil: PuppeteerInjectedUtil,
) => Awaitable<Node | null>;
/**
* @internal
*/
export const enum PollingOptions {
RAF = 'raf',
MUTATION = 'mutation',
}
/**
* @internal
*/
export class QueryHandler {
// Either one of these may be implemented, but at least one must be.
static querySelectorAll?: QuerySelectorAll;
static querySelector?: QuerySelector;
static get _querySelector(): QuerySelector {
if (this.querySelector) {
return this.querySelector;
}
if (!this.querySelectorAll) {
throw new Error('Cannot create default `querySelector`.');
}
return (this.querySelector = interpolateFunction(
async (node, selector, PuppeteerUtil) => {
const querySelectorAll: QuerySelectorAll =
PLACEHOLDER('querySelectorAll');
const results = querySelectorAll(node, selector, PuppeteerUtil);
for await (const result of results) {
return result;
}
return null;
},
{
querySelectorAll: stringifyFunction(this.querySelectorAll),
},
));
}
static get _querySelectorAll(): QuerySelectorAll {
if (this.querySelectorAll) {
return this.querySelectorAll;
}
if (!this.querySelector) {
throw new Error('Cannot create default `querySelectorAll`.');
}
return (this.querySelectorAll = interpolateFunction(
async function* (node, selector, PuppeteerUtil) {
const querySelector: QuerySelector = PLACEHOLDER('querySelector');
const result = await querySelector(node, selector, PuppeteerUtil);
if (result) {
yield result;
}
},
{
querySelector: stringifyFunction(this.querySelector),
},
));
}
/**
* Queries for multiple nodes given a selector and {@link ElementHandle}.
*
* Akin to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll | Document.querySelectorAll()}.
*/
static async *queryAll(
element: ElementHandle<Node>,
selector: string,
): AwaitableIterable<ElementHandle<Node>> {
using handle = await element.evaluateHandle(
this._querySelectorAll,
selector,
LazyArg.create(context => {
return context.puppeteerUtil;
}),
);
yield* transposeIterableHandle(handle);
}
/**
* Queries for a single node given a selector and {@link ElementHandle}.
*
* Akin to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector}.
*/
static async queryOne(
element: ElementHandle<Node>,
selector: string,
): Promise<ElementHandle<Node> | null> {
using result = await element.evaluateHandle(
this._querySelector,
selector,
LazyArg.create(context => {
return context.puppeteerUtil;
}),
);
if (!(_isElementHandle in result)) {
return null;
}
return result.move();
}
/**
* Waits until a single node appears for a given selector and
* {@link ElementHandle}.
*
* This will always query the handle in the Puppeteer world and migrate the
* result to the main world.
*/
static async waitFor(
elementOrFrame: ElementHandle<Node> | Frame,
selector: string,
options: WaitForSelectorOptions & {
polling?: PollingOptions;
},
): Promise<ElementHandle<Node> | null> {
let frame!: Frame;
using element = await (async () => {
if (!(_isElementHandle in elementOrFrame)) {
frame = elementOrFrame;
return;
}
frame = elementOrFrame.frame;
return await frame.isolatedRealm().adoptHandle(elementOrFrame);
})();
const {visible = false, hidden = false, timeout, signal} = options;
const polling = visible || hidden ? PollingOptions.RAF : options.polling;
try {
signal?.throwIfAborted();
using handle = await frame.isolatedRealm().waitForFunction(
async (PuppeteerUtil, query, selector, root, visible) => {
const querySelector = PuppeteerUtil.createFunction(
query,
) as QuerySelector;
const node = await querySelector(
root ?? document,
selector,
PuppeteerUtil,
);
return PuppeteerUtil.checkVisibility(node, visible);
},
{
polling,
root: element,
timeout,
signal,
},
LazyArg.create(context => {
return context.puppeteerUtil;
}),
stringifyFunction(this._querySelector),
selector,
element,
visible ? true : hidden ? false : undefined,
);
if (signal?.aborted) {
throw signal.reason;
}
if (!(_isElementHandle in handle)) {
return null;
}
return await frame.mainRealm().transferHandle(handle);
} catch (error) {
if (!isErrorLike(error)) {
throw error;
}
if (error.name === 'AbortError') {
throw error;
}
const waitForSelectorError = new (
error instanceof TimeoutError ? TimeoutError : Error
)(`Waiting for selector \`${selector}\` failed`);
waitForSelectorError.cause = error;
throw waitForSelectorError;
}
}
}

View File

@@ -0,0 +1,57 @@
/**
* @license
* Copyright 2024 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {source as injectedSource} from '../generated/injected.js';
/**
* @internal
*/
export class ScriptInjector {
#updated = false;
#amendments = new Set<string>();
// Appends a statement of the form `(PuppeteerUtil) => {...}`.
append(statement: string): void {
this.#update(() => {
this.#amendments.add(statement);
});
}
pop(statement: string): void {
this.#update(() => {
this.#amendments.delete(statement);
});
}
inject(inject: (script: string) => void, force = false): void {
if (this.#updated || force) {
inject(this.#get());
}
this.#updated = false;
}
#update(callback: () => void): void {
callback();
this.#updated = true;
}
#get(): string {
return `(() => {
const module = {};
${injectedSource}
${[...this.#amendments]
.map(statement => {
return `(${statement})(module.exports.default);`;
})
.join('')}
return module.exports.default;
})()`;
}
}
/**
* @internal
*/
export const scriptInjector = new ScriptInjector();

View File

@@ -0,0 +1,78 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {Protocol} from 'devtools-protocol';
/**
* The SecurityDetails class represents the security details of a
* response that was received over a secure connection.
*
* @public
*/
export class SecurityDetails {
#subjectName: string;
#issuer: string;
#validFrom: number;
#validTo: number;
#protocol: string;
#sanList: string[];
/**
* @internal
*/
constructor(securityPayload: Protocol.Network.SecurityDetails) {
this.#subjectName = securityPayload.subjectName;
this.#issuer = securityPayload.issuer;
this.#validFrom = securityPayload.validFrom;
this.#validTo = securityPayload.validTo;
this.#protocol = securityPayload.protocol;
this.#sanList = securityPayload.sanList;
}
/**
* The name of the issuer of the certificate.
*/
issuer(): string {
return this.#issuer;
}
/**
* {@link https://en.wikipedia.org/wiki/Unix_time | Unix timestamp}
* marking the start of the certificate's validity.
*/
validFrom(): number {
return this.#validFrom;
}
/**
* {@link https://en.wikipedia.org/wiki/Unix_time | Unix timestamp}
* marking the end of the certificate's validity.
*/
validTo(): number {
return this.#validTo;
}
/**
* The security protocol being used, e.g. "TLS 1.2".
*/
protocol(): string {
return this.#protocol;
}
/**
* The name of the subject to which the certificate was issued.
*/
subjectName(): string {
return this.#subjectName;
}
/**
* The list of {@link https://en.wikipedia.org/wiki/Subject_Alternative_Name | subject alternative names (SANs)} of the certificate.
*/
subjectAlternativeNames(): string[] {
return this.#sanList;
}
}

View File

@@ -0,0 +1,12 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Browsers supported by Puppeteer.
*
* @public
*/
export type SupportedBrowser = 'chrome' | 'firefox';

29
node_modules/puppeteer-core/src/common/TaskQueue.ts generated vendored Normal file
View File

@@ -0,0 +1,29 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
*/
export class TaskQueue {
#chain: Promise<void>;
constructor() {
this.#chain = Promise.resolve();
}
postTask<T>(task: () => Promise<T>): Promise<T> {
const result = this.#chain.then(task);
this.#chain = result.then(
() => {
return undefined;
},
() => {
return undefined;
},
);
return result;
}
}

View File

@@ -0,0 +1,20 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {QueryHandler, type QuerySelectorAll} from './QueryHandler.js';
/**
* @internal
*/
export class TextQueryHandler extends QueryHandler {
static override querySelectorAll: QuerySelectorAll = (
element,
selector,
{textQuerySelectorAll},
) => {
return textQuerySelectorAll(element, selector);
};
}

View File

@@ -0,0 +1,45 @@
/**
* @license
* Copyright 2019 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
const DEFAULT_TIMEOUT = 30000;
/**
* @internal
*/
export class TimeoutSettings {
#defaultTimeout: number | null;
#defaultNavigationTimeout: number | null;
constructor() {
this.#defaultTimeout = null;
this.#defaultNavigationTimeout = null;
}
setDefaultTimeout(timeout: number): void {
this.#defaultTimeout = timeout;
}
setDefaultNavigationTimeout(timeout: number): void {
this.#defaultNavigationTimeout = timeout;
}
navigationTimeout(): number {
if (this.#defaultNavigationTimeout !== null) {
return this.#defaultNavigationTimeout;
}
if (this.#defaultTimeout !== null) {
return this.#defaultTimeout;
}
return DEFAULT_TIMEOUT;
}
timeout(): number {
if (this.#defaultTimeout !== null) {
return this.#defaultTimeout;
}
return DEFAULT_TIMEOUT;
}
}

View File

@@ -0,0 +1,671 @@
/**
* @license
* Copyright 2017 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
*/
export interface KeyDefinition {
keyCode?: number;
shiftKeyCode?: number;
key?: string;
shiftKey?: string;
code?: string;
text?: string;
shiftText?: string;
location?: number;
}
/**
* All the valid keys that can be passed to functions that take user input, such
* as {@link Keyboard.press | keyboard.press }
*
* @public
*/
export type KeyInput =
| '0'
| '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9'
| 'Power'
| 'Eject'
| 'Abort'
| 'Help'
| 'Backspace'
| 'Tab'
| 'Numpad5'
| 'NumpadEnter'
| 'Enter'
| '\r'
| '\n'
| 'ShiftLeft'
| 'ShiftRight'
| 'ControlLeft'
| 'ControlRight'
| 'AltLeft'
| 'AltRight'
| 'Pause'
| 'CapsLock'
| 'Escape'
| 'Convert'
| 'NonConvert'
| 'Space'
| 'Numpad9'
| 'PageUp'
| 'Numpad3'
| 'PageDown'
| 'End'
| 'Numpad1'
| 'Home'
| 'Numpad7'
| 'ArrowLeft'
| 'Numpad4'
| 'Numpad8'
| 'ArrowUp'
| 'ArrowRight'
| 'Numpad6'
| 'Numpad2'
| 'ArrowDown'
| 'Select'
| 'Open'
| 'PrintScreen'
| 'Insert'
| 'Numpad0'
| 'Delete'
| 'NumpadDecimal'
| 'Digit0'
| 'Digit1'
| 'Digit2'
| 'Digit3'
| 'Digit4'
| 'Digit5'
| 'Digit6'
| 'Digit7'
| 'Digit8'
| 'Digit9'
| 'KeyA'
| 'KeyB'
| 'KeyC'
| 'KeyD'
| 'KeyE'
| 'KeyF'
| 'KeyG'
| 'KeyH'
| 'KeyI'
| 'KeyJ'
| 'KeyK'
| 'KeyL'
| 'KeyM'
| 'KeyN'
| 'KeyO'
| 'KeyP'
| 'KeyQ'
| 'KeyR'
| 'KeyS'
| 'KeyT'
| 'KeyU'
| 'KeyV'
| 'KeyW'
| 'KeyX'
| 'KeyY'
| 'KeyZ'
| 'MetaLeft'
| 'MetaRight'
| 'ContextMenu'
| 'NumpadMultiply'
| 'NumpadAdd'
| 'NumpadSubtract'
| 'NumpadDivide'
| 'F1'
| 'F2'
| 'F3'
| 'F4'
| 'F5'
| 'F6'
| 'F7'
| 'F8'
| 'F9'
| 'F10'
| 'F11'
| 'F12'
| 'F13'
| 'F14'
| 'F15'
| 'F16'
| 'F17'
| 'F18'
| 'F19'
| 'F20'
| 'F21'
| 'F22'
| 'F23'
| 'F24'
| 'NumLock'
| 'ScrollLock'
| 'AudioVolumeMute'
| 'AudioVolumeDown'
| 'AudioVolumeUp'
| 'MediaTrackNext'
| 'MediaTrackPrevious'
| 'MediaStop'
| 'MediaPlayPause'
| 'Semicolon'
| 'Equal'
| 'NumpadEqual'
| 'Comma'
| 'Minus'
| 'Period'
| 'Slash'
| 'Backquote'
| 'BracketLeft'
| 'Backslash'
| 'BracketRight'
| 'Quote'
| 'AltGraph'
| 'Props'
| 'Cancel'
| 'Clear'
| 'Shift'
| 'Control'
| 'Alt'
| 'Accept'
| 'ModeChange'
| ' '
| 'Print'
| 'Execute'
| '\u0000'
| 'a'
| 'b'
| 'c'
| 'd'
| 'e'
| 'f'
| 'g'
| 'h'
| 'i'
| 'j'
| 'k'
| 'l'
| 'm'
| 'n'
| 'o'
| 'p'
| 'q'
| 'r'
| 's'
| 't'
| 'u'
| 'v'
| 'w'
| 'x'
| 'y'
| 'z'
| 'Meta'
| '*'
| '+'
| '-'
| '/'
| ';'
| '='
| ','
| '.'
| '`'
| '['
| '\\'
| ']'
| "'"
| 'Attn'
| 'CrSel'
| 'ExSel'
| 'EraseEof'
| 'Play'
| 'ZoomOut'
| ')'
| '!'
| '@'
| '#'
| '$'
| '%'
| '^'
| '&'
| '('
| 'A'
| 'B'
| 'C'
| 'D'
| 'E'
| 'F'
| 'G'
| 'H'
| 'I'
| 'J'
| 'K'
| 'L'
| 'M'
| 'N'
| 'O'
| 'P'
| 'Q'
| 'R'
| 'S'
| 'T'
| 'U'
| 'V'
| 'W'
| 'X'
| 'Y'
| 'Z'
| ':'
| '<'
| '_'
| '>'
| '?'
| '~'
| '{'
| '|'
| '}'
| '"'
| 'SoftLeft'
| 'SoftRight'
| 'Camera'
| 'Call'
| 'EndCall'
| 'VolumeDown'
| 'VolumeUp';
/**
* @internal
*/
export const _keyDefinitions: Readonly<Record<KeyInput, KeyDefinition>> = {
'0': {keyCode: 48, key: '0', code: 'Digit0'},
'1': {keyCode: 49, key: '1', code: 'Digit1'},
'2': {keyCode: 50, key: '2', code: 'Digit2'},
'3': {keyCode: 51, key: '3', code: 'Digit3'},
'4': {keyCode: 52, key: '4', code: 'Digit4'},
'5': {keyCode: 53, key: '5', code: 'Digit5'},
'6': {keyCode: 54, key: '6', code: 'Digit6'},
'7': {keyCode: 55, key: '7', code: 'Digit7'},
'8': {keyCode: 56, key: '8', code: 'Digit8'},
'9': {keyCode: 57, key: '9', code: 'Digit9'},
Power: {key: 'Power', code: 'Power'},
Eject: {key: 'Eject', code: 'Eject'},
Abort: {keyCode: 3, code: 'Abort', key: 'Cancel'},
Help: {keyCode: 6, code: 'Help', key: 'Help'},
Backspace: {keyCode: 8, code: 'Backspace', key: 'Backspace'},
Tab: {keyCode: 9, code: 'Tab', key: 'Tab'},
Numpad5: {
keyCode: 12,
shiftKeyCode: 101,
key: 'Clear',
code: 'Numpad5',
shiftKey: '5',
location: 3,
},
NumpadEnter: {
keyCode: 13,
code: 'NumpadEnter',
key: 'Enter',
text: '\r',
location: 3,
},
Enter: {keyCode: 13, code: 'Enter', key: 'Enter', text: '\r'},
'\r': {keyCode: 13, code: 'Enter', key: 'Enter', text: '\r'},
'\n': {keyCode: 13, code: 'Enter', key: 'Enter', text: '\r'},
ShiftLeft: {keyCode: 16, code: 'ShiftLeft', key: 'Shift', location: 1},
ShiftRight: {keyCode: 16, code: 'ShiftRight', key: 'Shift', location: 2},
ControlLeft: {
keyCode: 17,
code: 'ControlLeft',
key: 'Control',
location: 1,
},
ControlRight: {
keyCode: 17,
code: 'ControlRight',
key: 'Control',
location: 2,
},
AltLeft: {keyCode: 18, code: 'AltLeft', key: 'Alt', location: 1},
AltRight: {keyCode: 18, code: 'AltRight', key: 'Alt', location: 2},
Pause: {keyCode: 19, code: 'Pause', key: 'Pause'},
CapsLock: {keyCode: 20, code: 'CapsLock', key: 'CapsLock'},
Escape: {keyCode: 27, code: 'Escape', key: 'Escape'},
Convert: {keyCode: 28, code: 'Convert', key: 'Convert'},
NonConvert: {keyCode: 29, code: 'NonConvert', key: 'NonConvert'},
Space: {keyCode: 32, code: 'Space', key: ' '},
Numpad9: {
keyCode: 33,
shiftKeyCode: 105,
key: 'PageUp',
code: 'Numpad9',
shiftKey: '9',
location: 3,
},
PageUp: {keyCode: 33, code: 'PageUp', key: 'PageUp'},
Numpad3: {
keyCode: 34,
shiftKeyCode: 99,
key: 'PageDown',
code: 'Numpad3',
shiftKey: '3',
location: 3,
},
PageDown: {keyCode: 34, code: 'PageDown', key: 'PageDown'},
End: {keyCode: 35, code: 'End', key: 'End'},
Numpad1: {
keyCode: 35,
shiftKeyCode: 97,
key: 'End',
code: 'Numpad1',
shiftKey: '1',
location: 3,
},
Home: {keyCode: 36, code: 'Home', key: 'Home'},
Numpad7: {
keyCode: 36,
shiftKeyCode: 103,
key: 'Home',
code: 'Numpad7',
shiftKey: '7',
location: 3,
},
ArrowLeft: {keyCode: 37, code: 'ArrowLeft', key: 'ArrowLeft'},
Numpad4: {
keyCode: 37,
shiftKeyCode: 100,
key: 'ArrowLeft',
code: 'Numpad4',
shiftKey: '4',
location: 3,
},
Numpad8: {
keyCode: 38,
shiftKeyCode: 104,
key: 'ArrowUp',
code: 'Numpad8',
shiftKey: '8',
location: 3,
},
ArrowUp: {keyCode: 38, code: 'ArrowUp', key: 'ArrowUp'},
ArrowRight: {keyCode: 39, code: 'ArrowRight', key: 'ArrowRight'},
Numpad6: {
keyCode: 39,
shiftKeyCode: 102,
key: 'ArrowRight',
code: 'Numpad6',
shiftKey: '6',
location: 3,
},
Numpad2: {
keyCode: 40,
shiftKeyCode: 98,
key: 'ArrowDown',
code: 'Numpad2',
shiftKey: '2',
location: 3,
},
ArrowDown: {keyCode: 40, code: 'ArrowDown', key: 'ArrowDown'},
Select: {keyCode: 41, code: 'Select', key: 'Select'},
Open: {keyCode: 43, code: 'Open', key: 'Execute'},
PrintScreen: {keyCode: 44, code: 'PrintScreen', key: 'PrintScreen'},
Insert: {keyCode: 45, code: 'Insert', key: 'Insert'},
Numpad0: {
keyCode: 45,
shiftKeyCode: 96,
key: 'Insert',
code: 'Numpad0',
shiftKey: '0',
location: 3,
},
Delete: {keyCode: 46, code: 'Delete', key: 'Delete'},
NumpadDecimal: {
keyCode: 46,
shiftKeyCode: 110,
code: 'NumpadDecimal',
key: '\u0000',
shiftKey: '.',
location: 3,
},
Digit0: {keyCode: 48, code: 'Digit0', shiftKey: ')', key: '0'},
Digit1: {keyCode: 49, code: 'Digit1', shiftKey: '!', key: '1'},
Digit2: {keyCode: 50, code: 'Digit2', shiftKey: '@', key: '2'},
Digit3: {keyCode: 51, code: 'Digit3', shiftKey: '#', key: '3'},
Digit4: {keyCode: 52, code: 'Digit4', shiftKey: '$', key: '4'},
Digit5: {keyCode: 53, code: 'Digit5', shiftKey: '%', key: '5'},
Digit6: {keyCode: 54, code: 'Digit6', shiftKey: '^', key: '6'},
Digit7: {keyCode: 55, code: 'Digit7', shiftKey: '&', key: '7'},
Digit8: {keyCode: 56, code: 'Digit8', shiftKey: '*', key: '8'},
Digit9: {keyCode: 57, code: 'Digit9', shiftKey: '(', key: '9'},
KeyA: {keyCode: 65, code: 'KeyA', shiftKey: 'A', key: 'a'},
KeyB: {keyCode: 66, code: 'KeyB', shiftKey: 'B', key: 'b'},
KeyC: {keyCode: 67, code: 'KeyC', shiftKey: 'C', key: 'c'},
KeyD: {keyCode: 68, code: 'KeyD', shiftKey: 'D', key: 'd'},
KeyE: {keyCode: 69, code: 'KeyE', shiftKey: 'E', key: 'e'},
KeyF: {keyCode: 70, code: 'KeyF', shiftKey: 'F', key: 'f'},
KeyG: {keyCode: 71, code: 'KeyG', shiftKey: 'G', key: 'g'},
KeyH: {keyCode: 72, code: 'KeyH', shiftKey: 'H', key: 'h'},
KeyI: {keyCode: 73, code: 'KeyI', shiftKey: 'I', key: 'i'},
KeyJ: {keyCode: 74, code: 'KeyJ', shiftKey: 'J', key: 'j'},
KeyK: {keyCode: 75, code: 'KeyK', shiftKey: 'K', key: 'k'},
KeyL: {keyCode: 76, code: 'KeyL', shiftKey: 'L', key: 'l'},
KeyM: {keyCode: 77, code: 'KeyM', shiftKey: 'M', key: 'm'},
KeyN: {keyCode: 78, code: 'KeyN', shiftKey: 'N', key: 'n'},
KeyO: {keyCode: 79, code: 'KeyO', shiftKey: 'O', key: 'o'},
KeyP: {keyCode: 80, code: 'KeyP', shiftKey: 'P', key: 'p'},
KeyQ: {keyCode: 81, code: 'KeyQ', shiftKey: 'Q', key: 'q'},
KeyR: {keyCode: 82, code: 'KeyR', shiftKey: 'R', key: 'r'},
KeyS: {keyCode: 83, code: 'KeyS', shiftKey: 'S', key: 's'},
KeyT: {keyCode: 84, code: 'KeyT', shiftKey: 'T', key: 't'},
KeyU: {keyCode: 85, code: 'KeyU', shiftKey: 'U', key: 'u'},
KeyV: {keyCode: 86, code: 'KeyV', shiftKey: 'V', key: 'v'},
KeyW: {keyCode: 87, code: 'KeyW', shiftKey: 'W', key: 'w'},
KeyX: {keyCode: 88, code: 'KeyX', shiftKey: 'X', key: 'x'},
KeyY: {keyCode: 89, code: 'KeyY', shiftKey: 'Y', key: 'y'},
KeyZ: {keyCode: 90, code: 'KeyZ', shiftKey: 'Z', key: 'z'},
MetaLeft: {keyCode: 91, code: 'MetaLeft', key: 'Meta', location: 1},
MetaRight: {keyCode: 92, code: 'MetaRight', key: 'Meta', location: 2},
ContextMenu: {keyCode: 93, code: 'ContextMenu', key: 'ContextMenu'},
NumpadMultiply: {
keyCode: 106,
code: 'NumpadMultiply',
key: '*',
location: 3,
},
NumpadAdd: {keyCode: 107, code: 'NumpadAdd', key: '+', location: 3},
NumpadSubtract: {
keyCode: 109,
code: 'NumpadSubtract',
key: '-',
location: 3,
},
NumpadDivide: {keyCode: 111, code: 'NumpadDivide', key: '/', location: 3},
F1: {keyCode: 112, code: 'F1', key: 'F1'},
F2: {keyCode: 113, code: 'F2', key: 'F2'},
F3: {keyCode: 114, code: 'F3', key: 'F3'},
F4: {keyCode: 115, code: 'F4', key: 'F4'},
F5: {keyCode: 116, code: 'F5', key: 'F5'},
F6: {keyCode: 117, code: 'F6', key: 'F6'},
F7: {keyCode: 118, code: 'F7', key: 'F7'},
F8: {keyCode: 119, code: 'F8', key: 'F8'},
F9: {keyCode: 120, code: 'F9', key: 'F9'},
F10: {keyCode: 121, code: 'F10', key: 'F10'},
F11: {keyCode: 122, code: 'F11', key: 'F11'},
F12: {keyCode: 123, code: 'F12', key: 'F12'},
F13: {keyCode: 124, code: 'F13', key: 'F13'},
F14: {keyCode: 125, code: 'F14', key: 'F14'},
F15: {keyCode: 126, code: 'F15', key: 'F15'},
F16: {keyCode: 127, code: 'F16', key: 'F16'},
F17: {keyCode: 128, code: 'F17', key: 'F17'},
F18: {keyCode: 129, code: 'F18', key: 'F18'},
F19: {keyCode: 130, code: 'F19', key: 'F19'},
F20: {keyCode: 131, code: 'F20', key: 'F20'},
F21: {keyCode: 132, code: 'F21', key: 'F21'},
F22: {keyCode: 133, code: 'F22', key: 'F22'},
F23: {keyCode: 134, code: 'F23', key: 'F23'},
F24: {keyCode: 135, code: 'F24', key: 'F24'},
NumLock: {keyCode: 144, code: 'NumLock', key: 'NumLock'},
ScrollLock: {keyCode: 145, code: 'ScrollLock', key: 'ScrollLock'},
AudioVolumeMute: {
keyCode: 173,
code: 'AudioVolumeMute',
key: 'AudioVolumeMute',
},
AudioVolumeDown: {
keyCode: 174,
code: 'AudioVolumeDown',
key: 'AudioVolumeDown',
},
AudioVolumeUp: {keyCode: 175, code: 'AudioVolumeUp', key: 'AudioVolumeUp'},
MediaTrackNext: {
keyCode: 176,
code: 'MediaTrackNext',
key: 'MediaTrackNext',
},
MediaTrackPrevious: {
keyCode: 177,
code: 'MediaTrackPrevious',
key: 'MediaTrackPrevious',
},
MediaStop: {keyCode: 178, code: 'MediaStop', key: 'MediaStop'},
MediaPlayPause: {
keyCode: 179,
code: 'MediaPlayPause',
key: 'MediaPlayPause',
},
Semicolon: {keyCode: 186, code: 'Semicolon', shiftKey: ':', key: ';'},
Equal: {keyCode: 187, code: 'Equal', shiftKey: '+', key: '='},
NumpadEqual: {keyCode: 187, code: 'NumpadEqual', key: '=', location: 3},
Comma: {keyCode: 188, code: 'Comma', shiftKey: '<', key: ','},
Minus: {keyCode: 189, code: 'Minus', shiftKey: '_', key: '-'},
Period: {keyCode: 190, code: 'Period', shiftKey: '>', key: '.'},
Slash: {keyCode: 191, code: 'Slash', shiftKey: '?', key: '/'},
Backquote: {keyCode: 192, code: 'Backquote', shiftKey: '~', key: '`'},
BracketLeft: {keyCode: 219, code: 'BracketLeft', shiftKey: '{', key: '['},
Backslash: {keyCode: 220, code: 'Backslash', shiftKey: '|', key: '\\'},
BracketRight: {keyCode: 221, code: 'BracketRight', shiftKey: '}', key: ']'},
Quote: {keyCode: 222, code: 'Quote', shiftKey: '"', key: "'"},
AltGraph: {keyCode: 225, code: 'AltGraph', key: 'AltGraph'},
Props: {keyCode: 247, code: 'Props', key: 'CrSel'},
Cancel: {keyCode: 3, key: 'Cancel', code: 'Abort'},
Clear: {keyCode: 12, key: 'Clear', code: 'Numpad5', location: 3},
Shift: {keyCode: 16, key: 'Shift', code: 'ShiftLeft', location: 1},
Control: {keyCode: 17, key: 'Control', code: 'ControlLeft', location: 1},
Alt: {keyCode: 18, key: 'Alt', code: 'AltLeft', location: 1},
Accept: {keyCode: 30, key: 'Accept'},
ModeChange: {keyCode: 31, key: 'ModeChange'},
' ': {keyCode: 32, key: ' ', code: 'Space'},
Print: {keyCode: 42, key: 'Print'},
Execute: {keyCode: 43, key: 'Execute', code: 'Open'},
'\u0000': {keyCode: 46, key: '\u0000', code: 'NumpadDecimal', location: 3},
a: {keyCode: 65, key: 'a', code: 'KeyA'},
b: {keyCode: 66, key: 'b', code: 'KeyB'},
c: {keyCode: 67, key: 'c', code: 'KeyC'},
d: {keyCode: 68, key: 'd', code: 'KeyD'},
e: {keyCode: 69, key: 'e', code: 'KeyE'},
f: {keyCode: 70, key: 'f', code: 'KeyF'},
g: {keyCode: 71, key: 'g', code: 'KeyG'},
h: {keyCode: 72, key: 'h', code: 'KeyH'},
i: {keyCode: 73, key: 'i', code: 'KeyI'},
j: {keyCode: 74, key: 'j', code: 'KeyJ'},
k: {keyCode: 75, key: 'k', code: 'KeyK'},
l: {keyCode: 76, key: 'l', code: 'KeyL'},
m: {keyCode: 77, key: 'm', code: 'KeyM'},
n: {keyCode: 78, key: 'n', code: 'KeyN'},
o: {keyCode: 79, key: 'o', code: 'KeyO'},
p: {keyCode: 80, key: 'p', code: 'KeyP'},
q: {keyCode: 81, key: 'q', code: 'KeyQ'},
r: {keyCode: 82, key: 'r', code: 'KeyR'},
s: {keyCode: 83, key: 's', code: 'KeyS'},
t: {keyCode: 84, key: 't', code: 'KeyT'},
u: {keyCode: 85, key: 'u', code: 'KeyU'},
v: {keyCode: 86, key: 'v', code: 'KeyV'},
w: {keyCode: 87, key: 'w', code: 'KeyW'},
x: {keyCode: 88, key: 'x', code: 'KeyX'},
y: {keyCode: 89, key: 'y', code: 'KeyY'},
z: {keyCode: 90, key: 'z', code: 'KeyZ'},
Meta: {keyCode: 91, key: 'Meta', code: 'MetaLeft', location: 1},
'*': {keyCode: 106, key: '*', code: 'NumpadMultiply', location: 3},
'+': {keyCode: 107, key: '+', code: 'NumpadAdd', location: 3},
'-': {keyCode: 109, key: '-', code: 'NumpadSubtract', location: 3},
'/': {keyCode: 111, key: '/', code: 'NumpadDivide', location: 3},
';': {keyCode: 186, key: ';', code: 'Semicolon'},
'=': {keyCode: 187, key: '=', code: 'Equal'},
',': {keyCode: 188, key: ',', code: 'Comma'},
'.': {keyCode: 190, key: '.', code: 'Period'},
'`': {keyCode: 192, key: '`', code: 'Backquote'},
'[': {keyCode: 219, key: '[', code: 'BracketLeft'},
'\\': {keyCode: 220, key: '\\', code: 'Backslash'},
']': {keyCode: 221, key: ']', code: 'BracketRight'},
"'": {keyCode: 222, key: "'", code: 'Quote'},
Attn: {keyCode: 246, key: 'Attn'},
CrSel: {keyCode: 247, key: 'CrSel', code: 'Props'},
ExSel: {keyCode: 248, key: 'ExSel'},
EraseEof: {keyCode: 249, key: 'EraseEof'},
Play: {keyCode: 250, key: 'Play'},
ZoomOut: {keyCode: 251, key: 'ZoomOut'},
')': {keyCode: 48, key: ')', code: 'Digit0'},
'!': {keyCode: 49, key: '!', code: 'Digit1'},
'@': {keyCode: 50, key: '@', code: 'Digit2'},
'#': {keyCode: 51, key: '#', code: 'Digit3'},
$: {keyCode: 52, key: '$', code: 'Digit4'},
'%': {keyCode: 53, key: '%', code: 'Digit5'},
'^': {keyCode: 54, key: '^', code: 'Digit6'},
'&': {keyCode: 55, key: '&', code: 'Digit7'},
'(': {keyCode: 57, key: '(', code: 'Digit9'},
A: {keyCode: 65, key: 'A', code: 'KeyA'},
B: {keyCode: 66, key: 'B', code: 'KeyB'},
C: {keyCode: 67, key: 'C', code: 'KeyC'},
D: {keyCode: 68, key: 'D', code: 'KeyD'},
E: {keyCode: 69, key: 'E', code: 'KeyE'},
F: {keyCode: 70, key: 'F', code: 'KeyF'},
G: {keyCode: 71, key: 'G', code: 'KeyG'},
H: {keyCode: 72, key: 'H', code: 'KeyH'},
I: {keyCode: 73, key: 'I', code: 'KeyI'},
J: {keyCode: 74, key: 'J', code: 'KeyJ'},
K: {keyCode: 75, key: 'K', code: 'KeyK'},
L: {keyCode: 76, key: 'L', code: 'KeyL'},
M: {keyCode: 77, key: 'M', code: 'KeyM'},
N: {keyCode: 78, key: 'N', code: 'KeyN'},
O: {keyCode: 79, key: 'O', code: 'KeyO'},
P: {keyCode: 80, key: 'P', code: 'KeyP'},
Q: {keyCode: 81, key: 'Q', code: 'KeyQ'},
R: {keyCode: 82, key: 'R', code: 'KeyR'},
S: {keyCode: 83, key: 'S', code: 'KeyS'},
T: {keyCode: 84, key: 'T', code: 'KeyT'},
U: {keyCode: 85, key: 'U', code: 'KeyU'},
V: {keyCode: 86, key: 'V', code: 'KeyV'},
W: {keyCode: 87, key: 'W', code: 'KeyW'},
X: {keyCode: 88, key: 'X', code: 'KeyX'},
Y: {keyCode: 89, key: 'Y', code: 'KeyY'},
Z: {keyCode: 90, key: 'Z', code: 'KeyZ'},
':': {keyCode: 186, key: ':', code: 'Semicolon'},
'<': {keyCode: 188, key: '<', code: 'Comma'},
_: {keyCode: 189, key: '_', code: 'Minus'},
'>': {keyCode: 190, key: '>', code: 'Period'},
'?': {keyCode: 191, key: '?', code: 'Slash'},
'~': {keyCode: 192, key: '~', code: 'Backquote'},
'{': {keyCode: 219, key: '{', code: 'BracketLeft'},
'|': {keyCode: 220, key: '|', code: 'Backslash'},
'}': {keyCode: 221, key: '}', code: 'BracketRight'},
'"': {keyCode: 222, key: '"', code: 'Quote'},
SoftLeft: {key: 'SoftLeft', code: 'SoftLeft', location: 4},
SoftRight: {key: 'SoftRight', code: 'SoftRight', location: 4},
Camera: {keyCode: 44, key: 'Camera', code: 'Camera', location: 4},
Call: {key: 'Call', code: 'Call', location: 4},
EndCall: {keyCode: 95, key: 'EndCall', code: 'EndCall', location: 4},
VolumeDown: {
keyCode: 182,
key: 'VolumeDown',
code: 'VolumeDown',
location: 4,
},
VolumeUp: {keyCode: 183, key: 'VolumeUp', code: 'VolumeUp', location: 4},
};

50
node_modules/puppeteer-core/src/common/Viewport.ts generated vendored Normal file
View File

@@ -0,0 +1,50 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @public
*/
export interface Viewport {
/**
* The page width in CSS pixels.
*
* @remarks
* Setting this value to `0` will reset this value to the system default.
*/
width: number;
/**
* The page height in CSS pixels.
*
* @remarks
* Setting this value to `0` will reset this value to the system default.
*/
height: number;
/**
* Specify device scale factor.
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio | devicePixelRatio} for more info.
*
* @remarks
* Setting this value to `0` will reset this value to the system default.
*
* @defaultValue `1`
*/
deviceScaleFactor?: number;
/**
* Whether the `meta viewport` tag is taken into account.
* @defaultValue `false`
*/
isMobile?: boolean;
/**
* Specifies if the viewport is in landscape mode.
* @defaultValue `false`
*/
isLandscape?: boolean;
/**
* Specify if the viewport supports touch events.
* @defaultValue `false`
*/
hasTouch?: boolean;
}

273
node_modules/puppeteer-core/src/common/WaitTask.ts generated vendored Normal file
View File

@@ -0,0 +1,273 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {ElementHandle} from '../api/ElementHandle.js';
import type {JSHandle} from '../api/JSHandle.js';
import type {Realm} from '../api/Realm.js';
import type {Poller} from '../injected/Poller.js';
import {Deferred} from '../util/Deferred.js';
import {isErrorLike} from '../util/ErrorLike.js';
import {stringifyFunction} from '../util/Function.js';
import {TimeoutError} from './Errors.js';
import {LazyArg} from './LazyArg.js';
import type {HandleFor} from './types.js';
/**
* @internal
*/
export interface WaitTaskOptions {
polling: 'raf' | 'mutation' | number;
root?: ElementHandle<Node>;
timeout: number;
signal?: AbortSignal;
}
/**
* @internal
*/
export class WaitTask<T = unknown> {
#world: Realm;
#polling: 'raf' | 'mutation' | number;
#root?: ElementHandle<Node>;
#fn: string;
#args: unknown[];
#timeout?: NodeJS.Timeout;
#genericError = new Error('Waiting failed');
#timeoutError?: TimeoutError;
#result = Deferred.create<HandleFor<T>>();
#poller?: JSHandle<Poller<T>>;
#signal?: AbortSignal;
#reruns: AbortController[] = [];
constructor(
world: Realm,
options: WaitTaskOptions,
fn: ((...args: unknown[]) => Promise<T>) | string,
...args: unknown[]
) {
this.#world = world;
this.#polling = options.polling;
this.#root = options.root;
this.#signal = options.signal;
this.#signal?.addEventListener('abort', this.#onAbortSignal, {
once: true,
});
switch (typeof fn) {
case 'string':
this.#fn = `() => {return (${fn});}`;
break;
default:
this.#fn = stringifyFunction(fn);
break;
}
this.#args = args;
this.#world.taskManager.add(this);
if (options.timeout) {
this.#timeoutError = new TimeoutError(
`Waiting failed: ${options.timeout}ms exceeded`,
);
this.#timeout = setTimeout(() => {
void this.terminate(this.#timeoutError);
}, options.timeout);
}
void this.rerun();
}
get result(): Promise<HandleFor<T>> {
return this.#result.valueOrThrow();
}
async rerun(): Promise<void> {
for (const prev of this.#reruns) {
prev.abort();
}
this.#reruns.length = 0;
const controller = new AbortController();
this.#reruns.push(controller);
try {
switch (this.#polling) {
case 'raf':
this.#poller = await this.#world.evaluateHandle(
({RAFPoller, createFunction}, fn, ...args) => {
const fun = createFunction(fn);
return new RAFPoller(() => {
return fun(...args) as Promise<T>;
});
},
LazyArg.create(context => {
return context.puppeteerUtil;
}),
this.#fn,
...this.#args,
);
break;
case 'mutation':
this.#poller = await this.#world.evaluateHandle(
({MutationPoller, createFunction}, root, fn, ...args) => {
const fun = createFunction(fn);
return new MutationPoller(() => {
return fun(...args) as Promise<T>;
}, root || document);
},
LazyArg.create(context => {
return context.puppeteerUtil;
}),
this.#root,
this.#fn,
...this.#args,
);
break;
default:
this.#poller = await this.#world.evaluateHandle(
({IntervalPoller, createFunction}, ms, fn, ...args) => {
const fun = createFunction(fn);
return new IntervalPoller(() => {
return fun(...args) as Promise<T>;
}, ms);
},
LazyArg.create(context => {
return context.puppeteerUtil;
}),
this.#polling,
this.#fn,
...this.#args,
);
break;
}
await this.#poller.evaluate(poller => {
void poller.start();
});
const result = await this.#poller.evaluateHandle(poller => {
return poller.result();
});
this.#result.resolve(result);
await this.terminate();
} catch (error) {
if (controller.signal.aborted) {
return;
}
const badError = this.getBadError(error);
if (badError) {
this.#genericError.cause = badError;
await this.terminate(this.#genericError);
}
}
}
async terminate(error?: Error): Promise<void> {
this.#world.taskManager.delete(this);
this.#signal?.removeEventListener('abort', this.#onAbortSignal);
clearTimeout(this.#timeout);
if (error && !this.#result.finished()) {
this.#result.reject(error);
}
if (this.#poller) {
try {
await this.#poller.evaluate(async poller => {
await poller.stop();
});
if (this.#poller) {
await this.#poller.dispose();
this.#poller = undefined;
}
} catch {
// Ignore errors since they most likely come from low-level cleanup.
}
}
}
/**
* Not all errors lead to termination. They usually imply we need to rerun the task.
*/
getBadError(error: unknown): Error | undefined {
if (isErrorLike(error)) {
// When frame is detached the task should have been terminated by the IsolatedWorld.
// This can fail if we were adding this task while the frame was detached,
// so we terminate here instead.
if (
error.message.includes(
'Execution context is not available in detached frame',
)
) {
return new Error('Waiting failed: Frame detached');
}
// When the page is navigated, the promise is rejected.
// We will try again in the new execution context.
if (error.message.includes('Execution context was destroyed')) {
return;
}
// We could have tried to evaluate in a context which was already
// destroyed.
if (error.message.includes('Cannot find context with specified id')) {
return;
}
// Errors coming from WebDriver BiDi. TODO: Adjust messages after
// https://github.com/w3c/webdriver-bidi/issues/540 is resolved.
if (error.message.includes('DiscardedBrowsingContextError')) {
return;
}
return error;
}
return new Error('WaitTask failed with an error', {
cause: error,
});
}
#onAbortSignal = () => {
void this.terminate(this.#signal?.reason);
};
}
/**
* @internal
*/
export class TaskManager {
#tasks: Set<WaitTask> = new Set<WaitTask>();
add(task: WaitTask<any>): void {
this.#tasks.add(task);
}
delete(task: WaitTask<any>): void {
this.#tasks.delete(task);
}
terminateAll(error?: Error): void {
for (const task of this.#tasks) {
void task.terminate(error);
}
this.#tasks.clear();
}
async rerunAll(): Promise<void> {
await Promise.all(
[...this.#tasks].map(task => {
return task.rerun();
}),
);
}
}

View File

@@ -0,0 +1,35 @@
/**
* @license
* Copyright 2023 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import {
QueryHandler,
type QuerySelectorAll,
type QuerySelector,
} from './QueryHandler.js';
/**
* @internal
*/
export class XPathQueryHandler extends QueryHandler {
static override querySelectorAll: QuerySelectorAll = (
element,
selector,
{xpathQuerySelectorAll},
) => {
return xpathQuerySelectorAll(element, selector);
};
static override querySelector: QuerySelector = (
element: Node,
selector: string,
{xpathQuerySelectorAll},
) => {
for (const result of xpathQuerySelectorAll(element, selector, 1)) {
return result;
}
return null;
};
}

43
node_modules/puppeteer-core/src/common/common.ts generated vendored Normal file
View File

@@ -0,0 +1,43 @@
/**
* @license
* Copyright 2022 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
export * from './BrowserWebSocketTransport.js';
export * from './CallbackRegistry.js';
export * from './AriaQueryHandler.js';
export type * from './Configuration.js';
export type * from './ConnectionTransport.js';
export type * from './ConnectOptions.js';
export * from './ConsoleMessage.js';
export type * from './Cookie.js';
export * from './CustomQueryHandler.js';
export * from './Debug.js';
export * from './Device.js';
export * from './Errors.js';
export * from './EventEmitter.js';
export * from './FileChooser.js';
export * from './GetQueryHandler.js';
export * from './HandleIterator.js';
export * from './LazyArg.js';
export * from './NetworkManagerEvents.js';
export * from './PDFOptions.js';
export * from './PierceQueryHandler.js';
export * from './PQueryHandler.js';
export type * from './SupportedBrowser.js';
export * from './PSelectorParser.js';
export * from './Puppeteer.js';
export * from './QueryHandler.js';
export * from './ScriptInjector.js';
export * from './SecurityDetails.js';
export * from './TaskQueue.js';
export * from './TextQueryHandler.js';
export * from './TimeoutSettings.js';
export type * from './types.js';
export * from './USKeyboardLayout.js';
export * from './util.js';
export type * from './Viewport.js';
export * from './WaitTask.js';
export * from './XPathQueryHandler.js';
export type * from './DownloadBehavior.js';

128
node_modules/puppeteer-core/src/common/types.ts generated vendored Normal file
View File

@@ -0,0 +1,128 @@
/**
* @license
* Copyright 2020 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {ParseSelector} from 'typed-query-selector/parser.js';
import type {ElementHandle} from '../api/ElementHandle.js';
import type {JSHandle} from '../api/JSHandle.js';
import type {LazyArg} from './LazyArg.js';
/**
* @public
*/
export type AwaitablePredicate<T> = (value: T) => Awaitable<boolean>;
/**
* @public
*/
export interface Moveable {
/**
* Moves the resource when 'using'.
*/
move(): this;
}
/**
* @internal
*/
export interface Disposed {
get disposed(): boolean;
}
/**
* @internal
*/
export interface BindingPayload {
type: string;
name: string;
seq: number;
args: unknown[];
/**
* Determines whether the arguments of the payload are trivial.
*/
isTrivial: boolean;
}
/**
* @internal
*/
export type AwaitableIterator<T> = Iterator<T> | AsyncIterator<T>;
/**
* @public
*/
export type AwaitableIterable<T> = Iterable<T> | AsyncIterable<T>;
/**
* @public
*/
export type Awaitable<T> = T | PromiseLike<T>;
/**
* @public
*/
export type HandleFor<T> = T extends Node ? ElementHandle<T> : JSHandle<T>;
/**
* @public
*/
export type HandleOr<T> = HandleFor<T> | JSHandle<T> | T;
/**
* @public
*/
export type FlattenHandle<T> = T extends HandleOr<infer U> ? U : never;
/**
* @internal
*/
export type FlattenLazyArg<T> = T extends LazyArg<infer U> ? U : T;
/**
* @internal
*/
export type InnerLazyParams<T extends unknown[]> = {
[K in keyof T]: FlattenLazyArg<T[K]>;
};
/**
* @public
*/
export type InnerParams<T extends unknown[]> = {
[K in keyof T]: FlattenHandle<T[K]>;
};
/**
* @public
*/
export type ElementFor<
TagName extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap,
> = TagName extends keyof HTMLElementTagNameMap
? HTMLElementTagNameMap[TagName]
: TagName extends keyof SVGElementTagNameMap
? SVGElementTagNameMap[TagName]
: never;
/**
* @public
*/
export type EvaluateFunc<T extends unknown[]> = (
...params: InnerParams<T>
) => Awaitable<unknown>;
/**
* @public
*/
export type EvaluateFuncWith<V, T extends unknown[]> = (
...params: [V, ...InnerParams<T>]
) => Awaitable<unknown>;
/**
* @public
*/
export type NodeFor<ComplexSelector extends string> =
ParseSelector<ComplexSelector>;

477
node_modules/puppeteer-core/src/common/util.ts generated vendored Normal file
View File

@@ -0,0 +1,477 @@
/**
* @license
* Copyright 2017 Google Inc.
* SPDX-License-Identifier: Apache-2.0
*/
import type {OperatorFunction} from '../../third_party/rxjs/rxjs.js';
import {
filter,
from,
fromEvent,
map,
mergeMap,
NEVER,
Observable,
timer,
} from '../../third_party/rxjs/rxjs.js';
import type {CDPSession} from '../api/CDPSession.js';
import {environment} from '../environment.js';
import {assert} from '../util/assert.js';
import {mergeUint8Arrays, stringToTypedArray} from '../util/encoding.js';
import {packageVersion} from '../util/version.js';
import {debug} from './Debug.js';
import {TimeoutError} from './Errors.js';
import type {EventEmitter, EventType} from './EventEmitter.js';
import type {
LowerCasePaperFormat,
ParsedPDFOptions,
PDFOptions,
} from './PDFOptions.js';
import {paperFormats} from './PDFOptions.js';
/**
* @internal
*/
export const debugError = debug('puppeteer:error');
/**
* @internal
*/
export const DEFAULT_VIEWPORT = Object.freeze({width: 800, height: 600});
/**
* @internal
*/
const SOURCE_URL = Symbol('Source URL for Puppeteer evaluation scripts');
/**
* @internal
*/
export class PuppeteerURL {
static INTERNAL_URL = 'pptr:internal';
static fromCallSite(
functionName: string,
site: NodeJS.CallSite,
): PuppeteerURL {
const url = new PuppeteerURL();
url.#functionName = functionName;
url.#siteString = site.toString();
return url;
}
static parse = (url: string): PuppeteerURL => {
url = url.slice('pptr:'.length);
const [functionName = '', siteString = ''] = url.split(';');
const puppeteerUrl = new PuppeteerURL();
puppeteerUrl.#functionName = functionName;
puppeteerUrl.#siteString = decodeURIComponent(siteString);
return puppeteerUrl;
};
static isPuppeteerURL = (url: string): boolean => {
return url.startsWith('pptr:');
};
#functionName!: string;
#siteString!: string;
get functionName(): string {
return this.#functionName;
}
get siteString(): string {
return this.#siteString;
}
toString(): string {
return `pptr:${[
this.#functionName,
encodeURIComponent(this.#siteString),
].join(';')}`;
}
}
/**
* @internal
*/
export const withSourcePuppeteerURLIfNone = <T extends NonNullable<unknown>>(
functionName: string,
object: T,
): T => {
if (Object.prototype.hasOwnProperty.call(object, SOURCE_URL)) {
return object;
}
const original = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => {
// First element is the function.
// Second element is the caller of this function.
// Third element is the caller of the caller of this function
// which is precisely what we want.
return stack[2];
};
const site = new Error().stack as unknown as NodeJS.CallSite;
Error.prepareStackTrace = original;
return Object.assign(object, {
[SOURCE_URL]: PuppeteerURL.fromCallSite(functionName, site),
});
};
/**
* @internal
*/
export const getSourcePuppeteerURLIfAvailable = <
T extends NonNullable<unknown>,
>(
object: T,
): PuppeteerURL | undefined => {
if (Object.prototype.hasOwnProperty.call(object, SOURCE_URL)) {
return object[SOURCE_URL as keyof T] as PuppeteerURL;
}
return undefined;
};
/**
* @internal
*/
export const isString = (obj: unknown): obj is string => {
return typeof obj === 'string' || obj instanceof String;
};
/**
* @internal
*/
export const isNumber = (obj: unknown): obj is number => {
return typeof obj === 'number' || obj instanceof Number;
};
/**
* @internal
*/
export const isPlainObject = (obj: unknown): obj is Record<any, unknown> => {
return typeof obj === 'object' && obj?.constructor === Object;
};
/**
* @internal
*/
export const isRegExp = (obj: unknown): obj is RegExp => {
return typeof obj === 'object' && obj?.constructor === RegExp;
};
/**
* @internal
*/
export const isDate = (obj: unknown): obj is Date => {
return typeof obj === 'object' && obj?.constructor === Date;
};
/**
* @internal
*/
export function evaluationString(
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
fun: Function | string,
...args: unknown[]
): string {
if (isString(fun)) {
assert(args.length === 0, 'Cannot evaluate a string with arguments');
return fun;
}
function serializeArgument(arg: unknown): string {
if (Object.is(arg, undefined)) {
return 'undefined';
}
return JSON.stringify(arg);
}
return `(${fun})(${args.map(serializeArgument).join(',')})`;
}
/**
* @internal
*/
export async function getReadableAsTypedArray(
readable: ReadableStream<Uint8Array>,
path?: string,
): Promise<Uint8Array | null> {
const buffers: Uint8Array[] = [];
const reader = readable.getReader();
if (path) {
const fileHandle = await environment.value.fs.promises.open(path, 'w+');
try {
while (true) {
const {done, value} = await reader.read();
if (done) {
break;
}
buffers.push(value);
await fileHandle.writeFile(value);
}
} finally {
await fileHandle.close();
}
} else {
while (true) {
const {done, value} = await reader.read();
if (done) {
break;
}
buffers.push(value);
}
}
try {
const concat = mergeUint8Arrays(buffers);
if (concat.length === 0) {
return null;
}
return concat;
} catch (error) {
debugError(error);
return null;
}
}
/**
* @internal
*/
export async function getReadableFromProtocolStream(
client: CDPSession,
handle: string,
): Promise<ReadableStream<Uint8Array>> {
return new ReadableStream({
async pull(controller) {
const {data, base64Encoded, eof} = await client.send('IO.read', {
handle,
});
controller.enqueue(stringToTypedArray(data, base64Encoded ?? false));
if (eof) {
await client.send('IO.close', {handle});
controller.close();
}
},
});
}
/**
* @internal
*/
export function validateDialogType(
type: string,
): 'alert' | 'confirm' | 'prompt' | 'beforeunload' {
let dialogType = null;
const validDialogTypes = new Set([
'alert',
'confirm',
'prompt',
'beforeunload',
]);
if (validDialogTypes.has(type)) {
dialogType = type;
}
assert(dialogType, `Unknown javascript dialog type: ${type}`);
return dialogType as 'alert' | 'confirm' | 'prompt' | 'beforeunload';
}
/**
* @internal
*/
export function timeout(ms: number, cause?: Error): Observable<never> {
return ms === 0
? NEVER
: timer(ms).pipe(
map(() => {
throw new TimeoutError(`Timed out after waiting ${ms}ms`, {cause});
}),
);
}
/**
* @internal
*/
export const UTILITY_WORLD_NAME =
'__puppeteer_utility_world__' + packageVersion;
/**
* @internal
*/
export const SOURCE_URL_REGEX =
/^[\x20\t]*\/\/[@#] sourceURL=\s{0,10}(\S*?)\s{0,10}$/m;
/**
* @internal
*/
export function getSourceUrlComment(url: string): string {
return `//# sourceURL=${url}`;
}
/**
* @internal
*/
export const NETWORK_IDLE_TIME = 500;
/**
* @internal
*/
export function parsePDFOptions(
options: PDFOptions = {},
lengthUnit: 'in' | 'cm' = 'in',
): ParsedPDFOptions {
const defaults: Omit<ParsedPDFOptions, 'width' | 'height' | 'margin'> = {
scale: 1,
displayHeaderFooter: false,
headerTemplate: '',
footerTemplate: '',
printBackground: false,
landscape: false,
pageRanges: '',
preferCSSPageSize: false,
omitBackground: false,
outline: false,
tagged: true,
waitForFonts: true,
};
let width = 8.5;
let height = 11;
if (options.format) {
const format =
paperFormats[options.format.toLowerCase() as LowerCasePaperFormat][
lengthUnit
];
assert(format, 'Unknown paper format: ' + options.format);
width = format.width;
height = format.height;
} else {
width = convertPrintParameterToInches(options.width, lengthUnit) ?? width;
height =
convertPrintParameterToInches(options.height, lengthUnit) ?? height;
}
const margin = {
top: convertPrintParameterToInches(options.margin?.top, lengthUnit) || 0,
left: convertPrintParameterToInches(options.margin?.left, lengthUnit) || 0,
bottom:
convertPrintParameterToInches(options.margin?.bottom, lengthUnit) || 0,
right:
convertPrintParameterToInches(options.margin?.right, lengthUnit) || 0,
};
// Quirk https://bugs.chromium.org/p/chromium/issues/detail?id=840455#c44
if (options.outline) {
options.tagged = true;
}
return {
...defaults,
...options,
width,
height,
margin,
};
}
/**
* @internal
*/
export const unitToPixels = {
px: 1,
in: 96,
cm: 37.8,
mm: 3.78,
};
function convertPrintParameterToInches(
parameter?: string | number,
lengthUnit: 'in' | 'cm' = 'in',
): number | undefined {
if (typeof parameter === 'undefined') {
return undefined;
}
let pixels;
if (isNumber(parameter)) {
// Treat numbers as pixel values to be aligned with phantom's paperSize.
pixels = parameter;
} else if (isString(parameter)) {
const text = parameter;
let unit = text.substring(text.length - 2).toLowerCase();
let valueText = '';
if (unit in unitToPixels) {
valueText = text.substring(0, text.length - 2);
} else {
// In case of unknown unit try to parse the whole parameter as number of pixels.
// This is consistent with phantom's paperSize behavior.
unit = 'px';
valueText = text;
}
const value = Number(valueText);
assert(!isNaN(value), 'Failed to parse parameter value: ' + text);
pixels = value * unitToPixels[unit as keyof typeof unitToPixels];
} else {
throw new Error(
'page.pdf() Cannot handle parameter type: ' + typeof parameter,
);
}
return pixels / unitToPixels[lengthUnit];
}
/**
* @internal
*/
export function fromEmitterEvent<
Events extends Record<EventType, unknown>,
Event extends keyof Events,
>(emitter: EventEmitter<Events>, eventName: Event): Observable<Events[Event]> {
return new Observable(subscriber => {
const listener = (event: Events[Event]) => {
subscriber.next(event);
};
emitter.on(eventName, listener);
return () => {
emitter.off(eventName, listener);
};
});
}
/**
* @internal
*/
export function fromAbortSignal(
signal?: AbortSignal,
cause?: Error,
): Observable<never> {
return signal
? fromEvent(signal, 'abort').pipe(
map(() => {
if (signal.reason instanceof Error) {
signal.reason.cause = cause;
throw signal.reason;
}
throw new Error(signal.reason, {cause});
}),
)
: NEVER;
}
/**
* @internal
*/
export function filterAsync<T>(
predicate: (value: T) => boolean | PromiseLike<boolean>,
): OperatorFunction<T, T> {
return mergeMap<T, Observable<T>>((value): Observable<T> => {
return from(Promise.resolve(predicate(value))).pipe(
filter(isMatch => {
return isMatch;
}),
map(() => {
return value;
}),
);
});
}