Add .gitignore to exclude all node packages and lock files

This commit is contained in:
Adolfo Reyna
2026-02-23 21:56:04 -05:00
parent faae96c9ed
commit dcc5c6c044
9747 changed files with 1555105 additions and 2 deletions
@@ -0,0 +1,97 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BrowserLog } from '../browser-log';
import type { LogLevel } from '../log-level';
import type { OnLog, Page } from './BrowserPage';
import type { BrowserRunner } from './BrowserRunner';
import type { Connection } from './Connection';
import { EventEmitter } from './EventEmitter';
import type { Viewport } from './PuppeteerViewport';
import type { SourceMapGetter } from './source-map-getter';
import { Target } from './Target';
interface WaitForTargetOptions {
timeout?: number;
}
export declare const enum BrowserEmittedEvents {
TargetChanged = "targetchanged",
TargetCreated = "targetcreated",
Closed = "closed",
ClosedSilent = "closed-silent"
}
export declare class HeadlessBrowser extends EventEmitter {
#private;
static create({ defaultViewport, timeout, userDataDir, args, executablePath, logLevel, indent }: {
defaultViewport: Viewport;
timeout: number;
userDataDir: string;
args: string[];
executablePath: string;
logLevel: LogLevel;
indent: boolean;
}): Promise<HeadlessBrowser>;
connection: Connection;
id: string;
runner: BrowserRunner;
get _targets(): Map<string, Target>;
constructor({ connection, defaultViewport, runner }: {
connection: Connection;
defaultViewport: Viewport;
runner: BrowserRunner;
});
browserContexts(): BrowserContext[];
newPage({ context, logLevel, indent, pageIndex, onBrowserLog, onLog }: {
context: SourceMapGetter;
logLevel: LogLevel;
indent: boolean;
pageIndex: number;
onBrowserLog: null | ((log: BrowserLog) => void);
onLog: OnLog;
}): Promise<Page>;
_createPageInContext({ context, logLevel, indent, pageIndex, onBrowserLog, onLog }: {
context: SourceMapGetter;
logLevel: LogLevel;
indent: boolean;
pageIndex: number;
onBrowserLog: null | ((log: BrowserLog) => void);
onLog: OnLog;
}): Promise<Page>;
targets(): Target[];
waitForTarget(predicate: (x: Target) => boolean | Promise<boolean>, options?: WaitForTargetOptions): Promise<Target>;
pages(): Promise<Page[]>;
close({ silent }: {
silent: boolean;
}): Promise<void>;
disconnect(): void;
}
export declare class BrowserContext extends EventEmitter {
#private;
constructor(browser: HeadlessBrowser);
targets(): Target[];
waitForTarget(predicate: (x: Target) => boolean | Promise<boolean>, options?: {
timeout?: number;
}): Promise<Target>;
pages(): Promise<Page[]>;
newPage({ context, logLevel, indent, pageIndex, onBrowserLog, onLog }: {
context: SourceMapGetter;
logLevel: LogLevel;
indent: boolean;
pageIndex: number;
onBrowserLog: null | ((log: BrowserLog) => void);
onLog: OnLog;
}): Promise<Page>;
browser(): HeadlessBrowser;
}
export {};
@@ -0,0 +1,237 @@
"use strict";
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowserContext = exports.HeadlessBrowser = void 0;
const assert_1 = require("./assert");
const BrowserRunner_1 = require("./BrowserRunner");
const EventEmitter_1 = require("./EventEmitter");
const Target_1 = require("./Target");
const util_1 = require("./util");
class HeadlessBrowser extends EventEmitter_1.EventEmitter {
static async create({ defaultViewport, timeout, userDataDir, args, executablePath, logLevel, indent, }) {
const runner = await (0, BrowserRunner_1.makeBrowserRunner)({
executablePath,
processArguments: args,
userDataDir,
indent,
logLevel,
timeout,
});
const browser = new HeadlessBrowser({
connection: runner.connection,
defaultViewport,
runner,
});
await runner.connection.send('Target.setDiscoverTargets', { discover: true });
return browser;
}
#defaultViewport;
connection;
#defaultContext;
#contexts;
#targets;
id;
runner;
get _targets() {
return this.#targets;
}
constructor({ connection, defaultViewport, runner, }) {
super();
this.#defaultViewport = defaultViewport;
this.connection = connection;
this.id = Math.random().toString(36).substring(2, 15);
this.#defaultContext = new BrowserContext(this);
this.#contexts = new Map();
this.#targets = new Map();
this.connection.on('Target.targetCreated', this.#targetCreated.bind(this));
this.connection.on('Target.targetDestroyed', this.#targetDestroyed.bind(this));
this.connection.on('Target.targetInfoChanged', this.#targetInfoChanged.bind(this));
this.runner = runner;
}
browserContexts() {
return [this.#defaultContext, ...Array.from(this.#contexts.values())];
}
async #targetCreated(event) {
var _a;
const { targetInfo } = event;
const { browserContextId } = targetInfo;
const context = browserContextId && this.#contexts.has(browserContextId)
? this.#contexts.get(browserContextId)
: this.#defaultContext;
if (!context) {
throw new Error('Missing browser context');
}
const target = new Target_1.Target(targetInfo, context, () => {
return this.connection.createSession(targetInfo);
}, (_a = this.#defaultViewport) !== null && _a !== void 0 ? _a : null);
(0, assert_1.assert)(!this.#targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
this.#targets.set(event.targetInfo.targetId, target);
if (await target._initializedPromise) {
this.emit("targetcreated" /* BrowserEmittedEvents.TargetCreated */, target);
}
}
#targetDestroyed(event) {
const target = this.#targets.get(event.targetId);
if (!target) {
throw new Error(`Missing target in _targetDestroyed (id = ${event.targetId})`);
}
target._initializedCallback(false);
this.#targets.delete(event.targetId);
target._closedCallback();
}
#targetInfoChanged(event) {
const target = this.#targets.get(event.targetInfo.targetId);
if (!target) {
throw new Error(`Missing target in targetInfoChanged (id = ${event.targetInfo.targetId})`);
}
const previousURL = target.url();
const wasInitialized = target._isInitialized;
target._targetInfoChanged(event.targetInfo);
if (wasInitialized && previousURL !== target.url()) {
this.emit("targetchanged" /* BrowserEmittedEvents.TargetChanged */, target);
}
}
newPage({ context, logLevel, indent, pageIndex, onBrowserLog, onLog, }) {
return this.#defaultContext.newPage({
context,
logLevel,
indent,
pageIndex,
onBrowserLog,
onLog,
});
}
async _createPageInContext({ context, logLevel, indent, pageIndex, onBrowserLog, onLog, }) {
const { value: { targetId }, } = await this.connection.send('Target.createTarget', {
url: 'about:blank',
browserContextId: undefined,
});
const target = this.#targets.get(targetId);
if (!target) {
throw new Error(`Missing target for page (id = ${targetId})`);
}
const initialized = await target._initializedPromise;
if (!initialized) {
throw new Error(`Failed to create target for page (id = ${targetId})`);
}
const page = await target.page({
sourceMapGetter: context,
logLevel,
indent,
pageIndex,
onBrowserLog,
onLog,
});
if (!page) {
throw new Error(`Failed to create a page for context`);
}
return page;
}
targets() {
return Array.from(this.#targets.values()).filter((target) => {
return target._isInitialized;
});
}
async waitForTarget(predicate, options = {}) {
const { timeout = 30000 } = options;
let resolve;
let isResolved = false;
const targetPromise = new Promise((x) => {
resolve = x;
});
this.on("targetcreated" /* BrowserEmittedEvents.TargetCreated */, check);
this.on("targetchanged" /* BrowserEmittedEvents.TargetChanged */, check);
try {
if (!timeout) {
return await targetPromise;
}
this.targets().forEach(check);
return await (0, util_1.waitWithTimeout)(targetPromise, 'target', timeout, this);
}
finally {
this.off("targetcreated" /* BrowserEmittedEvents.TargetCreated */, check);
this.off("targetchanged" /* BrowserEmittedEvents.TargetChanged */, check);
}
async function check(target) {
if ((await predicate(target)) && !isResolved) {
isResolved = true;
resolve(target);
}
}
}
async pages() {
const contextPages = await Promise.all(this.browserContexts().map((context) => {
return context.pages();
}));
// Flatten array.
return contextPages.reduce((acc, x) => {
return acc.concat(x);
}, []);
}
async close({ silent }) {
await this.runner.closeProcess();
(await this.pages()).forEach((page) => {
page.emit("disposed" /* PageEmittedEvents.Disposed */);
page.closed = true;
});
this.disconnect();
this.emit(silent ? "closed-silent" /* BrowserEmittedEvents.ClosedSilent */ : "closed" /* BrowserEmittedEvents.Closed */);
}
disconnect() {
this.connection.dispose();
}
}
exports.HeadlessBrowser = HeadlessBrowser;
class BrowserContext extends EventEmitter_1.EventEmitter {
#browser;
constructor(browser) {
super();
this.#browser = browser;
}
targets() {
return this.#browser.targets().filter((target) => {
return target.browserContext() === this;
});
}
waitForTarget(predicate, options = {}) {
return this.#browser.waitForTarget((target) => {
return target.browserContext() === this && predicate(target);
}, options);
}
async pages() {
const pages = await Promise.all(this.targets()
.filter((target) => target.type() === 'page')
.map((target) => target.expectPage()));
return pages.filter((page) => {
return Boolean(page);
});
}
newPage({ context, logLevel, indent, pageIndex, onBrowserLog, onLog, }) {
return this.#browser._createPageInContext({
context,
logLevel,
indent,
pageIndex,
onBrowserLog,
onLog,
});
}
browser() {
return this.#browser;
}
}
exports.BrowserContext = BrowserContext;
@@ -0,0 +1,33 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { DownloadBrowserProgressFn } from '../options/on-browser-download';
import { TESTED_VERSION } from './get-chrome-download-url';
export { TESTED_VERSION };
interface BrowserFetcherRevisionInfo {
folderPath: string;
executablePath: string;
url: string;
local: boolean;
}
export declare const readVersionFile: (chromeMode: "chrome-for-testing" | "headless-shell") => string | null;
export declare const downloadBrowser: ({ logLevel, indent, onProgress, version, chromeMode, }: {
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
indent: boolean;
onProgress: DownloadBrowserProgressFn;
version: string | null;
chromeMode: "chrome-for-testing" | "headless-shell";
}) => Promise<BrowserFetcherRevisionInfo | undefined>;
export declare const getRevisionInfo: (chromeMode: "chrome-for-testing" | "headless-shell") => BrowserFetcherRevisionInfo;
@@ -0,0 +1,246 @@
"use strict";
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRevisionInfo = exports.downloadBrowser = exports.readVersionFile = exports.TESTED_VERSION = void 0;
const fs = __importStar(require("node:fs"));
const os = __importStar(require("node:os"));
const path = __importStar(require("node:path"));
const extract_zip_1 = __importDefault(require("extract-zip"));
const node_util_1 = require("node:util");
const download_file_1 = require("../assets/download-file");
const make_file_executable_1 = require("../compositor/make-file-executable");
const get_chrome_download_url_1 = require("./get-chrome-download-url");
Object.defineProperty(exports, "TESTED_VERSION", { enumerable: true, get: function () { return get_chrome_download_url_1.TESTED_VERSION; } });
const get_download_destination_1 = require("./get-download-destination");
const mkdirAsync = fs.promises.mkdir;
const unlinkAsync = (0, node_util_1.promisify)(fs.unlink.bind(fs));
function existsAsync(filePath) {
return new Promise((resolve) => {
fs.access(filePath, (err) => {
return resolve(!err);
});
});
}
const getPlatform = () => {
const platform = os.platform();
switch (platform) {
case 'darwin':
return os.arch() === 'arm64' ? 'mac-arm64' : 'mac-x64';
case 'linux':
return os.arch() === 'arm64' ? 'linux-arm64' : 'linux64';
case 'win32':
return 'win64';
default:
throw new Error('Unsupported platform: ' + platform);
}
};
const getDownloadsFolder = (chromeMode) => {
const destination = chromeMode === 'headless-shell'
? 'chrome-headless-shell'
: 'chrome-for-testing';
return path.join((0, get_download_destination_1.getDownloadsCacheDir)(), destination);
};
const getVersionFilePath = (chromeMode) => {
const downloadsFolder = getDownloadsFolder(chromeMode);
return path.join(downloadsFolder, 'VERSION');
};
const getExpectedVersion = (version, _chromeMode) => {
if (version) {
return version;
}
return get_chrome_download_url_1.TESTED_VERSION;
};
const readVersionFile = (chromeMode) => {
const versionFilePath = getVersionFilePath(chromeMode);
try {
return fs.readFileSync(versionFilePath, 'utf-8').trim();
}
catch (_a) {
return null;
}
};
exports.readVersionFile = readVersionFile;
const writeVersionFile = (chromeMode, version) => {
const versionFilePath = getVersionFilePath(chromeMode);
fs.writeFileSync(versionFilePath, version);
};
const downloadBrowser = async ({ logLevel, indent, onProgress, version, chromeMode, }) => {
const platform = getPlatform();
const downloadURL = (0, get_chrome_download_url_1.getChromeDownloadUrl)({ platform, version, chromeMode });
const fileName = downloadURL.split('/').pop();
if (!fileName) {
throw new Error(`A malformed download URL was found: ${downloadURL}.`);
}
const downloadsFolder = getDownloadsFolder(chromeMode);
const archivePath = path.join(downloadsFolder, fileName);
const outputPath = getFolderPath(downloadsFolder, platform);
const expectedVersion = getExpectedVersion(version, chromeMode);
if (await existsAsync(outputPath)) {
const installedVersion = (0, exports.readVersionFile)(chromeMode);
if (installedVersion === expectedVersion) {
return (0, exports.getRevisionInfo)(chromeMode);
}
// VERSION file missing or mismatched - delete and re-download
fs.rmSync(outputPath, { recursive: true, force: true });
}
if (!(await existsAsync(downloadsFolder))) {
await mkdirAsync(downloadsFolder, {
recursive: true,
});
}
if (os.platform() !== 'darwin' &&
os.platform() !== 'linux' &&
os.arch() === 'arm64') {
throw new Error([
'Chrome Headless Shell is not available for Windows for arm64 architecture.',
].join('\n'));
}
(0, get_chrome_download_url_1.logDownloadUrl)({ url: downloadURL, logLevel, indent });
try {
await (0, download_file_1.downloadFile)({
url: downloadURL,
to: () => archivePath,
onProgress: (progress) => {
if (progress.totalSize === null || progress.percent === null) {
throw new Error('Expected totalSize and percent to be defined');
}
onProgress({
downloadedBytes: progress.downloaded,
totalSizeInBytes: progress.totalSize,
percent: progress.percent,
alreadyAvailable: false,
});
},
indent,
logLevel,
abortSignal: new AbortController().signal,
});
await (0, extract_zip_1.default)(archivePath, { dir: outputPath });
const possibleSubdirs = [
'chrome-linux',
'chrome-headless-shell-linux64',
'chromium-headless-shell-amazon-linux2023-arm64',
'chromium-headless-shell-amazon-linux2023-x64',
];
for (const subdir of possibleSubdirs) {
const chromeLinuxFolder = path.join(outputPath, subdir);
const chromePath = path.join(chromeLinuxFolder, 'chrome');
if (fs.existsSync(chromePath)) {
const chromeHeadlessShellPath = path.join(chromeLinuxFolder, 'chrome-headless-shell');
fs.renameSync(chromePath, chromeHeadlessShellPath);
}
if (fs.existsSync(chromeLinuxFolder)) {
const targetFolder = path.join(outputPath, 'chrome-headless-shell-' + platform);
if (chromeLinuxFolder !== targetFolder) {
fs.renameSync(chromeLinuxFolder, targetFolder);
}
}
}
}
catch (err) {
return Promise.reject(err);
}
finally {
if (await existsAsync(archivePath)) {
await unlinkAsync(archivePath);
}
}
writeVersionFile(chromeMode, expectedVersion);
const revisionInfo = (0, exports.getRevisionInfo)(chromeMode);
(0, make_file_executable_1.makeFileExecutableIfItIsNot)(revisionInfo.executablePath);
return revisionInfo;
};
exports.downloadBrowser = downloadBrowser;
const getFolderPath = (downloadsFolder, platform) => {
return path.resolve(downloadsFolder, platform);
};
const getExecutablePath = (chromeMode) => {
const downloadsFolder = getDownloadsFolder(chromeMode);
const platform = getPlatform();
const folderPath = getFolderPath(downloadsFolder, platform);
if (chromeMode === 'chrome-for-testing') {
if (platform === 'mac-arm64' || platform === 'mac-x64') {
return path.join(folderPath, `chrome-${platform}`, 'Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing');
}
if (platform === 'win64') {
return path.join(folderPath, 'chrome-win64', 'chrome.exe');
}
if (platform === 'linux64' || platform === 'linux-arm64') {
return path.join(folderPath, 'chrome-linux64', 'chrome');
}
throw new Error('unsupported platform' + platform);
}
if (chromeMode === 'headless-shell') {
return path.join(folderPath, `chrome-headless-shell-${platform}`, platform === 'win64'
? 'chrome-headless-shell.exe'
: platform === 'linux-arm64' || (0, get_chrome_download_url_1.isAmazonLinux2023)()
? 'headless_shell'
: 'chrome-headless-shell');
}
throw new Error('unsupported chrome mode' + chromeMode);
};
const getRevisionInfo = (chromeMode) => {
const executablePath = getExecutablePath(chromeMode);
const downloadsFolder = getDownloadsFolder(chromeMode);
const platform = getPlatform();
const folderPath = getFolderPath(downloadsFolder, platform);
const url = (0, get_chrome_download_url_1.getChromeDownloadUrl)({ platform, version: null, chromeMode });
const local = fs.existsSync(folderPath);
return {
executablePath,
folderPath,
local,
url,
};
};
exports.getRevisionInfo = getRevisionInfo;
@@ -0,0 +1,105 @@
import { BrowserLog } from '../browser-log';
import type { LogLevel } from '../log-level';
import type { HeadlessBrowser } from './Browser';
import type { CDPSession } from './Connection';
import { ConsoleMessage } from './ConsoleMessage';
import type { EvaluateFn, EvaluateFnReturnType, EvaluateHandleFn, SerializableOrJSHandle, UnwrapPromiseLike } from './EvalTypes';
import { EventEmitter } from './EventEmitter';
import type { Frame } from './FrameManager';
import type { HTTPResponse } from './HTTPResponse';
import type { JSHandle } from './JSHandle';
import type { Viewport } from './PuppeteerViewport';
import type { Target } from './Target';
import { TaskQueue } from './TaskQueue';
import type { SourceMapGetter } from './source-map-getter';
interface WaitForOptions {
timeout?: number;
}
export declare const enum PageEmittedEvents {
Error = "error",
Disposed = "disposed"
}
interface PageEventObject {
console: ConsoleMessage;
error: Error;
disposed: undefined;
}
export type OnLog = ({ logLevel, previewString, tag }: {
logLevel: LogLevel;
tag: string;
previewString: string;
}) => void;
export declare class Page extends EventEmitter {
#private;
id: string;
static _create({ client, target, defaultViewport, browser, sourceMapGetter, logLevel, indent, pageIndex, onBrowserLog, onLog }: {
client: CDPSession;
target: Target;
defaultViewport: Viewport;
browser: HeadlessBrowser;
sourceMapGetter: SourceMapGetter;
logLevel: LogLevel;
indent: boolean;
pageIndex: number;
onBrowserLog: null | ((log: BrowserLog) => void);
onLog: OnLog;
}): Promise<Page>;
closed: boolean;
browser: HeadlessBrowser;
screenshotTaskQueue: TaskQueue;
sourceMapGetter: SourceMapGetter;
logLevel: LogLevel;
indent: boolean;
pageIndex: number;
onBrowserLog: null | ((log: BrowserLog) => void);
onLog: OnLog;
constructor({ client, target, browser, sourceMapGetter, logLevel, indent, pageIndex, onBrowserLog, onLog }: {
client: CDPSession;
target: Target;
browser: HeadlessBrowser;
sourceMapGetter: SourceMapGetter;
logLevel: LogLevel;
indent: boolean;
pageIndex: number;
onBrowserLog: null | ((log: BrowserLog) => void);
onLog: OnLog;
});
/**
* Listen to page events.
*/
on<K extends keyof PageEventObject>(eventName: K, handler: (event: PageEventObject[K]) => void): EventEmitter;
once<K extends keyof PageEventObject>(eventName: K, handler: (event: PageEventObject[K]) => void): EventEmitter;
off<K extends keyof PageEventObject>(eventName: K, handler: (event: PageEventObject[K]) => void): EventEmitter;
/**
* @returns A target this page was created from.
*/
target(): Target;
_client(): CDPSession;
/**
* @returns The page's main frame.
* @remarks
* Page is guaranteed to have a main frame which persists during navigations.
*/
mainFrame(): Frame;
setViewport(viewport: Viewport): Promise<void>;
setDefaultNavigationTimeout(timeout: number): void;
setDefaultTimeout(timeout: number): void;
evaluateHandle<HandlerType extends JSHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandlerType>;
url(): string;
goto({ url, timeout, options }: {
url: string;
timeout: number;
options?: WaitForOptions & {
referer?: string;
};
}): Promise<HTTPResponse | null>;
bringToFront(): Promise<void>;
setAutoDarkModeOverride(): Promise<void>;
evaluate<T extends EvaluateFn>(pageFunction: T, ...args: SerializableOrJSHandle[]): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>>;
evaluateOnNewDocument(pageFunction: Function | string, ...args: unknown[]): Promise<void>;
close(options?: {
runBeforeUnload?: boolean;
}): Promise<void>;
setBrowserSourceMapGetter(context: SourceMapGetter): void;
}
export {};
@@ -0,0 +1,454 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Page = void 0;
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const no_react_1 = require("remotion/no-react");
const format_logs_1 = require("../format-logs");
const logger_1 = require("../logger");
const truthy_1 = require("../truthy");
const ConsoleMessage_1 = require("./ConsoleMessage");
const EventEmitter_1 = require("./EventEmitter");
const FrameManager_1 = require("./FrameManager");
const JSHandle_1 = require("./JSHandle");
const TaskQueue_1 = require("./TaskQueue");
const TimeoutSettings_1 = require("./TimeoutSettings");
const assert_1 = require("./assert");
const util_1 = require("./util");
const shouldHideWarning = (log) => {
// Mixed Content warnings caused by localhost should not be displayed
if (log.text.includes('Mixed Content:') &&
log.text.includes('http://localhost:')) {
return true;
}
return false;
};
const format = (eventType, args) => {
var _a, _b, _c, _d, _e, _f, _g, _h;
const previewString = args
.filter((a) => {
var _a;
return !(a.type === 'symbol' && ((_a = a.description) === null || _a === void 0 ? void 0 : _a.includes(`__remotion_`)));
})
.map((a) => (0, format_logs_1.formatRemoteObject)(a))
.filter(Boolean)
.join(' ');
let logLevelFromRemotionLog = null;
let tag = null;
for (const a of args) {
if (a.type === 'symbol' && ((_a = a.description) === null || _a === void 0 ? void 0 : _a.includes(`__remotion_level_`))) {
logLevelFromRemotionLog = (_d = (_c = (_b = a.description) === null || _b === void 0 ? void 0 : _b.split('__remotion_level_')) === null || _c === void 0 ? void 0 : _c[1]) === null || _d === void 0 ? void 0 : _d.replace(')', '');
}
if (a.type === 'symbol' && ((_e = a.description) === null || _e === void 0 ? void 0 : _e.includes(`__remotion_tag_`))) {
tag = (_h = (_g = (_f = a.description) === null || _f === void 0 ? void 0 : _f.split('__remotion_tag_')) === null || _g === void 0 ? void 0 : _g[1]) === null || _h === void 0 ? void 0 : _h.replace(')', '');
}
}
const logLevelFromEvent = eventType === 'debug'
? 'verbose'
: eventType === 'error'
? 'error'
: eventType === 'warning'
? 'warn'
: 'verbose';
return { previewString, logLevelFromRemotionLog, logLevelFromEvent, tag };
};
class Page extends EventEmitter_1.EventEmitter {
id;
static async _create({ client, target, defaultViewport, browser, sourceMapGetter, logLevel, indent, pageIndex, onBrowserLog, onLog, }) {
const page = new Page({
client,
target,
browser,
sourceMapGetter,
logLevel,
indent,
pageIndex,
onBrowserLog,
onLog,
});
await page.#initialize();
await page.setViewport(defaultViewport);
return page;
}
closed = false;
#client;
#target;
#timeoutSettings = new TimeoutSettings_1.TimeoutSettings();
#frameManager;
#pageBindings = new Map();
browser;
screenshotTaskQueue;
sourceMapGetter;
logLevel;
indent;
pageIndex;
onBrowserLog;
onLog;
constructor({ client, target, browser, sourceMapGetter, logLevel, indent, pageIndex, onBrowserLog, onLog, }) {
super();
this.#client = client;
this.#target = target;
this.#frameManager = new FrameManager_1.FrameManager(client, this, indent, logLevel);
this.screenshotTaskQueue = new TaskQueue_1.TaskQueue();
this.browser = browser;
this.id = String(Math.random());
this.sourceMapGetter = sourceMapGetter;
this.logLevel = logLevel;
this.indent = indent;
this.pageIndex = pageIndex;
this.onBrowserLog = onBrowserLog;
this.onLog = onLog;
client.on('Target.attachedToTarget', (event) => {
switch (event.targetInfo.type) {
case 'iframe':
break;
case 'worker':
break;
default:
// If we don't detach from service workers, they will never die.
// We still want to attach to workers for emitting events.
// We still want to attach to iframes so sessions may interact with them.
// We detach from all other types out of an abundance of caution.
// See https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/devtools_agent_host_impl.cc?ss=chromium&q=f:devtools%20-f:out%20%22::kTypePage%5B%5D%22
// for the complete list of available types.
client
.send('Target.detachFromTarget', {
sessionId: event.sessionId,
})
.catch((err) => logger_1.Log.error({ indent, logLevel }, err));
}
});
client.on('Runtime.consoleAPICalled', (event) => {
return this.#onConsoleAPI(event);
});
client.on('Runtime.bindingCalled', (event) => {
return this.#onBindingCalled(event);
});
client.on('Inspector.targetCrashed', () => {
return this.#onTargetCrashed();
});
client.on('Log.entryAdded', (event) => {
return this.#onLogEntryAdded(event);
});
}
#onConsole = (log) => {
var _a, _b;
var _c;
const stackTrace = log.stackTrace();
const { url, columnNumber, lineNumber } = (_c = stackTrace[0]) !== null && _c !== void 0 ? _c : {};
const logLevel = this.logLevel;
const indent = this.indent;
if (shouldHideWarning(log)) {
return;
}
(_a = this.onBrowserLog) === null || _a === void 0 ? void 0 : _a.call(this, {
stackTrace,
text: log.text,
type: log.type,
});
if ((url === null || url === void 0 ? void 0 : url.endsWith(no_react_1.NoReactInternals.bundleName)) &&
lineNumber &&
this.sourceMapGetter()) {
const origPosition = (_b = this.sourceMapGetter()) === null || _b === void 0 ? void 0 : _b.originalPositionFor({
column: columnNumber !== null && columnNumber !== void 0 ? columnNumber : 0,
line: lineNumber,
});
const file = [
origPosition === null || origPosition === void 0 ? void 0 : origPosition.source,
origPosition === null || origPosition === void 0 ? void 0 : origPosition.line,
origPosition === null || origPosition === void 0 ? void 0 : origPosition.column,
]
.filter(truthy_1.truthy)
.join(':');
const isDelayRenderClear = log.previewString.includes(no_react_1.NoReactInternals.DELAY_RENDER_CLEAR_TOKEN);
const tabInfo = `Tab ${this.pageIndex}`;
const tagInfo = [origPosition === null || origPosition === void 0 ? void 0 : origPosition.name, isDelayRenderClear ? null : file]
.filter(truthy_1.truthy)
.join('@');
const tag = [tabInfo, log.tag, log.tag ? null : tagInfo]
.filter(truthy_1.truthy)
.join(', ');
this.onLog({
logLevel: log.logLevel,
tag,
previewString: log.previewString,
});
}
else if (log.type === 'error') {
if (log.text.includes('Failed to load resource:')) {
logger_1.Log.error({ logLevel, tag: url, indent },
// Sometimes the log is like this:
// Failed to load resource: the server responded with a status of 404 ()
// We remove the empty parentheses.
log.text.replace(/\(\)$/, ''));
}
else {
logger_1.Log.error({ logLevel, tag: `console.${log.type}`, indent }, log.text);
}
}
else {
logger_1.Log.verbose({ logLevel, tag: `console.${log.type}`, indent }, log.text);
}
};
async #initialize() {
await Promise.all([
this.#frameManager.initialize(),
this.#client.send('Target.setAutoAttach', {
autoAttach: true,
waitForDebuggerOnStart: false,
flatten: true,
}),
this.#client.send('Performance.enable'),
this.#client.send('Log.enable'),
]);
}
/**
* Listen to page events.
*/
// Note: this method exists to define event typings and handle
// proper wireup of cooperative request interception. Actual event listening and
// dispatching is delegated to EventEmitter.
on(eventName, handler) {
return super.on(eventName, handler);
}
once(eventName, handler) {
// Note: this method only exists to define the types; we delegate the impl
// to EventEmitter.
return super.once(eventName, handler);
}
off(eventName, handler) {
return super.off(eventName, handler);
}
/**
* @returns A target this page was created from.
*/
target() {
return this.#target;
}
_client() {
return this.#client;
}
#onTargetCrashed() {
// This error message is being checked against in is-flaky-error.ts
this.emit('error', new Error('Page crashed!'));
}
#onLogEntryAdded(event) {
var _a;
const { level, text, args, source, url, lineNumber } = event.entry;
if (args) {
args.map((arg) => {
return (0, util_1.releaseObject)(this.#client, arg);
});
}
const { previewString, logLevelFromRemotionLog, logLevelFromEvent, tag } = format(level, args !== null && args !== void 0 ? args : []);
if (source !== 'worker') {
const message = new ConsoleMessage_1.ConsoleMessage({
type: level,
text,
args: [],
stackTraceLocations: [{ url, lineNumber }],
previewString,
logLevel: logLevelFromRemotionLog !== null && logLevelFromRemotionLog !== void 0 ? logLevelFromRemotionLog : logLevelFromEvent,
tag,
});
(_a = this.onBrowserLog) === null || _a === void 0 ? void 0 : _a.call(this, {
stackTrace: message.stackTrace(),
text: message.text,
type: message.type,
});
this.#onConsole(message);
}
}
/**
* @returns The page's main frame.
* @remarks
* Page is guaranteed to have a main frame which persists during navigations.
*/
mainFrame() {
return this.#frameManager.mainFrame();
}
async setViewport(viewport) {
const fromSurface = !process.env.DISABLE_FROM_SURFACE;
const request = fromSurface
? {
mobile: false,
width: viewport.width,
height: viewport.height,
deviceScaleFactor: viewport.deviceScaleFactor,
screenOrientation: {
angle: 0,
type: 'portraitPrimary',
},
}
: {
mobile: false,
width: viewport.width,
height: viewport.height,
deviceScaleFactor: 1,
screenHeight: viewport.height,
screenWidth: viewport.width,
scale: viewport.deviceScaleFactor,
viewport: {
height: viewport.height * viewport.deviceScaleFactor,
width: viewport.width * viewport.deviceScaleFactor,
scale: 1,
x: 0,
y: 0,
},
};
const { value } = await this.#client.send('Emulation.setDeviceMetricsOverride', request);
return value;
}
setDefaultNavigationTimeout(timeout) {
this.#timeoutSettings.setDefaultNavigationTimeout(timeout);
}
setDefaultTimeout(timeout) {
this.#timeoutSettings.setDefaultTimeout(timeout);
}
async evaluateHandle(pageFunction, ...args) {
const context = await this.mainFrame().executionContext();
return context.evaluateHandle(pageFunction, ...args);
}
#onConsoleAPI(event) {
if (event.executionContextId === 0) {
return;
}
const context = this.#frameManager.executionContextById(event.executionContextId, this.#client);
const values = event.args.map((arg) => {
return (0, JSHandle_1._createJSHandle)(context, arg);
});
this.#addConsoleMessage(event.type, values, event.stackTrace);
}
async #onBindingCalled(event) {
let payload;
try {
payload = JSON.parse(event.payload);
}
catch (_a) {
// The binding was either called by something in the page or it was
// called before our wrapper was initialized.
return;
}
const { type, name, seq, args } = payload;
if (type !== 'exposedFun' || !this.#pageBindings.has(name)) {
return;
}
let expression = null;
try {
const pageBinding = this.#pageBindings.get(name);
(0, assert_1.assert)(pageBinding);
const result = await pageBinding(...args);
expression = (0, util_1.pageBindingDeliverResultString)(name, seq, result);
}
catch (_error) {
if ((0, util_1.isErrorLike)(_error)) {
expression = (0, util_1.pageBindingDeliverErrorString)(name, seq, _error.message, _error.stack);
}
else {
expression = (0, util_1.pageBindingDeliverErrorValueString)(name, seq, _error);
}
}
await this.#client.send('Runtime.evaluate', {
expression,
contextId: event.executionContextId,
});
}
#addConsoleMessage(eventType, args, stackTrace) {
var _a, _b;
const textTokens = [];
for (const arg of args) {
const remoteObject = arg._remoteObject;
if (remoteObject.objectId) {
textTokens.push(arg.toString());
}
else {
textTokens.push((0, util_1.valueFromRemoteObject)(remoteObject));
}
}
const stackTraceLocations = [];
if (stackTrace) {
for (const callFrame of stackTrace.callFrames) {
stackTraceLocations.push({
url: callFrame.url,
lineNumber: callFrame.lineNumber,
columnNumber: callFrame.columnNumber,
});
}
}
const { previewString, logLevelFromRemotionLog, logLevelFromEvent, tag } = format(eventType, (_a = args.map((a) => a._remoteObject)) !== null && _a !== void 0 ? _a : []);
const logLevel = (_b = logLevelFromRemotionLog) !== null && _b !== void 0 ? _b : logLevelFromEvent;
const message = new ConsoleMessage_1.ConsoleMessage({
type: eventType,
text: textTokens.join(' '),
args,
stackTraceLocations,
previewString,
logLevel,
tag,
});
this.#onConsole(message);
}
url() {
return this.mainFrame().url();
}
goto({ url, timeout, options = {}, }) {
return this.#frameManager.mainFrame().goto(url, timeout, options);
}
async bringToFront() {
await this.#client.send('Page.bringToFront');
}
async setAutoDarkModeOverride() {
const result = await this.#client.send('Emulation.setEmulatedMedia', {
media: 'screen',
features: [
{
name: 'prefers-color-scheme',
value: 'dark',
},
],
});
console.log(result);
}
evaluate(pageFunction, ...args) {
return this.#frameManager.mainFrame().evaluate(pageFunction, ...args);
}
async evaluateOnNewDocument(pageFunction, ...args) {
const source = (0, util_1.evaluationString)(pageFunction, ...args);
await this.#client.send('Page.addScriptToEvaluateOnNewDocument', {
source,
});
}
async close(options = { runBeforeUnload: undefined }) {
const connection = this.#client.connection();
if (!connection) {
return;
}
const runBeforeUnload = Boolean(options.runBeforeUnload);
if (runBeforeUnload) {
await this.#client.send('Page.close');
}
else {
await connection.send('Target.closeTarget', {
targetId: this.#target._targetId,
});
await this.#target._isClosedPromise;
}
}
setBrowserSourceMapGetter(context) {
this.sourceMapGetter = context;
}
}
exports.Page = Page;
@@ -0,0 +1,32 @@
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Connection } from './Connection';
export declare const makeBrowserRunner: ({ executablePath, processArguments, userDataDir, logLevel, indent, timeout, }: {
executablePath: string;
processArguments: string[];
userDataDir: string;
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
indent: boolean;
timeout: number;
}) => Promise<{
listeners: (() => import("./EventEmitter").CommonEventEmitter)[];
deleteBrowserCaches: () => void;
forgetEventLoop: () => void;
rememberEventLoop: () => void;
connection: Connection;
closeProcess: () => Promise<void>;
}>;
export type BrowserRunner = Awaited<ReturnType<typeof makeBrowserRunner>>;
@@ -0,0 +1,303 @@
"use strict";
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeBrowserRunner = void 0;
const childProcess = __importStar(require("node:child_process"));
const node_path_1 = require("node:path");
const delete_directory_1 = require("../delete-directory");
const log_level_1 = require("../log-level");
const logger_1 = require("../logger");
const truthy_1 = require("../truthy");
const Connection_1 = require("./Connection");
const Errors_1 = require("./Errors");
const NodeWebSocketTransport_1 = require("./NodeWebSocketTransport");
const assert_1 = require("./assert");
const should_log_message_1 = require("./should-log-message");
const util_1 = require("./util");
const PROCESS_ERROR_EXPLANATION = `Puppeteer was unable to kill the process which ran the browser binary.
This means that, on future Puppeteer launches, Puppeteer might not be able to launch the browser.
Please check your open processes and ensure that the browser processes that Puppeteer launched have been killed.
If you think this is a bug, please report it on the Puppeteer issue tracker.`;
const makeBrowserRunner = async ({ executablePath, processArguments, userDataDir, logLevel, indent, timeout, }) => {
var _a, _b;
const dumpio = (0, log_level_1.isEqualOrBelowLogLevel)(logLevel, 'verbose');
const stdio = dumpio
? ['ignore', 'pipe', 'pipe']
: ['pipe', 'pipe', 'pipe'];
const proc = childProcess.spawn(executablePath, processArguments, {
// On non-windows platforms, `detached: true` makes child process a
// leader of a new process group, making it possible to kill child
// process tree with `.kill(-pid)` command. @see
// https://nodejs.org/api/child_process.html#child_process_options_detached
detached: process.platform !== 'win32',
env: process.env,
stdio,
});
const browserWSEndpoint = await waitForWSEndpoint({
browserProcess: proc,
timeout,
indent,
logLevel,
});
const transport = await NodeWebSocketTransport_1.NodeWebSocketTransport.create(browserWSEndpoint);
const connection = new Connection_1.Connection(transport);
const killProcess = () => {
// If the process failed to launch (for example if the browser executable path
// is invalid), then the process does not get a pid assigned. A call to
// `proc.kill` would error, as the `pid` to-be-killed can not be found.
if (proc.pid && pidExists(proc.pid)) {
try {
if (process.platform === 'win32') {
childProcess.exec(`taskkill /pid ${proc.pid} /T /F`, (error) => {
if (error) {
// taskkill can fail to kill the process e.g. due to missing permissions.
// Let's kill the process via Node API. This delays killing of all child
// processes of `this.proc` until the main Node.js process dies.
proc.kill();
}
});
}
else {
// on linux the process group can be killed with the group id prefixed with
// a minus sign. The process group id is the group leader's pid.
const processGroupId = -proc.pid;
logger_1.Log.verbose({ indent, logLevel }, `Trying to kill browser process group ${processGroupId}`);
try {
process.kill(processGroupId, 'SIGKILL');
}
catch (error) {
// Killing the process group can fail due e.g. to missing permissions.
// Let's kill the process via Node API. This delays killing of all child
// processes of `this.proc` until the main Node.js process dies.
logger_1.Log.verbose({ indent, logLevel }, `Could not kill browser process group ${processGroupId}. Killing process via Node.js API`);
proc.kill('SIGKILL');
}
}
}
catch (error) {
throw new Error(`${PROCESS_ERROR_EXPLANATION}\nError cause: ${(0, util_1.isErrorLike)(error) ? error.stack : error}`);
}
}
(0, delete_directory_1.deleteDirectory)(userDataDir);
// Cleanup this listener last, as that makes sure the full callback runs. If we
// perform this earlier, then the previous function calls would not happen.
(0, util_1.removeEventListeners)(listeners);
};
const closeProcess = () => {
if (closed) {
return Promise.resolve();
}
logger_1.Log.verbose({ indent, logLevel }, 'Received SIGTERM signal. Killing browser process');
killProcess();
(0, delete_directory_1.deleteDirectory)(userDataDir);
// Cleanup this listener last, as that makes sure the full callback runs. If we
// perform this earlier, then the previous function calls would not happen.
(0, util_1.removeEventListeners)(listeners);
return processClosing;
};
if (dumpio) {
(_a = proc.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (d) => {
const message = d.toString('utf8').trim();
if ((0, should_log_message_1.shouldLogBrowserMessage)(message)) {
const formatted = (0, should_log_message_1.formatChromeMessage)(message);
if (!formatted) {
return;
}
const { output, tag } = formatted;
logger_1.Log.verbose({ indent, logLevel, tag }, output);
}
});
(_b = proc.stderr) === null || _b === void 0 ? void 0 : _b.on('data', (d) => {
const message = d.toString('utf8').trim();
if ((0, should_log_message_1.shouldLogBrowserMessage)(message)) {
const formatted = (0, should_log_message_1.formatChromeMessage)(message);
if (!formatted) {
return;
}
const { output, tag } = formatted;
logger_1.Log.error({ indent, logLevel, tag }, output);
}
});
}
let closed = false;
const processClosing = new Promise((fulfill, reject) => {
proc.once('exit', () => {
closed = true;
// Cleanup as processes exit.
try {
fulfill();
}
catch (error) {
reject(error);
}
});
});
const listeners = [(0, util_1.addEventListener)(process, 'exit', killProcess)];
listeners.push((0, util_1.addEventListener)(process, 'SIGINT', () => {
killProcess();
process.exit(130);
}));
listeners.push((0, util_1.addEventListener)(process, 'SIGTERM', closeProcess));
listeners.push((0, util_1.addEventListener)(process, 'SIGHUP', closeProcess));
const deleteBrowserCaches = () => {
// We leave some data:
// Default/Cookies
// Default/Local Storage
// Default/Session Storage
// DevToolsActivePort
// Because not sure if it is bad to delete them while Chrome is running.
const cachePaths = [
(0, node_path_1.join)(userDataDir, 'Default', 'Cache', 'Cache_Data'),
(0, node_path_1.join)(userDataDir, 'Default', 'Code Cache'),
(0, node_path_1.join)(userDataDir, 'Default', 'DawnCache'),
(0, node_path_1.join)(userDataDir, 'Default', 'GPUCache'),
];
for (const p of cachePaths) {
(0, delete_directory_1.deleteDirectory)(p);
}
};
const rememberEventLoop = () => {
var _a, _b;
proc.ref();
// @ts-expect-error
(_a = proc.stdout) === null || _a === void 0 ? void 0 : _a.ref();
// @ts-expect-error
(_b = proc.stderr) === null || _b === void 0 ? void 0 : _b.ref();
(0, assert_1.assert)(connection, 'BrowserRunner not connected.');
connection.transport.rememberEventLoop();
};
const forgetEventLoop = () => {
var _a, _b;
proc.unref();
// @ts-expect-error
(_a = proc.stdout) === null || _a === void 0 ? void 0 : _a.unref();
// @ts-expect-error
(_b = proc.stderr) === null || _b === void 0 ? void 0 : _b.unref();
(0, assert_1.assert)(connection, 'BrowserRunner not connected.');
connection.transport.forgetEventLoop();
};
return {
listeners,
deleteBrowserCaches,
forgetEventLoop,
rememberEventLoop,
connection,
closeProcess,
};
};
exports.makeBrowserRunner = makeBrowserRunner;
function waitForWSEndpoint({ browserProcess, timeout, logLevel, indent, }) {
const browserStderr = browserProcess.stderr;
const browserStdout = browserProcess.stdout;
(0, assert_1.assert)(browserStderr, '`browserProcess` does not have stderr.');
(0, assert_1.assert)(browserStdout, '`browserProcess` does not have stdout.');
let stdioString = '';
return new Promise((resolve, reject) => {
browserStderr.addListener('data', onStdIoData);
browserStdout.addListener('data', onStdIoData);
browserStderr.addListener('close', onClose);
const listeners = [
() => browserStderr.removeListener('data', onStdIoData),
() => browserStdout.removeListener('data', onStdIoData),
() => browserStderr.removeListener('close', onClose),
(0, util_1.addEventListener)(browserProcess, 'exit', (code, signal) => {
logger_1.Log.verbose({ indent, logLevel }, 'Browser process exited with code', code, 'signal', signal);
return onClose(new Error(`Closed with ${code} signal: ${signal}`));
}),
(0, util_1.addEventListener)(browserProcess, 'error', (error) => {
return onClose(error);
}),
];
const timeoutId = timeout ? setTimeout(onTimeout, timeout) : 0;
function onClose(error) {
cleanup();
reject(new Error([
'Failed to launch the browser process!',
error ? error.stack : null,
stdioString,
'Troubleshooting: https://remotion.dev/docs/troubleshooting/browser-launch',
]
.filter(truthy_1.truthy)
.join('\n')));
}
function onTimeout() {
cleanup();
reject(new Errors_1.TimeoutError(`Timed out after ${timeout} ms while trying to connect to the browser! Chrome logged the following: ${stdioString}`));
}
function onStdIoData(data) {
stdioString += data.toString('utf8');
const match = stdioString.match(/DevTools listening on (ws:\/\/.*)/);
if (!match) {
return;
}
cleanup();
resolve(match[1]);
}
function cleanup() {
if (timeoutId) {
clearTimeout(timeoutId);
}
(0, util_1.removeEventListeners)(listeners);
}
});
}
function pidExists(pid) {
try {
return process.kill(pid, 0);
}
catch (error) {
if ((0, util_1.isErrnoException)(error)) {
if (error.code && error.code === 'ESRCH') {
return false;
}
}
throw error;
}
}
@@ -0,0 +1,49 @@
import type { Commands } from './devtools-commands';
import type { TargetInfo } from './devtools-types';
import { EventEmitter } from './EventEmitter';
import type { NodeWebSocketTransport } from './NodeWebSocketTransport';
export declare class Connection extends EventEmitter {
#private;
transport: NodeWebSocketTransport;
constructor(transport: NodeWebSocketTransport);
static fromSession(session: CDPSession): Connection | undefined;
session(sessionId: string): CDPSession | null;
send<T extends keyof Commands>(method: T, ...paramArgs: Commands[T]['paramsType']): Promise<{
value: Commands[T]['returnType'];
size: number;
}>;
_rawSend(message: Record<string, unknown>): number;
dispose(): void;
/**
* @param targetInfo - The target info
* @returns The CDP session that is created
*/
createSession(targetInfo: TargetInfo): Promise<CDPSession>;
}
interface CDPSessionOnMessageObject {
id?: number;
method: string;
params: Record<string, unknown>;
error: {
message: string;
data: any;
code: number;
};
result?: any;
}
export declare const CDPSessionEmittedEvents: {
readonly Disconnected: symbol;
};
export declare class CDPSession extends EventEmitter {
#private;
constructor(connection: Connection, targetType: string, sessionId: string);
connection(): Connection | undefined;
send<T extends keyof Commands>(method: T, ...paramArgs: Commands[T]['paramsType']): Promise<{
value: Commands[T]['returnType'];
size: number;
}>;
_onMessage(object: CDPSessionOnMessageObject, size: number): void;
_onClosed(): void;
id(): string;
}
export {};
@@ -0,0 +1,245 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CDPSession = exports.CDPSessionEmittedEvents = exports.Connection = void 0;
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const logger_1 = require("../logger");
const Errors_1 = require("./Errors");
const EventEmitter_1 = require("./EventEmitter");
const ConnectionEmittedEvents = {
Disconnected: Symbol('Connection.Disconnected'),
};
class Connection extends EventEmitter_1.EventEmitter {
transport;
#lastId = 0;
#sessions = new Map();
#closed = false;
#callbacks = new Map();
constructor(transport) {
super();
this.transport = transport;
this.transport.onmessage = this.#onMessage.bind(this);
this.transport.onclose = this.#onClose.bind(this);
}
static fromSession(session) {
return session.connection();
}
session(sessionId) {
return this.#sessions.get(sessionId) || null;
}
send(method, ...paramArgs) {
// There is only ever 1 param arg passed, but the Protocol defines it as an
// array of 0 or 1 items See this comment:
// https://github.com/ChromeDevTools/devtools-protocol/pull/113#issuecomment-412603285
// which explains why the protocol defines the params this way for better
// type-inference.
// So now we check if there are any params or not and deal with them accordingly.
const params = paramArgs.length ? paramArgs[0] : undefined;
const id = this._rawSend({ method, params });
return new Promise((resolve, reject) => {
var _a;
this.#callbacks.set(id, {
resolve,
reject,
method,
returnSize: true,
stack: (_a = new Error().stack) !== null && _a !== void 0 ? _a : '',
fn: method + JSON.stringify(params),
});
});
}
_rawSend(message) {
const id = ++this.#lastId;
const stringifiedMessage = JSON.stringify({ ...message, id });
this.transport.send(stringifiedMessage);
return id;
}
#onMessage(message) {
const object = JSON.parse(message);
if (object.method === 'Target.attachedToTarget') {
const { sessionId } = object.params;
const session = new CDPSession(this, object.params.targetInfo.type, sessionId);
this.#sessions.set(sessionId, session);
this.emit('sessionattached', session);
const parentSession = this.#sessions.get(object.sessionId);
if (parentSession) {
parentSession.emit('sessionattached', session);
}
}
else if (object.method === 'Target.detachedFromTarget') {
const session = this.#sessions.get(object.params.sessionId);
if (session) {
session._onClosed();
this.#sessions.delete(object.params.sessionId);
this.emit('sessiondetached', session);
const parentSession = this.#sessions.get(object.sessionId);
if (parentSession) {
parentSession.emit('sessiondetached', session);
}
}
}
if (object.sessionId) {
const session = this.#sessions.get(object.sessionId);
if (session) {
session._onMessage(object, message.length);
}
}
else if (object.id) {
const callback = this.#callbacks.get(object.id);
// Callbacks could be all rejected if someone has called `.dispose()`.
if (callback) {
this.#callbacks.delete(object.id);
if (object.error) {
callback.reject(createProtocolError(callback.method, object));
}
else if (callback.returnSize) {
callback.resolve({ value: object.result, size: message.length });
}
else {
callback.resolve(object.result);
}
}
}
else {
this.emit(object.method, object.params);
}
}
#onClose() {
if (this.#closed) {
return;
}
this.transport.onmessage = undefined;
this.transport.onclose = undefined;
for (const callback of this.#callbacks.values()) {
callback.reject(rewriteError(new Errors_1.ProtocolError(), `Protocol error (${callback.method}): Target closed. https://www.remotion.dev/docs/target-closed`));
}
this.#callbacks.clear();
for (const session of this.#sessions.values()) {
session._onClosed();
}
this.#sessions.clear();
this.emit(ConnectionEmittedEvents.Disconnected);
}
dispose() {
this.#onClose();
this.transport.close();
}
/**
* @param targetInfo - The target info
* @returns The CDP session that is created
*/
async createSession(targetInfo) {
const { value: { sessionId }, } = await this.send('Target.attachToTarget', {
targetId: targetInfo.targetId,
flatten: true,
});
const session = this.#sessions.get(sessionId);
if (!session) {
throw new Error('CDPSession creation failed.');
}
return session;
}
}
exports.Connection = Connection;
exports.CDPSessionEmittedEvents = {
Disconnected: Symbol('CDPSession.Disconnected'),
};
class CDPSession extends EventEmitter_1.EventEmitter {
#sessionId;
#targetType;
#callbacks = new Map();
#connection;
constructor(connection, targetType, sessionId) {
super();
this.#connection = connection;
this.#targetType = targetType;
this.#sessionId = sessionId;
}
connection() {
return this.#connection;
}
send(method, ...paramArgs) {
if (!this.#connection) {
return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the ${this.#targetType} has been closed.`));
}
// See the comment in Connection#send explaining why we do this.
const params = paramArgs.length ? paramArgs[0] : undefined;
const id = this.#connection._rawSend({
sessionId: this.#sessionId,
method,
params,
});
return new Promise((resolve, reject) => {
var _a;
if (this.#callbacks.size > 100) {
for (const callback of this.#callbacks.values()) {
logger_1.Log.info({ indent: false, logLevel: 'info' }, callback.fn);
}
throw new Error('Leak detected: Too many callbacks');
}
this.#callbacks.set(id, {
resolve,
reject,
method,
returnSize: true,
stack: (_a = new Error().stack) !== null && _a !== void 0 ? _a : '',
fn: method + JSON.stringify(params),
});
});
}
_onMessage(object, size) {
const callback = object.id ? this.#callbacks.get(object.id) : undefined;
if (object.id && callback) {
this.#callbacks.delete(object.id);
if (object.error) {
callback.reject(createProtocolError(callback.method, object));
}
else if (callback.returnSize) {
callback.resolve({ value: object.result, size });
}
else {
callback.resolve(object.result);
}
}
else {
this.emit(object.method, object.params);
}
}
_onClosed() {
this.#connection = undefined;
for (const callback of this.#callbacks.values()) {
callback.reject(rewriteError(new Errors_1.ProtocolError(), `Protocol error (${callback.method}): Target closed. https://www.remotion.dev/docs/target-closed`));
}
this.#callbacks.clear();
this.emit(exports.CDPSessionEmittedEvents.Disconnected);
}
id() {
return this.#sessionId;
}
}
exports.CDPSession = CDPSession;
function createProtocolError(method, object) {
let message = `Protocol error (${method}): ${object.error.message}`;
if ('data' in object.error) {
message += ` ${object.error.data}`;
}
return rewriteError(new Errors_1.ProtocolError(), message, object.error.message);
}
function rewriteError(error, message, originalMessage) {
error.message = message;
error.originalMessage = originalMessage !== null && originalMessage !== void 0 ? originalMessage : error.originalMessage;
return error;
}
@@ -0,0 +1,42 @@
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { LogLevel } from 'remotion';
import type { JSHandle } from './JSHandle';
export interface ConsoleMessageLocation {
url?: string;
lineNumber?: number;
columnNumber?: number;
}
export type ConsoleMessageType = 'log' | 'debug' | 'info' | 'error' | 'warning' | 'dir' | 'dirxml' | 'table' | 'trace' | 'clear' | 'startGroup' | 'startGroupCollapsed' | 'endGroup' | 'assert' | 'profile' | 'profileEnd' | 'count' | 'timeEnd' | 'verbose';
export declare class ConsoleMessage {
#private;
type: ConsoleMessageType;
text: string;
args: JSHandle[];
previewString: string;
logLevel: LogLevel;
tag: string | null;
constructor({ type, text, args, stackTraceLocations, previewString, logLevel, tag }: {
type: ConsoleMessageType;
text: string;
args: JSHandle[];
stackTraceLocations: ConsoleMessageLocation[];
previewString: string;
logLevel: LogLevel;
tag: string | null;
});
stackTrace(): ConsoleMessageLocation[];
}
@@ -0,0 +1,40 @@
"use strict";
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConsoleMessage = void 0;
class ConsoleMessage {
type;
text;
args;
previewString;
#stackTraceLocations;
logLevel;
tag;
constructor({ type, text, args, stackTraceLocations, previewString, logLevel, tag, }) {
this.type = type;
this.text = text;
this.args = args;
this.previewString = previewString;
this.#stackTraceLocations = stackTraceLocations;
this.logLevel = logLevel;
this.tag = tag;
}
stackTrace() {
return this.#stackTraceLocations;
}
}
exports.ConsoleMessage = ConsoleMessage;
@@ -0,0 +1,56 @@
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { HeadlessBrowser } from './Browser';
import type { EvaluateFn, EvaluateFnReturnType, EvaluateHandleFn, SerializableOrJSHandle, UnwrapPromiseLike } from './EvalTypes';
import type { ExecutionContext } from './ExecutionContext';
import type { Frame } from './FrameManager';
import type { JSHandle } from './JSHandle';
export declare class DOMWorld {
#private;
get _waitTasks(): Set<WaitTask>;
constructor(frame: Frame);
frame(): Frame;
_setContext(context: ExecutionContext | null): void;
_hasContext(): boolean;
_detach(): void;
executionContext(): Promise<ExecutionContext>;
evaluateHandle<HandlerType extends JSHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandlerType>;
evaluate<T extends EvaluateFn>(pageFunction: T, ...args: SerializableOrJSHandle[]): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>>;
waitForFunction({ browser, timeout, pageFunction, title }: {
browser: HeadlessBrowser;
timeout: number | null;
pageFunction: Function | string;
title: string;
}): WaitTask;
}
interface WaitTaskOptions {
domWorld: DOMWorld;
predicateBody: Function | string;
title: string;
timeout: number | null;
browser: HeadlessBrowser;
args: SerializableOrJSHandle[];
}
declare class WaitTask {
#private;
promise: Promise<JSHandle>;
constructor(options: WaitTaskOptions);
onBrowserClose: () => void;
onBrowserCloseSilent: () => void;
terminate(error: Error | null): void;
rerun(): Promise<void>;
}
export {};
@@ -0,0 +1,258 @@
"use strict";
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DOMWorld = void 0;
const assert_1 = require("./assert");
const Errors_1 = require("./Errors");
const util_1 = require("./util");
class DOMWorld {
#frame;
#contextPromise = null;
#contextResolveCallback = null;
#detached = false;
#waitTasks = new Set();
get _waitTasks() {
return this.#waitTasks;
}
constructor(frame) {
// Keep own reference to client because it might differ from the FrameManager's
// client for OOP iframes.
this.#frame = frame;
this._setContext(null);
}
frame() {
return this.#frame;
}
_setContext(context) {
var _a;
if (context) {
(0, assert_1.assert)(this.#contextResolveCallback, 'Execution Context has already been set.');
(_a = this.#contextResolveCallback) === null || _a === void 0 ? void 0 : _a.call(null, context);
this.#contextResolveCallback = null;
for (const waitTask of this._waitTasks) {
waitTask.rerun();
}
}
else {
this.#contextPromise = new Promise((fulfill) => {
this.#contextResolveCallback = fulfill;
});
}
}
_hasContext() {
return !this.#contextResolveCallback;
}
_detach() {
this.#detached = true;
for (const waitTask of this._waitTasks) {
waitTask.terminate(new Error('waitForFunction failed: frame got detached.'));
}
}
executionContext() {
if (this.#detached) {
throw new Error(`Execution context is not available in detached frame "${this.#frame.url()}" (are you trying to evaluate?)`);
}
if (this.#contextPromise === null) {
throw new Error(`Execution content promise is missing`);
}
return this.#contextPromise;
}
async evaluateHandle(pageFunction, ...args) {
const context = await this.executionContext();
return context.evaluateHandle(pageFunction, ...args);
}
async evaluate(pageFunction, ...args) {
const context = await this.executionContext();
return context.evaluate(pageFunction, ...args);
}
waitForFunction({ browser, timeout, pageFunction, title, }) {
return new WaitTask({
domWorld: this,
predicateBody: pageFunction,
title,
timeout,
args: [],
browser,
});
}
}
exports.DOMWorld = DOMWorld;
const noop = () => undefined;
class WaitTask {
#domWorld;
#timeout;
#predicateBody;
#args;
#runCount = 0;
#resolve = noop;
#reject = noop;
#timeoutTimer;
#terminated = false;
#browser;
promise;
constructor(options) {
function getPredicateBody(predicateBody) {
if ((0, util_1.isString)(predicateBody)) {
return `return (${predicateBody});`;
}
return `return (${predicateBody})(...args);`;
}
this.#domWorld = options.domWorld;
this.#timeout = options.timeout;
this.#predicateBody = getPredicateBody(options.predicateBody);
this.#args = options.args;
this.#runCount = 0;
this.#domWorld._waitTasks.add(this);
this.promise = new Promise((resolve, reject) => {
this.#resolve = resolve;
this.#reject = reject;
});
// Since page navigation requires us to re-install the pageScript, we should track
// timeout on our end.
if (options.timeout) {
const timeoutError = new Errors_1.TimeoutError(`waiting for ${options.title} failed: timeout ${options.timeout}ms exceeded`);
this.#timeoutTimer = setTimeout(() => {
return this.#reject(timeoutError);
}, options.timeout);
}
this.#browser = options.browser;
this.#browser.on("closed" /* BrowserEmittedEvents.Closed */, this.onBrowserClose);
this.#browser.on("closed-silent" /* BrowserEmittedEvents.ClosedSilent */, this.onBrowserCloseSilent);
this.rerun();
}
onBrowserClose = () => {
return this.terminate(new Error('Browser was closed'));
};
onBrowserCloseSilent = () => {
return this.terminate(null);
};
terminate(error) {
this.#terminated = true;
if (error) {
this.#reject(error);
}
this.#cleanup();
}
async rerun() {
const runCount = ++this.#runCount;
let success = null;
let error = null;
const context = await this.#domWorld.executionContext();
if (this.#terminated || runCount !== this.#runCount) {
return;
}
if (this.#terminated || runCount !== this.#runCount) {
return;
}
try {
success = await context.evaluateHandle(waitForPredicatePageFunction, this.#predicateBody, this.#timeout, ...this.#args);
}
catch (error_) {
error = error_;
}
if (this.#terminated || runCount !== this.#runCount) {
if (success) {
await success.dispose();
}
return;
}
// Ignore timeouts in pageScript - we track timeouts ourselves.
// If the frame's execution context has already changed, `frame.evaluate` will
// throw an error - ignore this predicate run altogether.
if (!error &&
(await this.#domWorld
.evaluate((s) => {
return !s;
}, success)
.catch(() => {
return true;
}))) {
if (!success) {
throw new Error('Assertion: result handle is not available');
}
await success.dispose();
return;
}
if (error) {
if (error.message.includes('TypeError: binding is not a function')) {
return this.rerun();
}
// When frame is detached the task should have been terminated by the DOMWorld.
// 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')) {
this.terminate(new Error('waitForFunction failed: frame got detached.'));
return;
}
// 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;
}
this.#reject(error);
}
else {
if (!success) {
throw new Error('Assertion: result handle is not available');
}
this.#resolve(success);
}
this.#cleanup();
}
#cleanup() {
if (this.#timeoutTimer !== undefined) {
clearTimeout(this.#timeoutTimer);
}
this.#browser.off("closed" /* BrowserEmittedEvents.Closed */, this.onBrowserClose);
this.#browser.off("closed-silent" /* BrowserEmittedEvents.ClosedSilent */, this.onBrowserCloseSilent);
if (this.#domWorld._waitTasks.size > 100) {
throw new Error('Leak detected: Too many WaitTasks');
}
this.#domWorld._waitTasks.delete(this);
}
}
function waitForPredicatePageFunction(predicateBody, timeout, ...args) {
// eslint-disable-next-line no-new-func
const predicate = new Function('...args', predicateBody);
let timedOut = false;
if (timeout) {
setTimeout(() => {
timedOut = true;
}, timeout);
}
return new Promise((resolve) => {
async function onRaf() {
if (timedOut) {
resolve(undefined);
return;
}
const success = await predicate(...args);
if (success) {
resolve(success);
}
else {
requestAnimationFrame(onRaf);
}
}
onRaf();
});
}
@@ -0,0 +1,25 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare class CustomError extends Error {
constructor(message?: string);
}
export declare class TimeoutError extends CustomError {
}
export declare class ProtocolError extends CustomError {
code?: number;
originalMessage: string;
}
export {};
@@ -0,0 +1,33 @@
"use strict";
/**
* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProtocolError = exports.TimeoutError = void 0;
class CustomError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
class TimeoutError extends CustomError {
}
exports.TimeoutError = TimeoutError;
class ProtocolError extends CustomError {
code;
originalMessage = '';
}
exports.ProtocolError = ProtocolError;
@@ -0,0 +1,27 @@
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { JSHandle } from './JSHandle';
export type EvaluateFn<T = any, U = any, V = any> = string | ((arg1: T, ...args: U[]) => V);
export type UnwrapPromiseLike<T> = T extends PromiseLike<infer U> ? U : T;
export type EvaluateFnReturnType<T extends EvaluateFn> = T extends (...args: any[]) => infer R ? R : any;
export type EvaluateHandleFn = string | ((...args: any[]) => any);
type Serializable = number | string | boolean | null | bigint | JSONArray | JSONObject;
type JSONArray = readonly Serializable[];
interface JSONObject {
[key: string]: Serializable;
}
export type SerializableOrJSHandle = Serializable | JSHandle;
export {};
@@ -0,0 +1,17 @@
"use strict";
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,23 @@
import type { EventType, Handler } from './mitt';
export interface CommonEventEmitter {
on(event: EventType, handler: Handler): CommonEventEmitter;
off(event: EventType, handler: Handler): CommonEventEmitter;
addListener(event: EventType, handler: Handler): CommonEventEmitter;
emit(event: EventType, eventData?: unknown): boolean;
once(event: EventType, handler: Handler): CommonEventEmitter;
listenerCount(event: string): number;
removeAllListeners(event?: EventType): CommonEventEmitter;
}
export declare class EventEmitter implements CommonEventEmitter {
private emitter;
private eventsMap;
constructor();
on(event: EventType, handler: Handler): EventEmitter;
off(event: EventType, handler: Handler): EventEmitter;
addListener(event: EventType, handler: Handler): EventEmitter;
emit(event: EventType, eventData?: unknown): boolean;
once(event: EventType, handler: Handler): EventEmitter;
listenerCount(event: EventType): number;
removeAllListeners(event?: EventType): EventEmitter;
private eventListenersCount;
}
@@ -0,0 +1,54 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EventEmitter = void 0;
const mitt_1 = __importDefault(require("./mitt"));
class EventEmitter {
emitter;
eventsMap = new Map();
constructor() {
this.emitter = (0, mitt_1.default)(this.eventsMap);
}
on(event, handler) {
this.emitter.on(event, handler);
return this;
}
off(event, handler) {
this.emitter.off(event, handler);
return this;
}
addListener(event, handler) {
this.on(event, handler);
return this;
}
emit(event, eventData) {
this.emitter.emit(event, eventData);
return this.eventListenersCount(event) > 0;
}
once(event, handler) {
const onceHandler = (eventData) => {
handler(eventData);
this.off(event, onceHandler);
};
return this.on(event, onceHandler);
}
listenerCount(event) {
return this.eventListenersCount(event);
}
removeAllListeners(event) {
if (event) {
this.eventsMap.delete(event);
}
else {
this.eventsMap.clear();
}
return this;
}
eventListenersCount(event) {
var _a;
return ((_a = this.eventsMap.get(event)) === null || _a === void 0 ? void 0 : _a.length) || 0;
}
}
exports.EventEmitter = EventEmitter;
@@ -0,0 +1,34 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { CDPSession } from './Connection';
import type { ExecutionContextDescription } from './devtools-types';
import type { DOMWorld } from './DOMWorld';
import type { EvaluateHandleFn, SerializableOrJSHandle } from './EvalTypes';
import type { Frame } from './FrameManager';
import type { ElementHandle } from './JSHandle';
import { JSHandle } from './JSHandle';
export declare const EVALUATION_SCRIPT_URL = "pptr://__puppeteer_evaluation_script__";
export declare class ExecutionContext {
#private;
_client: CDPSession;
_world: DOMWorld;
_contextId: number;
_contextName: string;
constructor(client: CDPSession, contextPayload: ExecutionContextDescription, world: DOMWorld);
frame(): Frame | null;
evaluate<ReturnType>(pageFunction: Function | string, ...args: unknown[]): Promise<ReturnType>;
evaluateHandle<HandleType extends JSHandle | ElementHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandleType>;
}
@@ -0,0 +1,171 @@
"use strict";
/* eslint-disable no-new-func */
/* eslint-disable no-new */
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExecutionContext = exports.EVALUATION_SCRIPT_URL = void 0;
const JSHandle_1 = require("./JSHandle");
const util_1 = require("./util");
exports.EVALUATION_SCRIPT_URL = 'pptr://__puppeteer_evaluation_script__';
const SOURCE_URL_REGEX = /^[\x20\t]*\/\/[@#] sourceURL=\s*(\S*?)\s*$/m;
class ExecutionContext {
_client;
_world;
_contextId;
_contextName;
constructor(client, contextPayload, world) {
this._client = client;
this._world = world;
this._contextId = contextPayload.id;
this._contextName = contextPayload.name;
}
frame() {
return this._world ? this._world.frame() : null;
}
evaluate(pageFunction, ...args) {
return this.#evaluate(true, pageFunction, ...args);
}
evaluateHandle(pageFunction, ...args) {
return this.#evaluate(false, pageFunction, ...args);
}
async #evaluate(returnByValue, pageFunction, ...args) {
const suffix = `//# sourceURL=${exports.EVALUATION_SCRIPT_URL}`;
if ((0, util_1.isString)(pageFunction)) {
const contextId = this._contextId;
const expression = pageFunction;
const expressionWithSourceUrl = SOURCE_URL_REGEX.test(expression)
? expression
: expression + '\n' + suffix;
const { value: { exceptionDetails: _details, result: _remoteObject }, } = await this._client
.send('Runtime.evaluate', {
expression: expressionWithSourceUrl,
contextId,
returnByValue,
awaitPromise: true,
userGesture: true,
})
.catch(rewriteError);
if (_details) {
throw new Error('Evaluation failed: ' + (0, util_1.getExceptionMessage)(_details));
}
return returnByValue
? (0, util_1.valueFromRemoteObject)(_remoteObject)
: (0, JSHandle_1._createJSHandle)(this, _remoteObject);
}
if (typeof pageFunction !== 'function') {
throw new Error(`Expected to get |string| or |function| as the first argument, but got "${pageFunction}" instead.`);
}
let functionText = pageFunction.toString();
try {
new Function('(' + functionText + ')');
}
catch (error) {
// This means we might have a function shorthand. Try another
// time prefixing 'function '.
if (functionText.startsWith('async ')) {
functionText =
'async function ' + functionText.substring('async '.length);
}
else {
functionText = 'function ' + functionText;
}
try {
new Function('(' + functionText + ')');
}
catch (_error) {
// We tried hard to serialize, but there's a weird beast here.
throw new Error('Passed function is not well-serializable!');
}
}
let callFunctionOnPromise;
try {
callFunctionOnPromise = this._client.send('Runtime.callFunctionOn', {
functionDeclaration: functionText + '\n' + suffix + '\n',
executionContextId: this._contextId,
arguments: args.map(convertArgument.bind(this)),
returnByValue,
awaitPromise: true,
userGesture: true,
});
}
catch (error) {
if (error instanceof TypeError &&
error.message.startsWith('Converting circular structure to JSON')) {
error.message += ' Recursive objects are not allowed.';
}
throw error;
}
const { value: { exceptionDetails, result: remoteObject }, } = await callFunctionOnPromise.catch(rewriteError);
if (exceptionDetails) {
throw new Error('Evaluation failed: ' + (0, util_1.getExceptionMessage)(exceptionDetails));
}
return returnByValue
? (0, util_1.valueFromRemoteObject)(remoteObject)
: (0, JSHandle_1._createJSHandle)(this, remoteObject);
function convertArgument(arg) {
if (typeof arg === 'bigint') {
// eslint-disable-line valid-typeof
return { unserializableValue: `${arg.toString()}n` };
}
if (Object.is(arg, -0)) {
return { unserializableValue: '-0' };
}
if (Object.is(arg, Infinity)) {
return { unserializableValue: 'Infinity' };
}
if (Object.is(arg, -Infinity)) {
return { unserializableValue: '-Infinity' };
}
if (Object.is(arg, NaN)) {
return { unserializableValue: 'NaN' };
}
const objectHandle = arg && arg instanceof JSHandle_1.JSHandle ? arg : null;
if (objectHandle) {
if (objectHandle._context !== this) {
throw new Error('JSHandles can be evaluated only in the context they were created!');
}
if (objectHandle._disposed) {
throw new Error('JSHandle is disposed!');
}
if (objectHandle._remoteObject.unserializableValue) {
return {
unserializableValue: objectHandle._remoteObject.unserializableValue,
};
}
if (!objectHandle._remoteObject.objectId) {
return { value: objectHandle._remoteObject.value };
}
return { objectId: objectHandle._remoteObject.objectId };
}
return { value: arg };
}
function rewriteError(error) {
if (error.message.includes('Object reference chain is too long')) {
return { value: { result: { type: 'undefined' } }, size: 1 };
}
if (error.message.includes("Object couldn't be returned by value")) {
return { value: { result: { type: 'undefined' } }, size: 1 };
}
if (error.message.endsWith('Cannot find context with specified id') ||
error.message.endsWith('Inspected target navigated or closed')) {
throw new Error('Execution context was destroyed, most likely because of a navigation.');
}
throw error;
}
}
}
exports.ExecutionContext = ExecutionContext;
@@ -0,0 +1,89 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { LogLevel } from '../log-level';
import type { Page } from './BrowserPage';
import type { CDPSession } from './Connection';
import { DOMWorld } from './DOMWorld';
import type { EvaluateFn, EvaluateFnReturnType, EvaluateHandleFn, SerializableOrJSHandle, UnwrapPromiseLike } from './EvalTypes';
import { EventEmitter } from './EventEmitter';
import { ExecutionContext } from './ExecutionContext';
import type { HTTPResponse } from './HTTPResponse';
import type { JSHandle } from './JSHandle';
import type { PuppeteerLifeCycleEvent } from './LifecycleWatcher';
import { NetworkManager } from './NetworkManager';
import type { Frame as TFrame } from './devtools-types';
export declare const FrameManagerEmittedEvents: {
FrameNavigated: symbol;
FrameDetached: symbol;
FrameSwapped: symbol;
LifecycleEvent: symbol;
FrameNavigatedWithinDocument: symbol;
ExecutionContextCreated: symbol;
ExecutionContextDestroyed: symbol;
};
export declare class FrameManager extends EventEmitter {
#private;
get _client(): CDPSession;
constructor(client: CDPSession, page: Page, indent: boolean, logLevel: LogLevel);
private setupEventListeners;
initialize(client?: CDPSession): Promise<void>;
networkManager(): NetworkManager;
navigateFrame(frame: Frame, url: string, timeout: number, options?: {
referer?: string;
timeout?: number;
waitUntil?: PuppeteerLifeCycleEvent;
}): Promise<HTTPResponse | null>;
page(): Page;
mainFrame(): Frame;
frames(): Frame[];
frame(frameId: string): Frame | null;
_ensureIsolatedWorld(session: CDPSession, name: string): Promise<void>;
executionContextById(contextId: number, session?: CDPSession): ExecutionContext;
}
export declare class Frame {
#private;
_frameManager: FrameManager;
_id: string;
_loaderId: string;
_name?: string;
_hasStartedLoading: boolean;
_lifecycleEvents: Set<string>;
_mainWorld: DOMWorld;
_secondaryWorld: DOMWorld;
_childFrames: Set<Frame>;
constructor(frameManager: FrameManager, parentFrame: Frame | null, frameId: string, client: CDPSession);
_updateClient(client: CDPSession): void;
isOOPFrame(): boolean;
goto(url: string, timeout: number, options?: {
referer?: string;
waitUntil?: PuppeteerLifeCycleEvent;
}): Promise<HTTPResponse | null>;
_client(): CDPSession;
/**
* @returns a promise that resolves to the frame's default execution context.
*/
executionContext(): Promise<ExecutionContext>;
evaluateHandle<HandlerType extends JSHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandlerType>;
evaluate<T extends EvaluateFn>(pageFunction: T, ...args: SerializableOrJSHandle[]): Promise<UnwrapPromiseLike<EvaluateFnReturnType<T>>>;
url(): string;
childFrames(): Frame[];
_navigated(framePayload: TFrame): void;
_navigatedWithinDocument(url: string): void;
_onLifecycleEvent(loaderId: string, name: string): void;
_onLoadingStopped(): void;
_onLoadingStarted(): void;
_detach(): void;
}
@@ -0,0 +1,492 @@
"use strict";
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Frame = exports.FrameManager = exports.FrameManagerEmittedEvents = void 0;
const Connection_1 = require("./Connection");
const DOMWorld_1 = require("./DOMWorld");
const EventEmitter_1 = require("./EventEmitter");
const ExecutionContext_1 = require("./ExecutionContext");
const LifecycleWatcher_1 = require("./LifecycleWatcher");
const NetworkManager_1 = require("./NetworkManager");
const assert_1 = require("./assert");
const flaky_errors_1 = require("./flaky-errors");
const util_1 = require("./util");
const UTILITY_WORLD_NAME = '__puppeteer_utility_world__';
exports.FrameManagerEmittedEvents = {
FrameNavigated: Symbol('FrameManager.FrameNavigated'),
FrameDetached: Symbol('FrameManager.FrameDetached'),
FrameSwapped: Symbol('FrameManager.FrameSwapped'),
LifecycleEvent: Symbol('FrameManager.LifecycleEvent'),
FrameNavigatedWithinDocument: Symbol('FrameManager.FrameNavigatedWithinDocument'),
ExecutionContextCreated: Symbol('FrameManager.ExecutionContextCreated'),
ExecutionContextDestroyed: Symbol('FrameManager.ExecutionContextDestroyed'),
};
class FrameManager extends EventEmitter_1.EventEmitter {
#page;
#networkManager;
#frames = new Map();
#contextIdToContext = new Map();
#isolatedWorlds = new Set();
#mainFrame;
#client;
get _client() {
return this.#client;
}
constructor(client, page, indent, logLevel) {
super();
this.#client = client;
this.#page = page;
this.#networkManager = new NetworkManager_1.NetworkManager(client, this, indent, logLevel);
this.setupEventListeners(this.#client);
}
setupEventListeners(session) {
session.on('Page.frameAttached', (event) => {
this.#onFrameAttached(session, event.frameId, event.parentFrameId);
});
session.on('Page.frameNavigated', (event) => {
this.#onFrameNavigated(event.frame);
});
session.on('Page.navigatedWithinDocument', (event) => {
this.#onFrameNavigatedWithinDocument(event.frameId, event.url);
});
session.on('Page.frameDetached', (event) => {
this.#onFrameDetached(event.frameId, event.reason);
});
session.on('Page.frameStartedLoading', (event) => {
this.#onFrameStartedLoading(event.frameId);
});
session.on('Page.frameStoppedLoading', (event) => {
this.#onFrameStoppedLoading(event.frameId);
});
session.on('Runtime.executionContextCreated', (event) => {
this.#onExecutionContextCreated(event.context, session);
});
session.on('Runtime.executionContextDestroyed', (event) => {
this.#onExecutionContextDestroyed(event.executionContextId, session);
});
session.on('Runtime.executionContextsCleared', () => {
this.#onExecutionContextsCleared(session);
});
session.on('Page.lifecycleEvent', (event) => {
this.#onLifecycleEvent(event);
});
session.on('Target.attachedToTarget', (event) => {
this.#onAttachedToTarget(event);
});
session.on('Target.detachedFromTarget', (event) => {
this.#onDetachedFromTarget(event);
});
}
async initialize(client = this.#client) {
try {
const result = await Promise.all([
client.send('Page.enable'),
client.send('Page.getFrameTree'),
client === this.#client
? Promise.resolve()
: client.send('Target.setAutoAttach', {
autoAttach: true,
waitForDebuggerOnStart: false,
flatten: true,
}),
]);
const { value: { frameTree }, } = result[1];
this.#handleFrameTree(client, frameTree);
await Promise.all([
client.send('Page.setLifecycleEventsEnabled', { enabled: true }),
client.send('Runtime.enable').then(() => {
return this._ensureIsolatedWorld(client, UTILITY_WORLD_NAME);
}),
client === this.#client
? this.#networkManager.initialize()
: Promise.resolve(),
]);
}
catch (error) {
// The target might have been closed before the initialization finished.
if ((0, util_1.isErrorLike)(error) && (0, flaky_errors_1.isTargetClosedErr)(error)) {
return;
}
throw error;
}
}
networkManager() {
return this.#networkManager;
}
async navigateFrame(frame, url, timeout, options = {}) {
const { referer = undefined, waitUntil = 'load' } = options;
const watcher = new LifecycleWatcher_1.LifecycleWatcher(this, frame, waitUntil, timeout);
let error = await Promise.race([
navigate(this.#client, url, referer, frame._id),
watcher.timeoutOrTerminationPromise(),
]);
if (!error) {
error = await Promise.race([
watcher.timeoutOrTerminationPromise(),
watcher.newDocumentNavigationPromise(),
watcher.sameDocumentNavigationPromise(),
]);
}
watcher.dispose();
if (error) {
throw error;
}
return watcher.navigationResponse();
async function navigate(client, _url, referrer, frameId) {
try {
const { value: response } = await client.send('Page.navigate', {
url: _url,
referrer,
frameId,
});
return response.errorText
? new Error(`${response.errorText} at ${_url}`)
: null;
}
catch (_error) {
if ((0, util_1.isErrorLike)(_error)) {
return _error;
}
throw _error;
}
}
}
async #onAttachedToTarget(event) {
if (event.targetInfo.type !== 'iframe') {
return;
}
const frame = this.#frames.get(event.targetInfo.targetId);
const connection = Connection_1.Connection.fromSession(this.#client);
(0, assert_1.assert)(connection);
const session = connection.session(event.sessionId);
(0, assert_1.assert)(session);
if (frame) {
frame._updateClient(session);
}
this.setupEventListeners(session);
await this.initialize(session);
}
#onDetachedFromTarget(event) {
if (!event.targetId) {
return;
}
const frame = this.#frames.get(event.targetId);
if (frame === null || frame === void 0 ? void 0 : frame.isOOPFrame()) {
// When an OOP iframe is removed from the page, it
// will only get a Target.detachedFromTarget event.
this.#removeFramesRecursively(frame);
}
}
#onLifecycleEvent(event) {
const frame = this.#frames.get(event.frameId);
if (!frame) {
return;
}
frame._onLifecycleEvent(event.loaderId, event.name);
this.emit(exports.FrameManagerEmittedEvents.LifecycleEvent, frame);
}
#onFrameStartedLoading(frameId) {
const frame = this.#frames.get(frameId);
if (!frame) {
return;
}
frame._onLoadingStarted();
}
#onFrameStoppedLoading(frameId) {
const frame = this.#frames.get(frameId);
if (!frame) {
return;
}
frame._onLoadingStopped();
this.emit(exports.FrameManagerEmittedEvents.LifecycleEvent, frame);
}
#handleFrameTree(session, frameTree) {
if (frameTree.frame.parentId) {
this.#onFrameAttached(session, frameTree.frame.id, frameTree.frame.parentId);
}
this.#onFrameNavigated(frameTree.frame);
if (!frameTree.childFrames) {
return;
}
for (const child of frameTree.childFrames) {
this.#handleFrameTree(session, child);
}
}
page() {
return this.#page;
}
mainFrame() {
(0, assert_1.assert)(this.#mainFrame, 'Requesting main frame too early!');
return this.#mainFrame;
}
frames() {
return Array.from(this.#frames.values());
}
frame(frameId) {
return this.#frames.get(frameId) || null;
}
#onFrameAttached(session, frameId, parentFrameId) {
if (this.#frames.has(frameId)) {
const _frame = this.#frames.get(frameId);
if (session && _frame.isOOPFrame()) {
// If an OOP iframes becomes a normal iframe again
// it is first attached to the parent page before
// the target is removed.
_frame._updateClient(session);
}
return;
}
(0, assert_1.assert)(parentFrameId);
const parentFrame = this.#frames.get(parentFrameId);
(0, assert_1.assert)(parentFrame);
const frame = new Frame(this, parentFrame, frameId, session);
this.#frames.set(frame._id, frame);
}
#onFrameNavigated(framePayload) {
const isMainFrame = !framePayload.parentId;
let frame = isMainFrame
? this.#mainFrame
: this.#frames.get(framePayload.id);
(0, assert_1.assert)(isMainFrame || frame, 'We either navigate top level or have old version of the navigated frame');
// Detach all child frames first.
if (frame) {
for (const child of frame.childFrames()) {
this.#removeFramesRecursively(child);
}
}
// Update or create main frame.
if (isMainFrame) {
if (frame) {
// Update frame id to retain frame identity on cross-process navigation.
this.#frames.delete(frame._id);
frame._id = framePayload.id;
}
else {
// Initial main frame navigation.
frame = new Frame(this, null, framePayload.id, this.#client);
}
this.#frames.set(framePayload.id, frame);
this.#mainFrame = frame;
}
// Update frame payload.
(0, assert_1.assert)(frame);
frame._navigated(framePayload);
this.emit(exports.FrameManagerEmittedEvents.FrameNavigated, frame);
}
async _ensureIsolatedWorld(session, name) {
const key = `${session.id()}:${name}`;
if (this.#isolatedWorlds.has(key)) {
return;
}
this.#isolatedWorlds.add(key);
await session.send('Page.addScriptToEvaluateOnNewDocument', {
source: `//# sourceURL=${ExecutionContext_1.EVALUATION_SCRIPT_URL}`,
worldName: name,
});
// Frames might be removed before we send this.
await Promise.all(this.frames()
.filter((frame) => {
return frame._client() === session;
})
.map((frame) => {
return session
.send('Page.createIsolatedWorld', {
frameId: frame._id,
worldName: name,
grantUniveralAccess: true,
})
.catch(() => undefined);
}));
}
#onFrameNavigatedWithinDocument(frameId, url) {
const frame = this.#frames.get(frameId);
if (!frame) {
return;
}
frame._navigatedWithinDocument(url);
this.emit(exports.FrameManagerEmittedEvents.FrameNavigatedWithinDocument, frame);
this.emit(exports.FrameManagerEmittedEvents.FrameNavigated, frame);
}
#onFrameDetached(frameId, reason) {
const frame = this.#frames.get(frameId);
if (reason === 'remove') {
// Only remove the frame if the reason for the detached event is
// an actual removement of the frame.
// For frames that become OOP iframes, the reason would be 'swap'.
if (frame) {
this.#removeFramesRecursively(frame);
}
}
else if (reason === 'swap') {
this.emit(exports.FrameManagerEmittedEvents.FrameSwapped, frame);
}
}
#onExecutionContextCreated(contextPayload, session) {
const auxData = contextPayload.auxData;
const frameId = auxData === null || auxData === void 0 ? void 0 : auxData.frameId;
const frame = typeof frameId === 'string' ? this.#frames.get(frameId) : undefined;
let world;
if (frame) {
// Only care about execution contexts created for the current session.
if (frame._client() !== session) {
return;
}
if (contextPayload.auxData && Boolean(contextPayload.auxData.isDefault)) {
world = frame._mainWorld;
}
else if (contextPayload.name === UTILITY_WORLD_NAME &&
!frame._secondaryWorld._hasContext()) {
// In case of multiple sessions to the same target, there's a race between
// connections so we might end up creating multiple isolated worlds.
// We can use either.
world = frame._secondaryWorld;
}
}
const context = new ExecutionContext_1.ExecutionContext((frame === null || frame === void 0 ? void 0 : frame._client()) || this.#client, contextPayload, world);
if (world) {
world._setContext(context);
}
const key = `${session.id()}:${contextPayload.id}`;
this.#contextIdToContext.set(key, context);
}
#onExecutionContextDestroyed(executionContextId, session) {
const key = `${session.id()}:${executionContextId}`;
const context = this.#contextIdToContext.get(key);
if (!context) {
return;
}
this.#contextIdToContext.delete(key);
if (context._world) {
context._world._setContext(null);
}
}
#onExecutionContextsCleared(session) {
for (const [key, context] of this.#contextIdToContext.entries()) {
// Make sure to only clear execution contexts that belong
// to the current session.
if (context._client !== session) {
continue;
}
if (context._world) {
context._world._setContext(null);
}
this.#contextIdToContext.delete(key);
}
}
executionContextById(contextId, session = this.#client) {
const key = `${session.id()}:${contextId}`;
const context = this.#contextIdToContext.get(key);
(0, assert_1.assert)(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
return context;
}
#removeFramesRecursively(frame) {
for (const child of frame.childFrames()) {
this.#removeFramesRecursively(child);
}
frame._detach();
this.#frames.delete(frame._id);
this.emit(exports.FrameManagerEmittedEvents.FrameDetached, frame);
}
}
exports.FrameManager = FrameManager;
class Frame {
#parentFrame;
#url = '';
#client;
_frameManager;
_id;
_loaderId = '';
_name;
_hasStartedLoading = false;
_lifecycleEvents = new Set();
_mainWorld;
_secondaryWorld;
_childFrames;
constructor(frameManager, parentFrame, frameId, client) {
this._frameManager = frameManager;
this.#parentFrame = parentFrame !== null && parentFrame !== void 0 ? parentFrame : null;
this.#url = '';
this._id = frameId;
this._loaderId = '';
this._childFrames = new Set();
if (this.#parentFrame) {
this.#parentFrame._childFrames.add(this);
}
this._updateClient(client);
}
_updateClient(client) {
this.#client = client;
this._mainWorld = new DOMWorld_1.DOMWorld(this);
this._secondaryWorld = new DOMWorld_1.DOMWorld(this);
}
isOOPFrame() {
return this.#client !== this._frameManager._client;
}
goto(url, timeout, options = {}) {
return this._frameManager.navigateFrame(this, url, timeout, options);
}
_client() {
return this.#client;
}
/**
* @returns a promise that resolves to the frame's default execution context.
*/
executionContext() {
return this._mainWorld.executionContext();
}
evaluateHandle(pageFunction, ...args) {
return this._mainWorld.evaluateHandle(pageFunction, ...args);
}
evaluate(pageFunction, ...args) {
return this._mainWorld.evaluate(pageFunction, ...args);
}
url() {
return this.#url;
}
childFrames() {
return Array.from(this._childFrames);
}
_navigated(framePayload) {
this._name = framePayload.name;
this.#url = `${framePayload.url}${framePayload.urlFragment || ''}`;
}
_navigatedWithinDocument(url) {
this.#url = url;
}
_onLifecycleEvent(loaderId, name) {
if (name === 'init') {
this._loaderId = loaderId;
this._lifecycleEvents.clear();
}
this._lifecycleEvents.add(name);
}
_onLoadingStopped() {
this._lifecycleEvents.add('load');
}
_onLoadingStarted() {
this._hasStartedLoading = true;
}
_detach() {
this._mainWorld._detach();
this._secondaryWorld._detach();
if (this.#parentFrame) {
this.#parentFrame._childFrames.delete(this);
}
this.#parentFrame = null;
}
}
exports.Frame = Frame;
@@ -0,0 +1,29 @@
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { RequestWillBeSentEvent } from './devtools-types';
import type { Frame } from './FrameManager';
import type { HTTPResponse } from './HTTPResponse';
export declare class HTTPRequest {
#private;
_requestId: string;
_response: HTTPResponse | null;
_url: string | null;
_fromMemoryCache: boolean;
constructor(frame: Frame | null, event: RequestWillBeSentEvent);
response(): HTTPResponse | null;
frame(): Frame | null;
isNavigationRequest(): boolean;
}
@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HTTPRequest = void 0;
class HTTPRequest {
_requestId;
_response = null;
_url = null;
_fromMemoryCache = false;
#isNavigationRequest;
#frame;
constructor(frame, event) {
this._requestId = event.requestId;
this.#isNavigationRequest =
event.requestId === event.loaderId && event.type === 'Document';
this.#frame = frame;
this._url = event.request.url;
}
response() {
return this._response;
}
frame() {
return this.#frame;
}
isNavigationRequest() {
return this.#isNavigationRequest;
}
}
exports.HTTPRequest = HTTPRequest;
@@ -0,0 +1,21 @@
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Response, ResponseReceivedExtraInfoEvent } from './devtools-types';
export declare class HTTPResponse {
#private;
constructor(responsePayload: Response, extraInfo: ResponseReceivedExtraInfoEvent | null);
status(): number;
}
@@ -0,0 +1,28 @@
"use strict";
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.HTTPResponse = void 0;
class HTTPResponse {
#status;
constructor(responsePayload, extraInfo) {
this.#status = extraInfo ? extraInfo.statusCode : responsePayload.status;
}
status() {
return this.#status;
}
}
exports.HTTPResponse = HTTPResponse;
@@ -0,0 +1,35 @@
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { CDPSession } from './Connection';
import type { DevtoolsRemoteObject } from './devtools-types';
import type { EvaluateHandleFn, SerializableOrJSHandle } from './EvalTypes';
import type { ExecutionContext } from './ExecutionContext';
export declare function _createJSHandle(context: ExecutionContext, remoteObject: DevtoolsRemoteObject): JSHandle;
export declare class JSHandle {
#private;
get _disposed(): boolean;
get _remoteObject(): DevtoolsRemoteObject;
get _context(): ExecutionContext;
constructor(context: ExecutionContext, client: CDPSession, remoteObject: DevtoolsRemoteObject);
executionContext(): ExecutionContext;
evaluateHandle<HandleType extends JSHandle = JSHandle>(pageFunction: EvaluateHandleFn, ...args: SerializableOrJSHandle[]): Promise<HandleType>;
asElement(): ElementHandle | null;
dispose(): Promise<void>;
toString(): string;
}
export declare class ElementHandle<ElementType extends Element = Element> extends JSHandle {
asElement(): ElementHandle<ElementType> | null;
}
@@ -0,0 +1,77 @@
"use strict";
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ElementHandle = exports.JSHandle = void 0;
exports._createJSHandle = _createJSHandle;
const util_1 = require("./util");
function _createJSHandle(context, remoteObject) {
const frame = context.frame();
if (remoteObject.subtype === 'node' && frame) {
return new ElementHandle(context, context._client, remoteObject);
}
return new JSHandle(context, context._client, remoteObject);
}
class JSHandle {
#client;
#disposed = false;
#context;
#remoteObject;
get _disposed() {
return this.#disposed;
}
get _remoteObject() {
return this.#remoteObject;
}
get _context() {
return this.#context;
}
constructor(context, client, remoteObject) {
this.#context = context;
this.#client = client;
this.#remoteObject = remoteObject;
}
executionContext() {
return this.#context;
}
evaluateHandle(pageFunction, ...args) {
return this.executionContext().evaluateHandle(pageFunction, this, ...args);
}
asElement() {
return null;
}
async dispose() {
if (this.#disposed) {
return;
}
this.#disposed = true;
await (0, util_1.releaseObject)(this.#client, this.#remoteObject);
}
toString() {
if (this.#remoteObject.objectId) {
const type = this.#remoteObject.subtype || this.#remoteObject.type;
return 'JSHandle@' + type;
}
return (0, util_1.valueFromRemoteObject)(this.#remoteObject);
}
}
exports.JSHandle = JSHandle;
class ElementHandle extends JSHandle {
asElement() {
return this;
}
}
exports.ElementHandle = ElementHandle;
@@ -0,0 +1,26 @@
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { LogLevel } from '../log-level';
import type { Viewport } from './PuppeteerViewport';
export interface LaunchOptions {
args: string[];
executablePath: string;
logLevel: LogLevel;
indent: boolean;
defaultViewport: Viewport;
userDataDir: string;
timeout: number;
}
@@ -0,0 +1,17 @@
"use strict";
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,18 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { HeadlessBrowser } from './Browser';
import type { LaunchOptions } from './LaunchOptions';
export declare const launchChrome: ({ args, executablePath, defaultViewport, indent, logLevel, userDataDir, timeout, }: LaunchOptions) => Promise<HeadlessBrowser>;
@@ -0,0 +1,41 @@
"use strict";
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.launchChrome = void 0;
const Browser_1 = require("./Browser");
const launchChrome = async ({ args, executablePath, defaultViewport, indent, logLevel, userDataDir, timeout, }) => {
const browser = await Browser_1.HeadlessBrowser.create({
defaultViewport,
args,
executablePath,
timeout,
userDataDir,
logLevel,
indent,
});
try {
await browser.waitForTarget((t) => {
return t.type() === 'page';
}, { timeout });
}
catch (error) {
await browser.close({ silent: false });
throw error;
}
return browser;
};
exports.launchChrome = launchChrome;
@@ -0,0 +1,29 @@
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TimeoutError } from './Errors';
import type { Frame, FrameManager } from './FrameManager';
import type { HTTPResponse } from './HTTPResponse';
export type PuppeteerLifeCycleEvent = 'load';
export declare class LifecycleWatcher {
#private;
constructor(frameManager: FrameManager, frame: Frame, waitUntil: PuppeteerLifeCycleEvent, timeout: number);
navigationResponse(): HTTPResponse | null;
sameDocumentNavigationPromise(): Promise<Error | undefined>;
newDocumentNavigationPromise(): Promise<Error | undefined>;
lifecyclePromise(): Promise<void>;
timeoutOrTerminationPromise(): Promise<Error | TimeoutError | undefined>;
dispose(): void;
}
@@ -0,0 +1,174 @@
"use strict";
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.LifecycleWatcher = void 0;
const assert_1 = require("./assert");
const Connection_1 = require("./Connection");
const Errors_1 = require("./Errors");
const FrameManager_1 = require("./FrameManager");
const NetworkManager_1 = require("./NetworkManager");
const util_1 = require("./util");
const puppeteerToProtocolLifecycle = new Map([['load', 'load']]);
const noop = () => undefined;
class LifecycleWatcher {
#expectedLifecycle;
#frameManager;
#frame;
#timeout;
#navigationRequest = null;
#eventListeners;
#sameDocumentNavigationCompleteCallback = noop;
#sameDocumentNavigationPromise = new Promise((fulfill) => {
this.#sameDocumentNavigationCompleteCallback = fulfill;
});
#lifecycleCallback = noop;
#lifecyclePromise = new Promise((fulfill) => {
this.#lifecycleCallback = fulfill;
});
#newDocumentNavigationCompleteCallback = noop;
#newDocumentNavigationPromise = new Promise((fulfill) => {
this.#newDocumentNavigationCompleteCallback = fulfill;
});
#terminationCallback = noop;
#terminationPromise = new Promise((fulfill) => {
this.#terminationCallback = fulfill;
});
#timeoutPromise;
#maximumTimer;
#hasSameDocumentNavigation;
#newDocumentNavigation;
#swapped;
constructor(frameManager, frame, waitUntil, timeout) {
const protocolEvent = puppeteerToProtocolLifecycle.get(waitUntil);
(0, assert_1.assert)(protocolEvent, 'Unknown value for options.waitUntil: ' + waitUntil);
this.#expectedLifecycle = [waitUntil];
this.#frameManager = frameManager;
this.#frame = frame;
this.#timeout = timeout;
this.#eventListeners = [
(0, util_1.addEventListener)(frameManager._client, Connection_1.CDPSessionEmittedEvents.Disconnected, this.#terminate.bind(this, new Error('Navigation failed because browser has disconnected!'))),
(0, util_1.addEventListener)(this.#frameManager, FrameManager_1.FrameManagerEmittedEvents.LifecycleEvent, this.#checkLifecycleComplete.bind(this)),
(0, util_1.addEventListener)(this.#frameManager, FrameManager_1.FrameManagerEmittedEvents.FrameNavigatedWithinDocument, this.#navigatedWithinDocument.bind(this)),
(0, util_1.addEventListener)(this.#frameManager, FrameManager_1.FrameManagerEmittedEvents.FrameNavigated, this.#navigated.bind(this)),
(0, util_1.addEventListener)(this.#frameManager, FrameManager_1.FrameManagerEmittedEvents.FrameSwapped, this.#frameSwapped.bind(this)),
(0, util_1.addEventListener)(this.#frameManager, FrameManager_1.FrameManagerEmittedEvents.FrameDetached, this.#onFrameDetached.bind(this)),
(0, util_1.addEventListener)(this.#frameManager.networkManager(), NetworkManager_1.NetworkManagerEmittedEvents.Request, this.#onRequest.bind(this)),
];
this.#timeoutPromise = this.#createTimeoutPromise();
this.#checkLifecycleComplete();
}
#onRequest(request) {
if (request.frame() !== this.#frame || !request.isNavigationRequest()) {
return;
}
this.#navigationRequest = request;
}
#onFrameDetached(frame) {
if (this.#frame === frame) {
this.#terminationCallback.call(null, new Error('Navigating frame was detached'));
return;
}
this.#checkLifecycleComplete();
}
navigationResponse() {
if (!this.#navigationRequest) {
return null;
}
const res = this.#navigationRequest.response();
return res;
}
#terminate(error) {
this.#terminationCallback.call(null, error);
}
sameDocumentNavigationPromise() {
return this.#sameDocumentNavigationPromise;
}
newDocumentNavigationPromise() {
return this.#newDocumentNavigationPromise;
}
lifecyclePromise() {
return this.#lifecyclePromise;
}
timeoutOrTerminationPromise() {
return Promise.race([this.#timeoutPromise, this.#terminationPromise]);
}
async #createTimeoutPromise() {
if (!this.#timeout) {
return new Promise(noop);
}
const errorMessage = 'Navigation timeout of ' + this.#timeout + ' ms exceeded';
await new Promise((fulfill) => {
this.#maximumTimer = setTimeout(fulfill, this.#timeout);
});
return new Errors_1.TimeoutError(errorMessage);
}
#navigatedWithinDocument(frame) {
if (frame !== this.#frame) {
return;
}
this.#hasSameDocumentNavigation = true;
this.#checkLifecycleComplete();
}
#navigated(frame) {
if (frame !== this.#frame) {
return;
}
this.#newDocumentNavigation = true;
this.#checkLifecycleComplete();
}
#frameSwapped(frame) {
if (frame !== this.#frame) {
return;
}
this.#swapped = true;
this.#checkLifecycleComplete();
}
#checkLifecycleComplete() {
// We expect navigation to commit.
if (!checkLifecycle(this.#frame, this.#expectedLifecycle)) {
return;
}
this.#lifecycleCallback();
if (this.#hasSameDocumentNavigation) {
this.#sameDocumentNavigationCompleteCallback();
}
if (this.#swapped || this.#newDocumentNavigation) {
this.#newDocumentNavigationCompleteCallback();
}
function checkLifecycle(frame, expectedLifecycle) {
for (const event of expectedLifecycle) {
if (!frame._lifecycleEvents.has(event)) {
return false;
}
}
for (const child of frame.childFrames()) {
if (child._hasStartedLoading &&
!checkLifecycle(child, expectedLifecycle)) {
return false;
}
}
return true;
}
}
dispose() {
(0, util_1.removeEventListeners)(this.#eventListeners);
if (this.#maximumTimer !== undefined) {
clearTimeout(this.#maximumTimer);
}
}
}
exports.LifecycleWatcher = LifecycleWatcher;
@@ -0,0 +1,34 @@
import type { LoadingFailedEvent, LoadingFinishedEvent, RequestPausedEvent, RequestWillBeSentEvent, ResponseReceivedEvent, ResponseReceivedExtraInfoEvent } from './devtools-types';
import type { HTTPRequest } from './HTTPRequest';
type QueuedEventGroup = {
responseReceivedEvent: ResponseReceivedEvent;
loadingFinishedEvent?: LoadingFinishedEvent;
loadingFailedEvent?: LoadingFailedEvent;
};
export type FetchRequestId = string;
type NetworkRequestId = string;
type RedirectInfo = {
event: RequestWillBeSentEvent;
fetchRequestId?: FetchRequestId;
};
export declare class NetworkEventManager {
#private;
forget(networkRequestId: NetworkRequestId): void;
queueFailedLoadInfo(networkRequestId: NetworkRequestId, event: LoadingFailedEvent): void;
getFailedLoadInfo(networkRequestId: NetworkRequestId): LoadingFailedEvent | undefined;
getResponseExtraInfo(networkRequestId: NetworkRequestId): ResponseReceivedExtraInfoEvent[];
private queuedRedirectInfo;
queueRedirectInfo(fetchRequestId: FetchRequestId, redirectInfo: RedirectInfo): void;
takeQueuedRedirectInfo(fetchRequestId: FetchRequestId): RedirectInfo | undefined;
storeRequestWillBeSent(networkRequestId: NetworkRequestId, event: RequestWillBeSentEvent): void;
getRequestWillBeSent(networkRequestId: NetworkRequestId): RequestWillBeSentEvent | undefined;
forgetRequestWillBeSent(networkRequestId: NetworkRequestId): void;
storeRequestPaused(networkRequestId: NetworkRequestId, event: RequestPausedEvent): void;
getRequest(networkRequestId: NetworkRequestId): HTTPRequest | undefined;
storeRequest(networkRequestId: NetworkRequestId, request: HTTPRequest): void;
forgetRequest(networkRequestId: NetworkRequestId): void;
getQueuedEventGroup(networkRequestId: NetworkRequestId): QueuedEventGroup | undefined;
queueEventGroup(networkRequestId: NetworkRequestId, event: QueuedEventGroup): void;
forgetQueuedEventGroup(networkRequestId: NetworkRequestId): void;
}
export {};
@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkEventManager = void 0;
class NetworkEventManager {
#requestWillBeSentMap = new Map();
#requestPausedMap = new Map();
#httpRequestsMap = new Map();
#responseReceivedExtraInfoMap = new Map();
#queuedRedirectInfoMap = new Map();
#queuedEventGroupMap = new Map();
#failedLoadInfoMap = new Map();
forget(networkRequestId) {
this.#requestWillBeSentMap.delete(networkRequestId);
this.#requestPausedMap.delete(networkRequestId);
this.#queuedEventGroupMap.delete(networkRequestId);
this.#queuedRedirectInfoMap.delete(networkRequestId);
this.#responseReceivedExtraInfoMap.delete(networkRequestId);
this.#failedLoadInfoMap.delete(networkRequestId);
}
queueFailedLoadInfo(networkRequestId, event) {
this.#failedLoadInfoMap.set(networkRequestId, { event });
}
getFailedLoadInfo(networkRequestId) {
var _a;
return (_a = this.#failedLoadInfoMap.get(networkRequestId)) === null || _a === void 0 ? void 0 : _a.event;
}
getResponseExtraInfo(networkRequestId) {
if (!this.#responseReceivedExtraInfoMap.has(networkRequestId)) {
this.#responseReceivedExtraInfoMap.set(networkRequestId, []);
}
return this.#responseReceivedExtraInfoMap.get(networkRequestId);
}
queuedRedirectInfo(fetchRequestId) {
if (!this.#queuedRedirectInfoMap.has(fetchRequestId)) {
this.#queuedRedirectInfoMap.set(fetchRequestId, []);
}
return this.#queuedRedirectInfoMap.get(fetchRequestId);
}
queueRedirectInfo(fetchRequestId, redirectInfo) {
this.queuedRedirectInfo(fetchRequestId).push(redirectInfo);
}
takeQueuedRedirectInfo(fetchRequestId) {
return this.queuedRedirectInfo(fetchRequestId).shift();
}
storeRequestWillBeSent(networkRequestId, event) {
this.#requestWillBeSentMap.set(networkRequestId, event);
}
getRequestWillBeSent(networkRequestId) {
return this.#requestWillBeSentMap.get(networkRequestId);
}
forgetRequestWillBeSent(networkRequestId) {
this.#requestWillBeSentMap.delete(networkRequestId);
}
storeRequestPaused(networkRequestId, event) {
this.#requestPausedMap.set(networkRequestId, event);
}
getRequest(networkRequestId) {
return this.#httpRequestsMap.get(networkRequestId);
}
storeRequest(networkRequestId, request) {
this.#httpRequestsMap.set(networkRequestId, request);
}
forgetRequest(networkRequestId) {
this.#httpRequestsMap.delete(networkRequestId);
}
getQueuedEventGroup(networkRequestId) {
return this.#queuedEventGroupMap.get(networkRequestId);
}
queueEventGroup(networkRequestId, event) {
this.#queuedEventGroupMap.set(networkRequestId, event);
}
forgetQueuedEventGroup(networkRequestId) {
this.#queuedEventGroupMap.delete(networkRequestId);
}
}
exports.NetworkEventManager = NetworkEventManager;
@@ -0,0 +1,37 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { LogLevel } from '../log-level';
import type { Commands } from './devtools-commands';
import { EventEmitter } from './EventEmitter';
import type { Frame } from './FrameManager';
export declare const NetworkManagerEmittedEvents: {
readonly Request: symbol;
};
interface CDPSession extends EventEmitter {
send<T extends keyof Commands>(method: T, ...paramArgs: Commands[T]['paramsType']): Promise<{
value: Commands[T]['returnType'];
size: number;
}>;
}
interface FrameManager {
frame(frameId: string): Frame | null;
}
export declare class NetworkManager extends EventEmitter {
#private;
constructor(client: CDPSession, frameManager: FrameManager, indent: boolean, logLevel: LogLevel);
initialize(): Promise<void>;
}
export {};
@@ -0,0 +1,253 @@
"use strict";
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkManager = exports.NetworkManagerEmittedEvents = void 0;
const EventEmitter_1 = require("./EventEmitter");
const handle_failed_resource_1 = require("./handle-failed-resource");
const HTTPRequest_1 = require("./HTTPRequest");
const HTTPResponse_1 = require("./HTTPResponse");
const NetworkEventManager_1 = require("./NetworkEventManager");
exports.NetworkManagerEmittedEvents = {
Request: Symbol('NetworkManager.Request'),
};
class NetworkManager extends EventEmitter_1.EventEmitter {
#client;
#frameManager;
#networkEventManager = new NetworkEventManager_1.NetworkEventManager();
#indent;
#logLevel;
constructor(client, frameManager, indent, logLevel) {
super();
this.#client = client;
this.#frameManager = frameManager;
this.#indent = indent;
this.#logLevel = logLevel;
this.#client.on('Fetch.requestPaused', this.#onRequestPaused.bind(this));
this.#client.on('Network.requestWillBeSent', this.#onRequestWillBeSent.bind(this));
this.#client.on('Network.requestServedFromCache', this.#onRequestServedFromCache.bind(this));
this.#client.on('Network.responseReceived', this.#onResponseReceived.bind(this));
this.#client.on('Network.loadingFinished', this.#onLoadingFinished.bind(this));
this.#client.on('Network.loadingFailed', this.#onLoadingFailed.bind(this));
this.#client.on('Network.responseReceivedExtraInfo', this.#onResponseReceivedExtraInfo.bind(this));
}
async initialize() {
await this.#client.send('Network.enable');
}
#onRequestWillBeSent(event) {
this.#onRequest(event, undefined);
}
/**
* CDP may send a Fetch.requestPaused without or before a
* Network.requestWillBeSent
*
* CDP may send multiple Fetch.requestPaused
* for the same Network.requestWillBeSent.
*/
#onRequestPaused(event) {
const { networkId: networkRequestId, requestId: fetchRequestId } = event;
if (!networkRequestId) {
return;
}
const requestWillBeSentEvent = (() => {
const _requestWillBeSentEvent = this.#networkEventManager.getRequestWillBeSent(networkRequestId);
// redirect requests have the same `requestId`,
if (_requestWillBeSentEvent &&
(_requestWillBeSentEvent.request.url !== event.request.url ||
_requestWillBeSentEvent.request.method !== event.request.method)) {
this.#networkEventManager.forgetRequestWillBeSent(networkRequestId);
return;
}
return _requestWillBeSentEvent;
})();
if (requestWillBeSentEvent) {
this.#patchRequestEventHeaders(requestWillBeSentEvent, event);
this.#onRequest(requestWillBeSentEvent, fetchRequestId);
}
else {
this.#networkEventManager.storeRequestPaused(networkRequestId, event);
}
}
#patchRequestEventHeaders(requestWillBeSentEvent, requestPausedEvent) {
requestWillBeSentEvent.request.headers = {
...requestWillBeSentEvent.request.headers,
// includes extra headers, like: Accept, Origin
...requestPausedEvent.request.headers,
};
}
#onRequest(event, fetchRequestId) {
if (event.redirectResponse) {
// We want to emit a response and requestfinished for the
// redirectResponse, but we can't do so unless we have a
// responseExtraInfo ready to pair it up with. If we don't have any
// responseExtraInfos saved in our queue, they we have to wait until
// the next one to emit response and requestfinished, *and* we should
// also wait to emit this Request too because it should come after the
// response/requestfinished.
let redirectResponseExtraInfo = null;
if (event.redirectHasExtraInfo) {
redirectResponseExtraInfo = this.#networkEventManager
.getResponseExtraInfo(event.requestId)
.shift();
if (!redirectResponseExtraInfo) {
this.#networkEventManager.queueRedirectInfo(event.requestId, {
event,
fetchRequestId,
});
return;
}
}
const _request = this.#networkEventManager.getRequest(event.requestId);
// If we connect late to the target, we could have missed the
// requestWillBeSent event.
if (_request) {
this.#handleRequestRedirect(_request, event.redirectResponse, redirectResponseExtraInfo);
}
}
const frame = event.frameId
? this.#frameManager.frame(event.frameId)
: null;
const request = new HTTPRequest_1.HTTPRequest(frame, event);
this.#networkEventManager.storeRequest(event.requestId, request);
this.emit(exports.NetworkManagerEmittedEvents.Request, request);
}
#onRequestServedFromCache(event) {
const request = this.#networkEventManager.getRequest(event.requestId);
if (request) {
request._fromMemoryCache = true;
}
}
#handleRequestRedirect(request, responsePayload, extraInfo) {
const response = new HTTPResponse_1.HTTPResponse(responsePayload, extraInfo);
request._response = response;
this.#forgetRequest(request, false);
}
#emitResponseEvent(responseReceived, extraInfo) {
const request = this.#networkEventManager.getRequest(responseReceived.requestId);
// FileUpload sends a response without a matching request.
if (!request) {
return;
}
const response = new HTTPResponse_1.HTTPResponse(responseReceived.response, extraInfo);
request._response = response;
}
#onResponseReceived(event) {
const request = this.#networkEventManager.getRequest(event.requestId);
let extraInfo = null;
if (request && !request._fromMemoryCache && event.hasExtraInfo) {
extraInfo = this.#networkEventManager
.getResponseExtraInfo(event.requestId)
.shift();
if (!extraInfo) {
// Wait until we get the corresponding ExtraInfo event.
this.#networkEventManager.queueEventGroup(event.requestId, {
responseReceivedEvent: event,
});
return;
}
}
this.#emitResponseEvent(event, extraInfo);
}
#onResponseReceivedExtraInfo(event) {
// We may have skipped a redirect response/request pair due to waiting for
// this ExtraInfo event. If so, continue that work now that we have the
// request.
const redirectInfo = this.#networkEventManager.takeQueuedRedirectInfo(event.requestId);
if (redirectInfo) {
this.#networkEventManager
.getResponseExtraInfo(event.requestId)
.push(event);
this.#onRequest(redirectInfo.event, redirectInfo.fetchRequestId);
return;
}
// We may have skipped response and loading events because we didn't have
// this ExtraInfo event yet. If so, emit those events now.
const queuedEvents = this.#networkEventManager.getQueuedEventGroup(event.requestId);
if (queuedEvents) {
this.#networkEventManager.forgetQueuedEventGroup(event.requestId);
this.#emitResponseEvent(queuedEvents.responseReceivedEvent, event);
if (queuedEvents.loadingFinishedEvent) {
this.#emitLoadingFinished(queuedEvents.loadingFinishedEvent);
}
if (queuedEvents.loadingFailedEvent) {
this.#emitLoadingFailed(queuedEvents.loadingFailedEvent);
}
return;
}
// Wait until we get another event that can use this ExtraInfo event.
this.#networkEventManager.getResponseExtraInfo(event.requestId).push(event);
}
#forgetRequest(request, events) {
const requestId = request._requestId;
this.#networkEventManager.forgetRequest(requestId);
if (events) {
this.#networkEventManager.forget(requestId);
}
}
#onLoadingFinished(event) {
// If the response event for this request is still waiting on a
// corresponding ExtraInfo event, then wait to emit this event too.
const queuedEvents = this.#networkEventManager.getQueuedEventGroup(event.requestId);
if (queuedEvents) {
queuedEvents.loadingFinishedEvent = event;
}
else {
this.#emitLoadingFinished(event);
}
}
#emitLoadingFinished(event) {
const request = this.#networkEventManager.getRequest(event.requestId);
// For certain requestIds we never receive requestWillBeSent event.
// @see https://crbug.com/750469
if (!request) {
return;
}
this.#forgetRequest(request, true);
}
#onLoadingFailed(event) {
// If the response event for this request is still waiting on a
// corresponding ExtraInfo event, then wait to emit this event too.
const queuedEvents = this.#networkEventManager.getQueuedEventGroup(event.requestId);
if (queuedEvents) {
queuedEvents.loadingFailedEvent = event;
}
else {
this.#emitLoadingFailed(event);
}
}
#emitLoadingFailed(event) {
const request = this.#networkEventManager.getRequest(event.requestId);
// For certain requestIds we never receive requestWillBeSent event.
// @see https://crbug.com/750469
if (!request) {
return;
}
if (event.canceled) {
this.#forgetRequest(request, true);
return;
}
const extraInfo = this.#networkEventManager.getResponseExtraInfo(event.requestId);
(0, handle_failed_resource_1.handleFailedResource)({
extraInfo,
event,
indent: this.#indent,
logLevel: this.#logLevel,
request,
});
this.#forgetRequest(request, true);
}
}
exports.NetworkManager = NetworkManager;
@@ -0,0 +1,19 @@
import type { WS } from '../ws/ws-types';
interface ConnectionTransport {
send(message: string): void;
close(): void;
onmessage?: (message: string) => void;
onclose?: () => void;
}
export declare class NodeWebSocketTransport implements ConnectionTransport {
static create(urlString: string): Promise<NodeWebSocketTransport>;
websocket: WS;
onmessage?: (message: string) => void;
onclose?: () => void;
constructor(ws: WS);
send(message: string): void;
close(): void;
forgetEventLoop(): void;
rememberEventLoop(): void;
}
export {};
@@ -0,0 +1,84 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeWebSocketTransport = void 0;
/**
* Copyright 2018 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const node_dns_1 = require("node:dns");
const node_url_1 = require("node:url");
const ws_types_1 = require("../ws/ws-types");
class NodeWebSocketTransport {
static async create(urlString) {
// Starting in Node 17, IPv6 is favoured over IPv4 due to a change
// in a default option:
// - https://github.com/nodejs/node/issues/40537,
// Due to this, for Firefox, we must parse and resolve the `localhost` hostname
// manually with the previous behavior according to:
// - https://nodejs.org/api/dns.html#dnslookuphostname-options-callback
// because of https://bugzilla.mozilla.org/show_bug.cgi?id=1769994.
const url = new node_url_1.URL(urlString);
if (url.hostname === 'localhost') {
const { address } = await node_dns_1.promises.lookup(url.hostname, { verbatim: false });
url.hostname = address;
}
return new Promise((resolve, reject) => {
const ws = new ws_types_1.ws(url, [], {
followRedirects: true,
perMessageDeflate: false,
maxPayload: 1024 * 1024 * 1024, // 1024Mb
headers: {
'User-Agent': `Remotion CLI`,
},
});
ws.addEventListener('open', () => {
return resolve(new NodeWebSocketTransport(ws));
});
ws.addEventListener('error', reject);
});
}
websocket;
onmessage;
onclose;
constructor(ws) {
this.websocket = ws;
this.websocket.addEventListener('message', (event) => {
if (this.onmessage) {
this.onmessage.call(null, event.data);
}
});
this.websocket.addEventListener('close', () => {
if (this.onclose) {
this.onclose.call(null);
}
});
// Silently ignore all errors - we don't know what to do with them.
this.websocket.addEventListener('error', () => undefined);
}
send(message) {
this.websocket.send(message);
}
close() {
this.websocket.close();
}
forgetEventLoop() {
// @ts-expect-error
this.websocket._socket.unref();
}
rememberEventLoop() {
// @ts-expect-error
this.websocket._socket.ref();
}
}
exports.NodeWebSocketTransport = NodeWebSocketTransport;
@@ -0,0 +1,5 @@
export interface Viewport {
width: number;
height: number;
deviceScaleFactor: number;
}
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
"use strict";
@@ -0,0 +1,72 @@
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BrowserLog } from '../browser-log';
import type { LogLevel } from '../log-level';
import type { BrowserContext, HeadlessBrowser } from './Browser';
import { OnLog, Page } from './BrowserPage';
import type { CDPSession } from './Connection';
import type { TargetInfo } from './devtools-types';
import type { Viewport } from './PuppeteerViewport';
import type { SourceMapGetter } from './source-map-getter';
export declare class Target {
#private;
_initializedPromise: Promise<boolean> | null;
_initializedCallback: (x: boolean) => void;
_isClosedPromise: Promise<void>;
_closedCallback: () => void;
_isInitialized: boolean;
_targetId: string;
constructor(targetInfo: TargetInfo, browserContext: BrowserContext, sessionFactory: () => Promise<CDPSession>, defaultViewport: Viewport);
/**
* Creates a Chrome Devtools Protocol session attached to the target.
*/
createCDPSession(): Promise<CDPSession>;
_getTargetInfo(): TargetInfo;
/**
* If the target is not of type `"page"` or `"background_page"`, returns `null`.
*/
page({ sourceMapGetter, logLevel, indent, pageIndex, onBrowserLog, onLog }: {
sourceMapGetter: SourceMapGetter;
logLevel: LogLevel;
indent: boolean;
pageIndex: number;
onBrowserLog: null | ((log: BrowserLog) => void);
onLog: OnLog;
}): Promise<Page | null>;
expectPage(): Promise<Page | null>;
url(): string;
/**
* Identifies what kind of target this is.
*
* @remarks
*
* See {@link https://developer.chrome.com/extensions/background_pages | docs} for more info about background pages.
*/
type(): 'page' | 'background_page' | 'service_worker' | 'shared_worker' | 'other' | 'browser' | 'webview';
/**
* Get the browser the target belongs to.
*/
browser(): HeadlessBrowser;
/**
* Get the browser context the target belongs to.
*/
browserContext(): BrowserContext;
/**
* Get the target that opened this target. Top-level targets return `null`.
*/
opener(): Target | undefined;
_targetInfoChanged(targetInfo: TargetInfo): void;
}
@@ -0,0 +1,154 @@
"use strict";
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Target = void 0;
const BrowserPage_1 = require("./BrowserPage");
const isPagetTarget = (target) => {
return (target.type === 'page' ||
target.type === 'background_page' ||
target.type === 'webview');
};
class Target {
#browserContext;
#targetInfo;
#sessionFactory;
#defaultViewport;
#pagePromise;
_initializedPromise;
_initializedCallback;
_isClosedPromise;
_closedCallback;
_isInitialized;
_targetId;
constructor(targetInfo, browserContext, sessionFactory, defaultViewport) {
this.#targetInfo = targetInfo;
this.#browserContext = browserContext;
this._targetId = targetInfo.targetId;
this.#sessionFactory = sessionFactory;
this.#defaultViewport = defaultViewport;
this._initializedPromise = new Promise((fulfill) => {
this._initializedCallback = fulfill;
}).then((success) => {
if (!success) {
return false;
}
const opener = this.opener();
if (!opener || !opener.#pagePromise || this.type() !== 'page') {
return true;
}
return true;
});
this._isClosedPromise = new Promise((fulfill) => {
this._closedCallback = fulfill;
});
this._isInitialized =
!isPagetTarget(this.#targetInfo) || this.#targetInfo.url !== '';
if (this._isInitialized) {
this._initializedCallback(true);
}
}
/**
* Creates a Chrome Devtools Protocol session attached to the target.
*/
createCDPSession() {
return this.#sessionFactory();
}
_getTargetInfo() {
return this.#targetInfo;
}
/**
* If the target is not of type `"page"` or `"background_page"`, returns `null`.
*/
async page({ sourceMapGetter, logLevel, indent, pageIndex, onBrowserLog, onLog, }) {
var _a;
if (isPagetTarget(this.#targetInfo) && !this.#pagePromise) {
this.#pagePromise = this.#sessionFactory().then((client) => {
var _a;
return BrowserPage_1.Page._create({
client,
target: this,
defaultViewport: (_a = this.#defaultViewport) !== null && _a !== void 0 ? _a : null,
browser: this.browser(),
sourceMapGetter,
logLevel,
indent,
pageIndex,
onBrowserLog,
onLog,
});
});
}
return (_a = (await this.#pagePromise)) !== null && _a !== void 0 ? _a : null;
}
async expectPage() {
var _a;
return (_a = (await this.#pagePromise)) !== null && _a !== void 0 ? _a : null;
}
url() {
return this.#targetInfo.url;
}
/**
* Identifies what kind of target this is.
*
* @remarks
*
* See {@link https://developer.chrome.com/extensions/background_pages | docs} for more info about background pages.
*/
type() {
const { type } = this.#targetInfo;
if (type === 'page' ||
type === 'background_page' ||
type === 'service_worker' ||
type === 'shared_worker' ||
type === 'browser' ||
type === 'webview') {
return type;
}
return 'other';
}
/**
* Get the browser the target belongs to.
*/
browser() {
return this.#browserContext.browser();
}
/**
* Get the browser context the target belongs to.
*/
browserContext() {
return this.#browserContext;
}
/**
* Get the target that opened this target. Top-level targets return `null`.
*/
opener() {
const { openerId } = this.#targetInfo;
if (!openerId) {
return;
}
return this.browser()._targets.get(openerId);
}
_targetInfoChanged(targetInfo) {
this.#targetInfo = targetInfo;
if (!this._isInitialized &&
(!isPagetTarget(this.#targetInfo) || this.#targetInfo.url !== '')) {
this._isInitialized = true;
this._initializedCallback(true);
}
}
}
exports.Target = Target;
@@ -0,0 +1,20 @@
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export declare class TaskQueue {
#private;
constructor();
postTask<T>(task: () => Promise<T>): Promise<T>;
}
@@ -0,0 +1,34 @@
"use strict";
/**
* Copyright 2020 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskQueue = void 0;
class TaskQueue {
#chain;
constructor() {
this.#chain = Promise.resolve();
}
postTask(task) {
const result = this.#chain.then(task);
this.#chain = result.then(() => {
return undefined;
}, () => {
return undefined;
});
return result;
}
}
exports.TaskQueue = TaskQueue;
@@ -0,0 +1,24 @@
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export declare const DEFAULT_TIMEOUT = 30000;
export declare class TimeoutSettings {
#private;
constructor();
setDefaultTimeout(timeout: number): void;
setDefaultNavigationTimeout(timeout: number): void;
navigationTimeout(): number;
timeout(): number;
}
@@ -0,0 +1,49 @@
"use strict";
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TimeoutSettings = exports.DEFAULT_TIMEOUT = void 0;
exports.DEFAULT_TIMEOUT = 30000;
class TimeoutSettings {
#defaultTimeout;
#defaultNavigationTimeout;
constructor() {
this.#defaultTimeout = null;
this.#defaultNavigationTimeout = null;
}
setDefaultTimeout(timeout) {
this.#defaultTimeout = timeout;
}
setDefaultNavigationTimeout(timeout) {
this.#defaultNavigationTimeout = timeout;
}
navigationTimeout() {
if (this.#defaultNavigationTimeout !== null) {
return this.#defaultNavigationTimeout;
}
if (this.#defaultTimeout !== null) {
return this.#defaultTimeout;
}
return exports.DEFAULT_TIMEOUT;
}
timeout() {
if (this.#defaultTimeout !== null) {
return this.#defaultTimeout;
}
return exports.DEFAULT_TIMEOUT;
}
}
exports.TimeoutSettings = TimeoutSettings;
@@ -0,0 +1 @@
export declare const assert: (value: unknown, message?: string) => asserts value;
@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.assert = void 0;
const assert = (value, message) => {
if (!value) {
throw new Error(message);
}
};
exports.assert = assert;
@@ -0,0 +1,6 @@
import type { OnBrowserDownload } from '../options/on-browser-download';
export declare const defaultBrowserDownloadProgress: ({ indent, logLevel, api, }: {
indent: boolean;
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
api: string;
}) => OnBrowserDownload;
@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultBrowserDownloadProgress = void 0;
const logger_1 = require("../logger");
const to_megabytes_1 = require("../to-megabytes");
const defaultBrowserDownloadProgress = ({ indent, logLevel, api, }) => ({ chromeMode }) => {
if (chromeMode === 'chrome-for-testing') {
logger_1.Log.info({ indent, logLevel }, 'Downloading Chrome for Testing https://www.remotion.dev/chrome-for-testing');
}
else {
logger_1.Log.info({ indent, logLevel }, 'Downloading Chrome Headless Shell https://www.remotion.dev/chrome-headless-shell');
}
logger_1.Log.info({ indent, logLevel }, `Customize this behavior by adding a onBrowserDownload function to ${api}.`);
let lastProgress = 0;
return {
onProgress: (progress) => {
if (progress.downloadedBytes > lastProgress + 10000000 ||
progress.percent === 1) {
lastProgress = progress.downloadedBytes;
if (chromeMode === 'chrome-for-testing') {
logger_1.Log.info({ indent, logLevel }, `Downloading Chrome for Testing - ${(0, to_megabytes_1.toMegabytes)(progress.downloadedBytes)}/${(0, to_megabytes_1.toMegabytes)(progress.totalSizeInBytes)}`);
}
else {
logger_1.Log.info({ indent, logLevel }, `Downloading Chrome Headless Shell - ${(0, to_megabytes_1.toMegabytes)(progress.downloadedBytes)}/${(0, to_megabytes_1.toMegabytes)(progress.totalSizeInBytes)}`);
}
}
},
version: null,
};
};
exports.defaultBrowserDownloadProgress = defaultBrowserDownloadProgress;
@@ -0,0 +1,288 @@
import type { ActivateTargetRequest, AddScriptToEvaluateOnNewDocumentRequest, AddScriptToEvaluateOnNewDocumentResponse, CallFunctionOnRequest, CallFunctionOnResponse, CaptureScreenshotRequest, CaptureScreenshotResponse, CloseTargetRequest, CloseTargetResponse, CreateIsolatedWorldRequest, CreateIsolatedWorldResponse, DetachFromTargetRequest, DevtoolsRemoteObject, EnableRequest, ExceptionDetails, GetFrameTreeResponse, NavigateRequest, NavigateResponse, PrintPDFRequest, PrintPDFResponse, ReleaseObjectRequest, SetAutoAttachRequest, SetDefaultBackgroundColorOverrideRequest, SetDeviceMetricsOverrideRequest, SetLifecycleEventsEnabledRequest } from './devtools-types';
export interface Commands {
/**
* Does nothing.
*/
'Console.clearMessages': {
paramsType: [];
returnType: void;
};
'Target.setDiscoverTargets': {
paramsType: [
{
/**
* Whether to discover available targets.
*/
discover: boolean;
}
];
returnType: void;
};
'Target.createTarget': {
paramsType: [
{
/**
* The initial URL the page will be navigated to. An empty string indicates about:blank.
*/
url: string;
/**
* Frame width in DIP (headless chrome only).
*/
width?: number;
/**
* Frame height in DIP (headless chrome only).
*/
height?: number;
/**
* The browser context to create the page in.
*/
browserContextId?: string;
/**
* Whether BeginFrames for this target will be controlled via DevTools (headless chrome only,
* not supported on MacOS yet, false by default).
*/
enableBeginFrameControl?: boolean;
/**
* Whether to create a new Window or Tab (chrome-only, false by default).
*/
newWindow?: boolean;
/**
* Whether to create the target in background or foreground (chrome-only,
* false by default).
*/
background?: boolean;
}
];
returnType: {
targetId: string;
};
};
'Browser.close': {
paramsType: [];
returnType: void;
};
'Target.attachToTarget': {
paramsType: [
{
targetId: string;
/**
* Enables "flat" access to the session via specifying sessionId attribute in the commands.
* We plan to make this the default, deprecate non-flattened mode,
* and eventually retire it. See crbug.com/991325.
*/
flatten?: boolean;
}
];
returnType: {
/**
* Id assigned to the session.
*/
sessionId: string;
};
};
'Runtime.callFunctionOn': {
paramsType: [CallFunctionOnRequest];
returnType: CallFunctionOnResponse;
};
'Runtime.evaluate': {
paramsType: [
{
/**
* Expression to evaluate.
*/
expression: string;
/**
* Symbolic group name that can be used to release multiple objects.
*/
objectGroup?: string;
/**
* Determines whether Command Line API should be available during the evaluation.
*/
includeCommandLineAPI?: boolean;
/**
* In silent mode exceptions thrown during evaluation are not reported and do not pause
* execution. Overrides `setPauseOnException` state.
*/
silent?: boolean;
/**
* Specifies in which execution context to perform evaluation. If the parameter is omitted the
* evaluation will be performed in the context of the inspected page.
* This is mutually exclusive with `uniqueContextId`, which offers an
* alternative way to identify the execution context that is more reliable
* in a multi-process environment.
*/
contextId?: number;
/**
* Whether the result is expected to be a JSON object that should be sent by value.
*/
returnByValue?: boolean;
/**
* Whether preview should be generated for the result.
*/
generatePreview?: boolean;
/**
* Whether execution should be treated as initiated by user in the UI.
*/
userGesture?: boolean;
/**
* Whether execution should `await` for resulting value and return once awaited promise is
* resolved.
*/
awaitPromise?: boolean;
/**
* Whether to throw an exception if side effect cannot be ruled out during evaluation.
* This implies `disableBreaks` below.
*/
throwOnSideEffect?: boolean;
/**
* Terminate execution after timing out (number of milliseconds).
*/
/**
* Disable breakpoints during execution.
*/
disableBreaks?: boolean;
/**
* Setting this flag to true enables `let` re-declaration and top-level `await`.
* Note that `let` variables can only be re-declared if they originate from
* `replMode` themselves.
*/
replMode?: boolean;
/**
* The Content Security Policy (CSP) for the target might block 'unsafe-eval'
* which includes eval(), Function(), setTimeout() and setInterval()
* when called with non-callable arguments. This flag bypasses CSP for this
* evaluation and allows unsafe-eval. Defaults to true.
*/
allowUnsafeEvalBlockedByCSP?: boolean;
/**
* An alternative way to specify the execution context to evaluate in.
* Compared to contextId that may be reused across processes, this is guaranteed to be
* system-unique, so it can be used to prevent accidental evaluation of the expression
* in context different than intended (e.g. as a result of navigation across process
* boundaries).
* This is mutually exclusive with `contextId`.
*/
uniqueContextId?: string;
/**
* Whether the result should be serialized according to https://w3c.github.io/webdriver-bidi.
*/
generateWebDriverValue?: boolean;
}
];
returnType: {
/**
* Evaluation result.
*/
result: DevtoolsRemoteObject;
/**
* Exception details.
*/
exceptionDetails?: ExceptionDetails;
};
};
'Page.enable': {
paramsType: [];
returnType: void;
};
'Page.getFrameTree': {
paramsType: [];
returnType: GetFrameTreeResponse;
};
'Target.setAutoAttach': {
paramsType: [SetAutoAttachRequest];
returnType: void;
};
'Page.setLifecycleEventsEnabled': {
paramsType: [SetLifecycleEventsEnabledRequest];
returnType: void;
};
'Runtime.enable': {
paramsType: [];
returnType: void;
};
'Page.navigate': {
paramsType: [NavigateRequest];
returnType: NavigateResponse;
};
'Page.addScriptToEvaluateOnNewDocument': {
paramsType: [AddScriptToEvaluateOnNewDocumentRequest];
returnType: AddScriptToEvaluateOnNewDocumentResponse;
};
'Page.createIsolatedWorld': {
paramsType: [CreateIsolatedWorldRequest];
returnType: CreateIsolatedWorldResponse;
};
'Page.captureScreenshot': {
paramsType: [CaptureScreenshotRequest?];
returnType: CaptureScreenshotResponse;
};
'Page.printToPDF': {
paramsType: [PrintPDFRequest];
returnType: PrintPDFResponse;
};
'Target.activateTarget': {
paramsType: [ActivateTargetRequest];
returnType: void;
};
'Emulation.setDefaultBackgroundColorOverride': {
paramsType: [SetDefaultBackgroundColorOverrideRequest?];
returnType: void;
};
'Network.enable': {
paramsType: [EnableRequest?];
returnType: void;
};
'Target.detachFromTarget': {
paramsType: [DetachFromTargetRequest?];
returnType: void;
};
/**
* Enable collecting and reporting metrics.
*/
'Performance.enable': {
paramsType: [EnableRequest?];
returnType: void;
};
'Log.enable': {
paramsType: [];
returnType: void;
};
'Page.setDeviceMetricsOverride': {
paramsType: [SetDeviceMetricsOverrideRequest];
returnType: void;
};
'Page.bringToFront': {
paramsType: [];
returnType: void;
};
'Emulation.setEmulatedMedia': {
paramsType: [
{
media: 'screen';
features: [
{
name: 'prefers-color-scheme';
value: 'dark';
}
];
}
];
returnType: void;
};
'Emulation.setDeviceMetricsOverride': {
paramsType: [SetDeviceMetricsOverrideRequest];
returnType: void;
};
'Target.closeTarget': {
paramsType: [CloseTargetRequest];
returnType: CloseTargetResponse;
};
'Page.close': {
paramsType: [];
returnType: void;
};
'Runtime.releaseObject': {
paramsType: [ReleaseObjectRequest];
returnType: void;
};
}
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
export declare const isTargetClosedErr: (error: Error | undefined) => boolean | undefined;
export declare const isFlakyNetworkError: (error: Error | undefined) => boolean | undefined;
@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isFlakyNetworkError = exports.isTargetClosedErr = void 0;
const isTargetClosedErr = (error) => {
var _a, _b;
return (((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('Target closed')) ||
((_b = error === null || error === void 0 ? void 0 : error.message) === null || _b === void 0 ? void 0 : _b.includes('Session closed')));
};
exports.isTargetClosedErr = isTargetClosedErr;
const isFlakyNetworkError = (error) => {
var _a, _b, _c, _d, _e, _f, _g;
return (((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('ERR_CONNECTION_REFUSED')) ||
((_b = error === null || error === void 0 ? void 0 : error.message) === null || _b === void 0 ? void 0 : _b.includes('ERR_CONNECTION_RESET')) ||
((_c = error === null || error === void 0 ? void 0 : error.message) === null || _c === void 0 ? void 0 : _c.includes('ERR_CONNECTION_TIMED_OUT')) ||
((_d = error === null || error === void 0 ? void 0 : error.message) === null || _d === void 0 ? void 0 : _d.includes('ERR_INTERNET_DISCONNECTED')) ||
((_e = error === null || error === void 0 ? void 0 : error.message) === null || _e === void 0 ? void 0 : _e.includes('ERR_NAME_RESOLUTION_FAILED')) ||
((_f = error === null || error === void 0 ? void 0 : error.message) === null || _f === void 0 ? void 0 : _f.includes('ERR_ADDRESS_UNREACHABLE')) ||
((_g = error === null || error === void 0 ? void 0 : error.message) === null || _g === void 0 ? void 0 : _g.includes('ERR_NETWORK_CHANGED')));
};
exports.isFlakyNetworkError = isFlakyNetworkError;
@@ -0,0 +1,15 @@
import type { ChromeMode } from '../options/chrome-mode';
export declare const TESTED_VERSION = "144.0.7559.20";
export type Platform = 'linux64' | 'linux-arm64' | 'mac-x64' | 'mac-arm64' | 'win64';
export declare const isAmazonLinux2023: () => boolean;
export declare const canUseRemotionMediaBinaries: () => boolean;
export declare function getChromeDownloadUrl({ platform, version, chromeMode }: {
platform: Platform;
version: string | null;
chromeMode: ChromeMode;
}): string;
export declare const logDownloadUrl: ({ url, logLevel, indent, }: {
url: string;
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
indent: boolean;
}) => void;
@@ -0,0 +1,147 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.logDownloadUrl = exports.canUseRemotionMediaBinaries = exports.isAmazonLinux2023 = exports.TESTED_VERSION = void 0;
exports.getChromeDownloadUrl = getChromeDownloadUrl;
const fs = __importStar(require("node:fs"));
const os = __importStar(require("node:os"));
const logger_1 = require("../logger");
exports.TESTED_VERSION = '144.0.7559.20';
// https://github.com/microsoft/playwright/blame/e76ca6cba40c26bf22c19cf37398d2b9da9ed465/packages/playwright-core/browsers.json
// packages/playwright-core/browsers.json
const PLAYWRIGHT_VERSION = '1207'; // 144.0.7559.20
const isAmazonLinux2023 = () => {
if (os.platform() !== 'linux') {
return false;
}
try {
const osRelease = fs.readFileSync('/etc/os-release', 'utf-8');
return (osRelease.includes('Amazon Linux') && osRelease.includes('VERSION="2023"'));
}
catch (_a) {
return false;
}
};
exports.isAmazonLinux2023 = isAmazonLinux2023;
// remotion.media binaries are built on Ubuntu 24.04 which requires glibc 2.35+
const MINIMUM_GLIBC_FOR_REMOTION_MEDIA = [2, 35];
const getGlibcVersion = () => {
if (process.platform !== 'linux') {
return null;
}
const { report } = process;
if (!report) {
return null;
}
const rep = report.getReport();
if (typeof rep === 'string') {
return null;
}
// @ts-expect-error no types
const { glibcVersionRuntime } = rep.header;
if (!glibcVersionRuntime) {
return null;
}
const split = glibcVersionRuntime.split('.');
if (split.length !== 2) {
return null;
}
return [Number(split[0]), Number(split[1])];
};
const isGlibcVersionAtLeast = (required) => {
const version = getGlibcVersion();
if (version === null) {
// If we can't detect, assume it's not compatible to be safe
return false;
}
const [major, minor] = version;
const [reqMajor, reqMinor] = required;
if (major > reqMajor) {
return true;
}
if (major === reqMajor && minor >= reqMinor) {
return true;
}
return false;
};
const canUseRemotionMediaBinaries = () => {
if (process.platform !== 'linux') {
// remotion.media binaries are only for Linux
return false;
}
return isGlibcVersionAtLeast(MINIMUM_GLIBC_FOR_REMOTION_MEDIA);
};
exports.canUseRemotionMediaBinaries = canUseRemotionMediaBinaries;
function getChromeDownloadUrl({ platform, version, chromeMode, }) {
if (platform === 'linux-arm64') {
// Amazon Linux 2023 on arm64 needs a special build.
// This binary is compatible with older glibc (no 2.35 requirement).
if ((0, exports.isAmazonLinux2023)() && chromeMode === 'headless-shell' && !version) {
return 'https://remotion.media/chromium-headless-shell-amazon-linux-arm64-144.0.7559.20.zip';
}
if (chromeMode === 'chrome-for-testing') {
return `https://playwright.azureedge.net/builds/chromium/${version !== null && version !== void 0 ? version : PLAYWRIGHT_VERSION}/chromium-linux-arm64.zip`;
}
if (version) {
return `https://playwright.azureedge.net/builds/chromium/${version}/chromium-headless-shell-linux-arm64.zip`;
}
// Regular arm64 binary requires glibc 2.35+
if ((0, exports.canUseRemotionMediaBinaries)()) {
return `https://remotion.media/chromium-headless-shell-linux-arm64-${exports.TESTED_VERSION}.zip?clearcache`;
}
// Fall back to Playwright for older glibc (non-Amazon Linux systems)
return `https://playwright.azureedge.net/builds/chromium/${PLAYWRIGHT_VERSION}/chromium-headless-shell-linux-arm64.zip`;
}
if (chromeMode === 'headless-shell') {
// Amazon Linux 2023 needs a special build.
// This binary is compatible with older glibc (no 2.35 requirement).
if ((0, exports.isAmazonLinux2023)() && platform === 'linux64' && !version) {
return `https://remotion.media/chromium-headless-shell-amazon-linux-x64-144.0.7559.20.zip`;
}
if (platform === 'linux64' && version === null) {
if ((0, exports.canUseRemotionMediaBinaries)()) {
return `https://remotion.media/chromium-headless-shell-linux-x64-${exports.TESTED_VERSION}.zip?clearcache`;
}
// Fall back to Google's CDN for older glibc
return `https://storage.googleapis.com/chrome-for-testing-public/${exports.TESTED_VERSION}/${platform}/chrome-headless-shell-${platform}.zip`;
}
return `https://storage.googleapis.com/chrome-for-testing-public/${version !== null && version !== void 0 ? version : exports.TESTED_VERSION}/${platform}/chrome-headless-shell-${platform}.zip`;
}
return `https://storage.googleapis.com/chrome-for-testing-public/${version !== null && version !== void 0 ? version : exports.TESTED_VERSION}/${platform}/chrome-${platform}.zip`;
}
const logDownloadUrl = ({ url, logLevel, indent, }) => {
logger_1.Log.info({ indent, logLevel }, `Downloading from: ${url}`);
};
exports.logDownloadUrl = logDownloadUrl;
@@ -0,0 +1 @@
export declare const getDownloadsCacheDir: () => string;
@@ -0,0 +1,38 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDownloadsCacheDir = void 0;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const getDownloadsCacheDir = () => {
const cwd = process.cwd();
let dir = cwd;
for (;;) {
try {
if (node_fs_1.default.statSync(node_path_1.default.join(dir, 'package.json')).isFile()) {
break;
}
// eslint-disable-next-line no-empty
}
catch (e) { }
const parent = node_path_1.default.dirname(dir);
if (dir === parent) {
dir = undefined;
break;
}
dir = parent;
}
if (!dir) {
return node_path_1.default.resolve(cwd, '.remotion');
}
if (process.versions.pnp === '1') {
return node_path_1.default.resolve(dir, '.pnp/.remotion');
}
if (process.versions.pnp === '3') {
return node_path_1.default.resolve(dir, '.yarn/.remotion');
}
return node_path_1.default.resolve(dir, 'node_modules/.remotion');
};
exports.getDownloadsCacheDir = getDownloadsCacheDir;
@@ -0,0 +1,9 @@
import type { HTTPRequest } from './HTTPRequest';
import type { LoadingFailedEvent, ResponseReceivedExtraInfoEvent } from './devtools-types';
export declare const handleFailedResource: ({ extraInfo, logLevel, indent, request, event, }: {
extraInfo: ResponseReceivedExtraInfoEvent[];
logLevel: "error" | "info" | "trace" | "verbose" | "warn";
indent: boolean;
request: HTTPRequest;
event: LoadingFailedEvent;
}) => void;
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleFailedResource = void 0;
const logger_1 = require("../logger");
const handleFailedResource = ({ extraInfo, logLevel, indent, request, event, }) => {
var _a;
var _b;
const firstExtraInfo = (_b = extraInfo[0]) !== null && _b !== void 0 ? _b : null;
logger_1.Log.warn({ indent, logLevel }, `Browser failed to load ${request._url} (${event.type}): ${event.errorText}`);
if (firstExtraInfo) {
logger_1.Log.warn({ indent, logLevel }, `HTTP status code: ${firstExtraInfo.statusCode}, headers:`);
logger_1.Log.warn({ indent, logLevel }, JSON.stringify(firstExtraInfo.headers, null, 2));
}
if (event.errorText === 'net::ERR_FAILED' &&
event.type === 'Fetch' &&
((_a = request._url) === null || _a === void 0 ? void 0 : _a.includes('/proxy'))) {
logger_1.Log.warn({ indent, logLevel }, 'This could be caused by Chrome rejecting the request because the disk space is low.');
logger_1.Log.warn({ indent, logLevel }, 'This could be caused by Chrome rejecting the request because the disk space is low.');
logger_1.Log.warn({ indent, logLevel }, 'Consider increasing the disk size of your Lambda function.');
}
};
exports.handleFailedResource = handleFailedResource;
@@ -0,0 +1,22 @@
export type EventType = string | symbol;
export type Handler<T = any> = (event?: T) => void;
type WildcardHandler = (type: EventType, event?: any) => void;
type EventHandlerList = Array<Handler>;
type WildCardEventHandlerList = Array<WildcardHandler>;
type EventHandlerMap = Map<EventType, EventHandlerList | WildCardEventHandlerList>;
export interface Emitter {
all: EventHandlerMap;
on<T = any>(type: EventType, handler: Handler<T>): void;
on(type: '*', handler: WildcardHandler): void;
off<T = any>(type: EventType, handler: Handler<T>): void;
off(type: '*', handler: WildcardHandler): void;
emit<T = any>(type: EventType, event?: T): void;
emit(type: '*', event?: any): void;
}
/**
* Mitt: Tiny (~200b) functional event emitter / pubsub.
* @name mitt
* @returns {Mitt}
*/
export default function mitt(all?: EventHandlerMap): Emitter;
export {};
@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = mitt;
/**
* Mitt: Tiny (~200b) functional event emitter / pubsub.
* @name mitt
* @returns {Mitt}
*/
function mitt(all) {
all = all || new Map();
return {
/**
* A Map of event names to registered handler functions.
*/
all,
/**
* Register an event handler for the given type.
* @param {string|symbol} type Type of event to listen for, or `"*"` for all events
* @param {Function} handler Function to call in response to given event
* @memberOf mitt
*/
on: (type, handler) => {
const handlers = all === null || all === void 0 ? void 0 : all.get(type);
const added = handlers === null || handlers === void 0 ? void 0 : handlers.push(handler);
if (!added) {
all === null || all === void 0 ? void 0 : all.set(type, [handler]);
}
},
off: (type, handler) => {
const handlers = all === null || all === void 0 ? void 0 : all.get(type);
if (handlers) {
// eslint-disable-next-line no-bitwise
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
},
emit: (type, evt) => {
((all === null || all === void 0 ? void 0 : all.get(type)) || [])
.slice()
.forEach((handler) => {
handler(evt);
});
((all === null || all === void 0 ? void 0 : all.get('*')) || [])
.slice()
.forEach((handler) => {
handler(type, evt);
});
},
};
}
@@ -0,0 +1,14 @@
type ParsedBrowserLogMessage = {
day: number;
month: number;
hour: number;
minute: number;
seconds: number;
microseconds: number;
level: string;
location: string;
lineNumber: number;
message: string;
};
export declare const parseBrowserLogMessage: (input: string) => ParsedBrowserLogMessage | null;
export {};
@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseBrowserLogMessage = void 0;
const parseBrowserLogMessage = (input) => {
var _a;
// Chrome <144: [MMDD/HHMMSS.UUUUUU:LEVEL:LOCATION(LINE)] message
// Chrome 144+: [MMDD/HHMMSS.UUUUUU:LEVEL:LOCATION:LINE] message
const format = /^\[([0-9]{4})\/([0-9]{6})\.([0-9]{6}):([A-Z]+):(.*?)(?:\(([0-9]+)\)|:([0-9]+))\](.*)/;
const match = input.match(format);
if (!match) {
return null;
}
const date = match[1];
const day = parseInt(date.slice(0, 2), 10);
const month = parseInt(date.slice(2, 4), 10);
const time = match[2];
const hour = parseInt(time.slice(0, 2), 10);
const minute = parseInt(time.slice(2, 4), 10);
const seconds = parseInt(time.slice(4, 6), 10);
const microseconds = parseInt(match[3], 10);
const level = match[4];
const location = match[5];
const lineNumber = parseInt((_a = match[6]) !== null && _a !== void 0 ? _a : match[7], 10);
const message = match[8].trim();
return {
day,
month,
hour,
minute,
seconds,
microseconds,
level,
location,
lineNumber,
message,
};
};
exports.parseBrowserLogMessage = parseBrowserLogMessage;
@@ -0,0 +1,11 @@
export declare const shouldLogBrowserMessage: (message: string) => boolean;
export declare const formatChromeMessage: (input: string) => {
output: string;
tag: string;
} | null;
type ChromeLogLocation = {
location: string;
lineNumber: number;
};
export declare const parseChromeLogLocation: (message: string) => ChromeLogLocation | null;
export {};
@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseChromeLogLocation = exports.formatChromeMessage = exports.shouldLogBrowserMessage = void 0;
const parse_browser_log_message_1 = require("./parse-browser-log-message");
const shouldLogBrowserMessage = (message) => {
// Not relevant for the user
if (message.startsWith('DevTools listening on')) {
return false;
}
// In Ubuntu GitHub Action
if (message.includes('Falling back to ALSA for audio output')) {
return false;
}
// In Ubuntu GitHub Action
if (message.includes('Floss manager not present, cannot set Floss enable/disable')) {
return false;
}
// Noisy but harmless warning
if (message.includes('Failed to send GpuControl.CreateCommandBuffer')) {
return false;
}
if (message.includes('CreatePlatformSocket() failed: Address family not supported by protocol')) {
return false;
}
if (message.includes('Fontconfig error: No writable cache directories')) {
return false;
}
if (message.includes('AttributionReportingCrossAppWeb cannot be enabled in this configuration')) {
return false;
}
if (message.includes('Trying to Produce a Memory representation from a non-existent mailbox.')) {
return false;
}
if (message.includes('Received HEADERS for invalid stream')) {
return false;
}
if (message.includes('CVDisplayLinkCreateWithCGDisplay failed')) {
return false;
}
if (message.includes('Falling back to ALSA for audio output')) {
return false;
}
if (message.includes('VizNullHypothesis is disabled')) {
return false;
}
return true;
};
exports.shouldLogBrowserMessage = shouldLogBrowserMessage;
const formatChromeMessage = (input) => {
const parsed = (0, parse_browser_log_message_1.parseBrowserLogMessage)(input);
if (!parsed) {
return { output: input, tag: 'chrome' };
}
const { location, lineNumber, message } = parsed;
// Don't print console.log's, these are handled through the WebSocket
if (location === 'CONSOLE') {
return null;
}
return { output: `${location}:${lineNumber}: ${message}`, tag: 'chrome' };
};
exports.formatChromeMessage = formatChromeMessage;
const parseChromeLogLocation = (message) => {
const regex = /(.*), source: (.*) \(([0-9]+)\)/;
const match = message.match(regex);
if (!match) {
return null;
}
return {
lineNumber: parseInt(match[3], 10),
location: match[2],
};
};
exports.parseChromeLogLocation = parseChromeLogLocation;
@@ -0,0 +1,2 @@
import type { AnySourceMapConsumer } from '../symbolicate-stacktrace';
export type SourceMapGetter = () => AnySourceMapConsumer | null;
@@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,37 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { HeadlessBrowser } from './Browser';
import type { CDPSession } from './Connection';
import type { DevtoolsRemoteObject, ExceptionDetails } from './devtools-types';
import type { CommonEventEmitter } from './EventEmitter';
export declare function getExceptionMessage(exceptionDetails: ExceptionDetails): string;
export declare function valueFromRemoteObject(remoteObject: DevtoolsRemoteObject): any;
export declare function releaseObject(client: CDPSession, remoteObject: DevtoolsRemoteObject): Promise<void>;
export declare function addEventListener(emitter: CommonEventEmitter, eventName: string | symbol, handler: (...args: any[]) => void): () => CommonEventEmitter;
export declare function removeEventListeners(listeners: Array<() => void>): void;
export declare const isString: (obj: unknown) => obj is string;
export declare function evaluationString(fun: Function | string, ...args: unknown[]): string;
export declare function pageBindingDeliverResultString(name: string, seq: number, result: unknown): string;
export declare function pageBindingDeliverErrorString(name: string, seq: number, message: string, stack?: string): string;
export declare function pageBindingDeliverErrorValueString(name: string, seq: number, value: unknown): string;
export declare function waitWithTimeout<T>(promise: Promise<T>, taskName: string, timeout: number, browser: HeadlessBrowser): Promise<T>;
interface ErrorLike extends Error {
name: string;
message: string;
}
export declare function isErrorLike(obj: unknown): obj is ErrorLike;
export declare function isErrnoException(obj: unknown): obj is NodeJS.ErrnoException;
export {};
@@ -0,0 +1,169 @@
"use strict";
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.isString = void 0;
exports.getExceptionMessage = getExceptionMessage;
exports.valueFromRemoteObject = valueFromRemoteObject;
exports.releaseObject = releaseObject;
exports.addEventListener = addEventListener;
exports.removeEventListeners = removeEventListeners;
exports.evaluationString = evaluationString;
exports.pageBindingDeliverResultString = pageBindingDeliverResultString;
exports.pageBindingDeliverErrorString = pageBindingDeliverErrorString;
exports.pageBindingDeliverErrorValueString = pageBindingDeliverErrorValueString;
exports.waitWithTimeout = waitWithTimeout;
exports.isErrorLike = isErrorLike;
exports.isErrnoException = isErrnoException;
const assert_1 = require("./assert");
const Errors_1 = require("./Errors");
function getExceptionMessage(exceptionDetails) {
if (exceptionDetails.exception) {
return (exceptionDetails.exception.description || exceptionDetails.exception.value);
}
let message = exceptionDetails.text;
if (exceptionDetails.stackTrace) {
for (const callframe of exceptionDetails.stackTrace.callFrames) {
const location = callframe.url +
':' +
callframe.lineNumber +
':' +
callframe.columnNumber;
const functionName = callframe.functionName || '<anonymous>';
message += `\n at ${functionName} (${location})`;
}
}
return message;
}
function valueFromRemoteObject(remoteObject) {
(0, assert_1.assert)(!remoteObject.objectId, 'Cannot extract value when objectId is given');
if (remoteObject.unserializableValue) {
if (remoteObject.type === 'bigint' && typeof BigInt !== 'undefined') {
return BigInt(remoteObject.unserializableValue.replace('n', ''));
}
switch (remoteObject.unserializableValue) {
case '-0':
return -0;
case 'NaN':
return NaN;
case 'Infinity':
return Infinity;
case '-Infinity':
return -Infinity;
default:
throw new Error('Unsupported unserializable value: ' +
remoteObject.unserializableValue);
}
}
return remoteObject.value;
}
async function releaseObject(client, remoteObject) {
if (!remoteObject.objectId) {
return;
}
await client
.send('Runtime.releaseObject', { objectId: remoteObject.objectId })
.catch(() => {
// Exceptions might happen in case of a page been navigated or closed.
// Swallow these since they are harmless and we don't leak anything in this case.
});
}
function addEventListener(emitter, eventName, handler) {
emitter.on(eventName, handler);
return () => emitter.off(eventName, handler);
}
function removeEventListeners(listeners) {
for (const listener of listeners) {
listener();
}
listeners.length = 0;
}
const isString = (obj) => {
return typeof obj === 'string' || obj instanceof String;
};
exports.isString = isString;
function evaluationString(fun, ...args) {
if ((0, exports.isString)(fun)) {
(0, assert_1.assert)(args.length === 0, 'Cannot evaluate a string with arguments');
return fun;
}
function serializeArgument(arg) {
if (Object.is(arg, undefined)) {
return 'undefined';
}
return JSON.stringify(arg);
}
return `(${fun})(${args.map(serializeArgument).join(',')})`;
}
function pageBindingDeliverResultString(name, seq, result) {
function deliverResult(_name, _seq, _result) {
window[_name].callbacks.get(_seq).resolve(_result);
window[_name].callbacks.delete(_seq);
}
return evaluationString(deliverResult, name, seq, result);
}
function pageBindingDeliverErrorString(name, seq, message, stack) {
function deliverError(_name, _seq, _message, _stack) {
const error = new Error(_message);
error.stack = _stack;
window[_name].callbacks.get(_seq).reject(error);
window[_name].callbacks.delete(_seq);
}
return evaluationString(deliverError, name, seq, message, stack);
}
function pageBindingDeliverErrorValueString(name, seq, value) {
function deliverErrorValue(_name, _seq, _value) {
window[_name].callbacks.get(_seq).reject(_value);
window[_name].callbacks.delete(_seq);
}
return evaluationString(deliverErrorValue, name, seq, value);
}
async function waitWithTimeout(promise, taskName, timeout, browser) {
let reject;
const timeoutError = new Errors_1.TimeoutError(`waiting for ${taskName} failed: timeout ${timeout}ms exceeded`);
const timeoutPromise = new Promise((_res, rej) => {
reject = rej;
});
let timeoutTimer = null;
if (timeout) {
timeoutTimer = setTimeout(() => {
return reject(timeoutError);
}, timeout);
}
try {
return await Promise.race([
new Promise((_, rej) => {
browser.once("closed" /* BrowserEmittedEvents.Closed */, () => {
return rej();
});
}),
promise,
timeoutPromise,
]);
}
finally {
if (timeoutTimer) {
clearTimeout(timeoutTimer);
}
}
}
function isErrorLike(obj) {
return (typeof obj === 'object' && obj !== null && 'name' in obj && 'message' in obj);
}
function isErrnoException(obj) {
return (isErrorLike(obj) &&
('errno' in obj || 'code' in obj || 'path' in obj || 'syscall' in obj));
}