Node updated. Some todos.
This commit is contained in:
185
Scripts/node_modules/@puppeteer/browsers/src/CLI.ts
generated
vendored
185
Scripts/node_modules/@puppeteer/browsers/src/CLI.ts
generated
vendored
@ -27,14 +27,16 @@ import {
|
||||
launch,
|
||||
} from './launch.js';
|
||||
|
||||
interface InstallBrowser {
|
||||
name: Browser;
|
||||
buildId: string;
|
||||
}
|
||||
interface InstallArgs {
|
||||
browser: {
|
||||
name: Browser;
|
||||
buildId: string;
|
||||
};
|
||||
browser?: InstallBrowser;
|
||||
path?: string;
|
||||
platform?: BrowserPlatform;
|
||||
baseUrl?: string;
|
||||
installDeps?: boolean;
|
||||
}
|
||||
|
||||
interface LaunchArgs {
|
||||
@ -60,7 +62,12 @@ export class CLI {
|
||||
#rl?: readline.Interface;
|
||||
#scriptName = '';
|
||||
#allowCachePathOverride = true;
|
||||
#pinnedBrowsers?: Partial<{[key in Browser]: string}>;
|
||||
#pinnedBrowsers?: Partial<{
|
||||
[key in Browser]: {
|
||||
buildId: string;
|
||||
skipDownload: boolean;
|
||||
};
|
||||
}>;
|
||||
#prefixCommand?: {cmd: string; description: string};
|
||||
|
||||
constructor(
|
||||
@ -71,7 +78,12 @@ export class CLI {
|
||||
scriptName?: string;
|
||||
prefixCommand?: {cmd: string; description: string};
|
||||
allowCachePathOverride?: boolean;
|
||||
pinnedBrowsers?: Partial<{[key in Browser]: string}>;
|
||||
pinnedBrowsers?: Partial<{
|
||||
[key in Browser]: {
|
||||
buildId: string;
|
||||
skipDownload: boolean;
|
||||
};
|
||||
}>;
|
||||
},
|
||||
rl?: readline.Interface
|
||||
) {
|
||||
@ -152,9 +164,11 @@ export class CLI {
|
||||
|
||||
#build(yargs: Yargs.Argv<unknown>): Yargs.Argv<unknown> {
|
||||
const latestOrPinned = this.#pinnedBrowsers ? 'pinned' : 'latest';
|
||||
// If there are pinned browsers allow the positional arg to be optional
|
||||
const browserArgType = this.#pinnedBrowsers ? '[browser]' : '<browser>';
|
||||
return yargs
|
||||
.command(
|
||||
'install <browser>',
|
||||
`install ${browserArgType}`,
|
||||
'Download and install the specified browser. If successful, the command outputs the actual browser buildId that was installed and the absolute path to the browser executable (format: <browser>@<buildID> <path>).',
|
||||
yargs => {
|
||||
this.#defineBrowserParameter(yargs);
|
||||
@ -164,6 +178,14 @@ export class CLI {
|
||||
type: 'string',
|
||||
desc: 'Base URL to download from',
|
||||
});
|
||||
if (this.#pinnedBrowsers) {
|
||||
yargs.example('$0 install', 'Install all pinned browsers');
|
||||
}
|
||||
yargs.option('install-deps', {
|
||||
type: 'boolean',
|
||||
desc: 'Whether to attempt installing system dependencies (only supported on Linux, requires root privileges).',
|
||||
default: false,
|
||||
});
|
||||
yargs.example(
|
||||
'$0 install chrome',
|
||||
`Install the ${latestOrPinned} available build of the Chrome browser.`
|
||||
@ -172,6 +194,18 @@ export class CLI {
|
||||
'$0 install chrome@latest',
|
||||
'Install the latest available build for the Chrome browser.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install chrome@stable',
|
||||
'Install the latest available build for the Chrome browser from the stable channel.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install chrome@beta',
|
||||
'Install the latest available build for the Chrome browser from the beta channel.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install chrome@dev',
|
||||
'Install the latest available build for the Chrome browser from the dev channel.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install chrome@canary',
|
||||
'Install the latest available build for the Chrome Canary browser.'
|
||||
@ -210,7 +244,31 @@ export class CLI {
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install firefox',
|
||||
'Install the latest available build of the Firefox browser.'
|
||||
'Install the latest nightly available build of the Firefox browser.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install firefox@stable',
|
||||
'Install the latest stable build of the Firefox browser.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install firefox@beta',
|
||||
'Install the latest beta build of the Firefox browser.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install firefox@devedition',
|
||||
'Install the latest devedition build of the Firefox browser.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install firefox@esr',
|
||||
'Install the latest ESR build of the Firefox browser.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install firefox@nightly',
|
||||
'Install the latest nightly build of the Firefox browser.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install firefox@stable_111.0.1',
|
||||
'Install a specific version of the Firefox browser.'
|
||||
);
|
||||
yargs.example(
|
||||
'$0 install firefox --platform mac',
|
||||
@ -225,45 +283,35 @@ export class CLI {
|
||||
},
|
||||
async argv => {
|
||||
const args = argv as unknown as InstallArgs;
|
||||
args.platform ??= detectBrowserPlatform();
|
||||
if (!args.platform) {
|
||||
throw new Error(`Could not resolve the current platform`);
|
||||
}
|
||||
if (args.browser.buildId === 'pinned') {
|
||||
const pinnedVersion = this.#pinnedBrowsers?.[args.browser.name];
|
||||
if (!pinnedVersion) {
|
||||
throw new Error(
|
||||
`No pinned version found for ${args.browser.name}`
|
||||
);
|
||||
if (this.#pinnedBrowsers && !args.browser) {
|
||||
// Use allSettled to avoid scenarios that
|
||||
// a browser may fail early and leave the other
|
||||
// installation in a faulty state
|
||||
const result = await Promise.allSettled(
|
||||
Object.entries(this.#pinnedBrowsers).map(
|
||||
async ([browser, options]) => {
|
||||
if (options.skipDownload) {
|
||||
return;
|
||||
}
|
||||
await this.#install({
|
||||
...argv,
|
||||
browser: {
|
||||
name: browser as Browser,
|
||||
buildId: options.buildId,
|
||||
},
|
||||
});
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
for (const install of result) {
|
||||
if (install.status === 'rejected') {
|
||||
throw install.reason;
|
||||
}
|
||||
}
|
||||
args.browser.buildId = pinnedVersion;
|
||||
} else {
|
||||
await this.#install(args);
|
||||
}
|
||||
args.browser.buildId = await resolveBuildId(
|
||||
args.browser.name,
|
||||
args.platform,
|
||||
args.browser.buildId
|
||||
);
|
||||
await install({
|
||||
browser: args.browser.name,
|
||||
buildId: args.browser.buildId,
|
||||
platform: args.platform,
|
||||
cacheDir: args.path ?? this.#cachePath,
|
||||
downloadProgressCallback: makeProgressCallback(
|
||||
args.browser.name,
|
||||
args.browser.buildId
|
||||
),
|
||||
baseUrl: args.baseUrl,
|
||||
});
|
||||
console.log(
|
||||
`${args.browser.name}@${
|
||||
args.browser.buildId
|
||||
} ${computeExecutablePath({
|
||||
browser: args.browser.name,
|
||||
buildId: args.browser.buildId,
|
||||
cacheDir: args.path ?? this.#cachePath,
|
||||
platform: args.platform,
|
||||
})}`
|
||||
);
|
||||
}
|
||||
)
|
||||
.command(
|
||||
@ -364,6 +412,51 @@ export class CLI {
|
||||
? 'pinned'
|
||||
: 'latest';
|
||||
}
|
||||
|
||||
async #install(args: InstallArgs) {
|
||||
args.platform ??= detectBrowserPlatform();
|
||||
if (!args.browser) {
|
||||
throw new Error(`No browser arg proveded`);
|
||||
}
|
||||
if (!args.platform) {
|
||||
throw new Error(`Could not resolve the current platform`);
|
||||
}
|
||||
if (args.browser.buildId === 'pinned') {
|
||||
const options = this.#pinnedBrowsers?.[args.browser.name];
|
||||
if (!options || !options.buildId) {
|
||||
throw new Error(`No pinned version found for ${args.browser.name}`);
|
||||
}
|
||||
args.browser.buildId = options.buildId;
|
||||
}
|
||||
const originalBuildId = args.browser.buildId;
|
||||
args.browser.buildId = await resolveBuildId(
|
||||
args.browser.name,
|
||||
args.platform,
|
||||
args.browser.buildId
|
||||
);
|
||||
await install({
|
||||
browser: args.browser.name,
|
||||
buildId: args.browser.buildId,
|
||||
platform: args.platform,
|
||||
cacheDir: args.path ?? this.#cachePath,
|
||||
downloadProgressCallback: makeProgressCallback(
|
||||
args.browser.name,
|
||||
args.browser.buildId
|
||||
),
|
||||
baseUrl: args.baseUrl,
|
||||
buildIdAlias:
|
||||
originalBuildId !== args.browser.buildId ? originalBuildId : undefined,
|
||||
installDeps: args.installDeps,
|
||||
});
|
||||
console.log(
|
||||
`${args.browser.name}@${args.browser.buildId} ${computeExecutablePath({
|
||||
browser: args.browser.name,
|
||||
buildId: args.browser.buildId,
|
||||
cacheDir: args.path ?? this.#cachePath,
|
||||
platform: args.platform,
|
||||
})}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -378,7 +471,7 @@ export function makeProgressCallback(
|
||||
return (downloadedBytes: number, totalBytes: number) => {
|
||||
if (!progressBar) {
|
||||
progressBar = new ProgressBar(
|
||||
`Downloading ${browser} r${buildId} - ${toMegabytes(
|
||||
`Downloading ${browser} ${buildId} - ${toMegabytes(
|
||||
totalBytes
|
||||
)} [:bar] :percent :etas `,
|
||||
{
|
||||
|
||||
63
Scripts/node_modules/@puppeteer/browsers/src/Cache.ts
generated
vendored
63
Scripts/node_modules/@puppeteer/browsers/src/Cache.ts
generated
vendored
@ -8,13 +8,18 @@ import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import debug from 'debug';
|
||||
|
||||
import {
|
||||
Browser,
|
||||
type BrowserPlatform,
|
||||
executablePathByBrowser,
|
||||
getVersionComparator,
|
||||
} from './browser-data/browser-data.js';
|
||||
import {detectBrowserPlatform} from './detectPlatform.js';
|
||||
|
||||
const debugCache = debug('puppeteer:browsers:cache');
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -57,6 +62,14 @@ export class InstalledBrowser {
|
||||
this.buildId
|
||||
);
|
||||
}
|
||||
|
||||
readMetadata(): Metadata {
|
||||
return this.#cache.readMetadata(this.browser);
|
||||
}
|
||||
|
||||
writeMetadata(metadata: Metadata): void {
|
||||
this.#cache.writeMetadata(this.browser, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,6 +93,11 @@ export interface ComputeExecutablePathOptions {
|
||||
buildId: string;
|
||||
}
|
||||
|
||||
export interface Metadata {
|
||||
// Maps an alias (canary/latest/dev/etc.) to a buildId.
|
||||
aliases: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache used by Puppeteer relies on the following structure:
|
||||
*
|
||||
@ -112,6 +130,39 @@ export class Cache {
|
||||
return path.join(this.#rootDir, browser);
|
||||
}
|
||||
|
||||
metadataFile(browser: Browser): string {
|
||||
return path.join(this.browserRoot(browser), '.metadata');
|
||||
}
|
||||
|
||||
readMetadata(browser: Browser): Metadata {
|
||||
const metatadaPath = this.metadataFile(browser);
|
||||
if (!fs.existsSync(metatadaPath)) {
|
||||
return {aliases: {}};
|
||||
}
|
||||
// TODO: add type-safe parsing.
|
||||
const data = JSON.parse(fs.readFileSync(metatadaPath, 'utf8'));
|
||||
if (typeof data !== 'object') {
|
||||
throw new Error('.metadata is not an object');
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
writeMetadata(browser: Browser, metadata: Metadata): void {
|
||||
const metatadaPath = this.metadataFile(browser);
|
||||
fs.mkdirSync(path.dirname(metatadaPath), {recursive: true});
|
||||
fs.writeFileSync(metatadaPath, JSON.stringify(metadata, null, 2));
|
||||
}
|
||||
|
||||
resolveAlias(browser: Browser, alias: string): string | undefined {
|
||||
const metadata = this.readMetadata(browser);
|
||||
if (alias === 'latest') {
|
||||
return Object.values(metadata.aliases || {})
|
||||
.sort(getVersionComparator(browser))
|
||||
.at(-1);
|
||||
}
|
||||
return metadata.aliases[alias];
|
||||
}
|
||||
|
||||
installationDir(
|
||||
browser: Browser,
|
||||
platform: BrowserPlatform,
|
||||
@ -134,6 +185,12 @@ export class Cache {
|
||||
platform: BrowserPlatform,
|
||||
buildId: string
|
||||
): void {
|
||||
const metadata = this.readMetadata(browser);
|
||||
for (const alias of Object.keys(metadata.aliases)) {
|
||||
if (metadata.aliases[alias] === buildId) {
|
||||
delete metadata.aliases[alias];
|
||||
}
|
||||
}
|
||||
fs.rmSync(this.installationDir(browser, platform, buildId), {
|
||||
force: true,
|
||||
recursive: true,
|
||||
@ -180,6 +237,12 @@ export class Cache {
|
||||
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
|
||||
);
|
||||
}
|
||||
try {
|
||||
options.buildId =
|
||||
this.resolveAlias(options.browser, options.buildId) ?? options.buildId;
|
||||
} catch {
|
||||
debugCache('could not read .metadata file for the browser');
|
||||
}
|
||||
const installationDir = this.installationDir(
|
||||
options.browser,
|
||||
options.platform,
|
||||
|
||||
131
Scripts/node_modules/@puppeteer/browsers/src/browser-data/browser-data.ts
generated
vendored
131
Scripts/node_modules/@puppeteer/browsers/src/browser-data/browser-data.ts
generated
vendored
@ -43,31 +43,47 @@ export const executablePathByBrowser = {
|
||||
[Browser.FIREFOX]: firefox.relativeExecutablePath,
|
||||
};
|
||||
|
||||
export const versionComparators = {
|
||||
[Browser.CHROMEDRIVER]: chromedriver.compareVersions,
|
||||
[Browser.CHROMEHEADLESSSHELL]: chromeHeadlessShell.compareVersions,
|
||||
[Browser.CHROME]: chrome.compareVersions,
|
||||
[Browser.CHROMIUM]: chromium.compareVersions,
|
||||
[Browser.FIREFOX]: firefox.compareVersions,
|
||||
};
|
||||
|
||||
export {Browser, BrowserPlatform, ChromeReleaseChannel};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @internal
|
||||
*/
|
||||
export async function resolveBuildId(
|
||||
async function resolveBuildIdForBrowserTag(
|
||||
browser: Browser,
|
||||
platform: BrowserPlatform,
|
||||
tag: string
|
||||
tag: BrowserTag
|
||||
): Promise<string> {
|
||||
switch (browser) {
|
||||
case Browser.FIREFOX:
|
||||
switch (tag as BrowserTag) {
|
||||
switch (tag) {
|
||||
case BrowserTag.LATEST:
|
||||
return await firefox.resolveBuildId('FIREFOX_NIGHTLY');
|
||||
return await firefox.resolveBuildId(firefox.FirefoxChannel.NIGHTLY);
|
||||
case BrowserTag.BETA:
|
||||
return await firefox.resolveBuildId(firefox.FirefoxChannel.BETA);
|
||||
case BrowserTag.NIGHTLY:
|
||||
return await firefox.resolveBuildId(firefox.FirefoxChannel.NIGHTLY);
|
||||
case BrowserTag.DEVEDITION:
|
||||
return await firefox.resolveBuildId(
|
||||
firefox.FirefoxChannel.DEVEDITION
|
||||
);
|
||||
case BrowserTag.STABLE:
|
||||
return await firefox.resolveBuildId(firefox.FirefoxChannel.STABLE);
|
||||
case BrowserTag.ESR:
|
||||
return await firefox.resolveBuildId(firefox.FirefoxChannel.ESR);
|
||||
case BrowserTag.CANARY:
|
||||
case BrowserTag.DEV:
|
||||
case BrowserTag.STABLE:
|
||||
throw new Error(
|
||||
`${tag} is not supported for ${browser}. Use 'latest' instead.`
|
||||
);
|
||||
throw new Error(`${tag.toUpperCase()} is not available for Firefox`);
|
||||
}
|
||||
case Browser.CHROME: {
|
||||
switch (tag as BrowserTag) {
|
||||
switch (tag) {
|
||||
case BrowserTag.LATEST:
|
||||
return await chrome.resolveBuildId(ChromeReleaseChannel.CANARY);
|
||||
case BrowserTag.BETA:
|
||||
@ -78,13 +94,11 @@ export async function resolveBuildId(
|
||||
return await chrome.resolveBuildId(ChromeReleaseChannel.DEV);
|
||||
case BrowserTag.STABLE:
|
||||
return await chrome.resolveBuildId(ChromeReleaseChannel.STABLE);
|
||||
default:
|
||||
const result = await chrome.resolveBuildId(tag);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
case BrowserTag.NIGHTLY:
|
||||
case BrowserTag.DEVEDITION:
|
||||
case BrowserTag.ESR:
|
||||
throw new Error(`${tag.toUpperCase()} is not available for Chrome`);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
case Browser.CHROMEDRIVER: {
|
||||
switch (tag) {
|
||||
@ -97,13 +111,13 @@ export async function resolveBuildId(
|
||||
return await chromedriver.resolveBuildId(ChromeReleaseChannel.DEV);
|
||||
case BrowserTag.STABLE:
|
||||
return await chromedriver.resolveBuildId(ChromeReleaseChannel.STABLE);
|
||||
default:
|
||||
const result = await chromedriver.resolveBuildId(tag);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
case BrowserTag.NIGHTLY:
|
||||
case BrowserTag.DEVEDITION:
|
||||
case BrowserTag.ESR:
|
||||
throw new Error(
|
||||
`${tag.toUpperCase()} is not available for ChromeDriver`
|
||||
);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
case Browser.CHROMEHEADLESSSHELL: {
|
||||
switch (tag) {
|
||||
@ -124,29 +138,68 @@ export async function resolveBuildId(
|
||||
return await chromeHeadlessShell.resolveBuildId(
|
||||
ChromeReleaseChannel.STABLE
|
||||
);
|
||||
default:
|
||||
const result = await chromeHeadlessShell.resolveBuildId(tag);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
case BrowserTag.NIGHTLY:
|
||||
case BrowserTag.DEVEDITION:
|
||||
case BrowserTag.ESR:
|
||||
throw new Error(`${tag} is not available for chrome-headless-shell`);
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
case Browser.CHROMIUM:
|
||||
switch (tag as BrowserTag) {
|
||||
switch (tag) {
|
||||
case BrowserTag.LATEST:
|
||||
return await chromium.resolveBuildId(platform);
|
||||
case BrowserTag.BETA:
|
||||
case BrowserTag.NIGHTLY:
|
||||
case BrowserTag.CANARY:
|
||||
case BrowserTag.DEV:
|
||||
case BrowserTag.DEVEDITION:
|
||||
case BrowserTag.BETA:
|
||||
case BrowserTag.STABLE:
|
||||
case BrowserTag.ESR:
|
||||
throw new Error(
|
||||
`${tag} is not supported for ${browser}. Use 'latest' instead.`
|
||||
`${tag} is not supported for Chromium. Use 'latest' instead.`
|
||||
);
|
||||
}
|
||||
}
|
||||
// We assume the tag is the buildId if it didn't match any keywords.
|
||||
return tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function resolveBuildId(
|
||||
browser: Browser,
|
||||
platform: BrowserPlatform,
|
||||
tag: string
|
||||
): Promise<string> {
|
||||
const browserTag = tag as BrowserTag;
|
||||
if (Object.values(BrowserTag).includes(browserTag)) {
|
||||
return await resolveBuildIdForBrowserTag(browser, platform, browserTag);
|
||||
}
|
||||
|
||||
switch (browser) {
|
||||
case Browser.FIREFOX:
|
||||
return tag;
|
||||
case Browser.CHROME:
|
||||
const chromeResult = await chrome.resolveBuildId(tag);
|
||||
if (chromeResult) {
|
||||
return chromeResult;
|
||||
}
|
||||
return tag;
|
||||
case Browser.CHROMEDRIVER:
|
||||
const chromeDriverResult = await chromedriver.resolveBuildId(tag);
|
||||
if (chromeDriverResult) {
|
||||
return chromeDriverResult;
|
||||
}
|
||||
return tag;
|
||||
case Browser.CHROMEHEADLESSSHELL:
|
||||
const chromeHeadlessShellResult =
|
||||
await chromeHeadlessShell.resolveBuildId(tag);
|
||||
if (chromeHeadlessShellResult) {
|
||||
return chromeHeadlessShellResult;
|
||||
}
|
||||
return tag;
|
||||
case Browser.CHROMIUM:
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,3 +238,15 @@ export function resolveSystemExecutablePath(
|
||||
return chrome.resolveSystemExecutablePath(platform, channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version comparator for the given browser that can be used to sort
|
||||
* browser versions.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function getVersionComparator(
|
||||
browser: Browser
|
||||
): (a: string, b: string) => number {
|
||||
return versionComparators[browser];
|
||||
}
|
||||
|
||||
4
Scripts/node_modules/@puppeteer/browsers/src/browser-data/chrome-headless-shell.ts
generated
vendored
4
Scripts/node_modules/@puppeteer/browsers/src/browser-data/chrome-headless-shell.ts
generated
vendored
@ -25,7 +25,7 @@ function folder(platform: BrowserPlatform): string {
|
||||
export function resolveDownloadUrl(
|
||||
platform: BrowserPlatform,
|
||||
buildId: string,
|
||||
baseUrl = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'
|
||||
baseUrl = 'https://storage.googleapis.com/chrome-for-testing-public'
|
||||
): string {
|
||||
return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`;
|
||||
}
|
||||
@ -66,4 +66,4 @@ export function relativeExecutablePath(
|
||||
}
|
||||
}
|
||||
|
||||
export {resolveBuildId} from './chrome.js';
|
||||
export {resolveBuildId, compareVersions} from './chrome.js';
|
||||
|
||||
20
Scripts/node_modules/@puppeteer/browsers/src/browser-data/chrome.ts
generated
vendored
20
Scripts/node_modules/@puppeteer/browsers/src/browser-data/chrome.ts
generated
vendored
@ -6,6 +6,8 @@
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import semver from 'semver';
|
||||
|
||||
import {getJSON} from '../httpUtil.js';
|
||||
|
||||
import {BrowserPlatform, ChromeReleaseChannel} from './types.js';
|
||||
@ -28,7 +30,7 @@ function folder(platform: BrowserPlatform): string {
|
||||
export function resolveDownloadUrl(
|
||||
platform: BrowserPlatform,
|
||||
buildId: string,
|
||||
baseUrl = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'
|
||||
baseUrl = 'https://storage.googleapis.com/chrome-for-testing-public'
|
||||
): string {
|
||||
return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`;
|
||||
}
|
||||
@ -193,3 +195,19 @@ export function resolveSystemExecutablePath(
|
||||
`Unable to detect browser executable path for '${channel}' on ${platform}.`
|
||||
);
|
||||
}
|
||||
|
||||
export function compareVersions(a: string, b: string): number {
|
||||
if (!semver.valid(a)) {
|
||||
throw new Error(`Version ${a} is not a valid semver version`);
|
||||
}
|
||||
if (!semver.valid(b)) {
|
||||
throw new Error(`Version ${b} is not a valid semver version`);
|
||||
}
|
||||
if (semver.gt(a, b)) {
|
||||
return 1;
|
||||
} else if (semver.lt(a, b)) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
4
Scripts/node_modules/@puppeteer/browsers/src/browser-data/chromedriver.ts
generated
vendored
4
Scripts/node_modules/@puppeteer/browsers/src/browser-data/chromedriver.ts
generated
vendored
@ -25,7 +25,7 @@ function folder(platform: BrowserPlatform): string {
|
||||
export function resolveDownloadUrl(
|
||||
platform: BrowserPlatform,
|
||||
buildId: string,
|
||||
baseUrl = 'https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing'
|
||||
baseUrl = 'https://storage.googleapis.com/chrome-for-testing-public'
|
||||
): string {
|
||||
return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`;
|
||||
}
|
||||
@ -53,4 +53,4 @@ export function relativeExecutablePath(
|
||||
}
|
||||
}
|
||||
|
||||
export {resolveBuildId} from './chrome.js';
|
||||
export {resolveBuildId, compareVersions} from './chrome.js';
|
||||
|
||||
4
Scripts/node_modules/@puppeteer/browsers/src/browser-data/chromium.ts
generated
vendored
4
Scripts/node_modules/@puppeteer/browsers/src/browser-data/chromium.ts
generated
vendored
@ -86,3 +86,7 @@ export async function resolveBuildId(
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function compareVersions(a: string, b: string): number {
|
||||
return Number(a) - Number(b);
|
||||
}
|
||||
|
||||
185
Scripts/node_modules/@puppeteer/browsers/src/browser-data/firefox.ts
generated
vendored
185
Scripts/node_modules/@puppeteer/browsers/src/browser-data/firefox.ts
generated
vendored
@ -11,7 +11,7 @@ import {getJSON} from '../httpUtil.js';
|
||||
|
||||
import {BrowserPlatform, type ProfileOptions} from './types.js';
|
||||
|
||||
function archive(platform: BrowserPlatform, buildId: string): string {
|
||||
function archiveNightly(platform: BrowserPlatform, buildId: string): string {
|
||||
switch (platform) {
|
||||
case BrowserPlatform.LINUX:
|
||||
return `firefox-${buildId}.en-US.${platform}-x86_64.tar.bz2`;
|
||||
@ -24,11 +24,63 @@ function archive(platform: BrowserPlatform, buildId: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
function archive(platform: BrowserPlatform, buildId: string): string {
|
||||
switch (platform) {
|
||||
case BrowserPlatform.LINUX:
|
||||
return `firefox-${buildId}.tar.bz2`;
|
||||
case BrowserPlatform.MAC_ARM:
|
||||
case BrowserPlatform.MAC:
|
||||
return `Firefox ${buildId}.dmg`;
|
||||
case BrowserPlatform.WIN32:
|
||||
case BrowserPlatform.WIN64:
|
||||
return `Firefox Setup ${buildId}.exe`;
|
||||
}
|
||||
}
|
||||
|
||||
function platformName(platform: BrowserPlatform): string {
|
||||
switch (platform) {
|
||||
case BrowserPlatform.LINUX:
|
||||
return `linux-x86_64`;
|
||||
case BrowserPlatform.MAC_ARM:
|
||||
case BrowserPlatform.MAC:
|
||||
return `mac`;
|
||||
case BrowserPlatform.WIN32:
|
||||
case BrowserPlatform.WIN64:
|
||||
return platform;
|
||||
}
|
||||
}
|
||||
|
||||
function parseBuildId(buildId: string): [FirefoxChannel, string] {
|
||||
for (const value of Object.values(FirefoxChannel)) {
|
||||
if (buildId.startsWith(value + '_')) {
|
||||
buildId = buildId.substring(value.length + 1);
|
||||
return [value, buildId];
|
||||
}
|
||||
}
|
||||
// Older versions do not have channel as the prefix.«
|
||||
return [FirefoxChannel.NIGHTLY, buildId];
|
||||
}
|
||||
|
||||
export function resolveDownloadUrl(
|
||||
platform: BrowserPlatform,
|
||||
buildId: string,
|
||||
baseUrl = 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central'
|
||||
baseUrl?: string
|
||||
): string {
|
||||
const [channel] = parseBuildId(buildId);
|
||||
switch (channel) {
|
||||
case FirefoxChannel.NIGHTLY:
|
||||
baseUrl ??=
|
||||
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central';
|
||||
break;
|
||||
case FirefoxChannel.DEVEDITION:
|
||||
baseUrl ??= 'https://archive.mozilla.org/pub/devedition/releases';
|
||||
break;
|
||||
case FirefoxChannel.BETA:
|
||||
case FirefoxChannel.STABLE:
|
||||
case FirefoxChannel.ESR:
|
||||
baseUrl ??= 'https://archive.mozilla.org/pub/firefox/releases';
|
||||
break;
|
||||
}
|
||||
return `${baseUrl}/${resolveDownloadPath(platform, buildId).join('/')}`;
|
||||
}
|
||||
|
||||
@ -36,36 +88,88 @@ export function resolveDownloadPath(
|
||||
platform: BrowserPlatform,
|
||||
buildId: string
|
||||
): string[] {
|
||||
return [archive(platform, buildId)];
|
||||
const [channel, resolvedBuildId] = parseBuildId(buildId);
|
||||
switch (channel) {
|
||||
case FirefoxChannel.NIGHTLY:
|
||||
return [archiveNightly(platform, resolvedBuildId)];
|
||||
case FirefoxChannel.DEVEDITION:
|
||||
case FirefoxChannel.BETA:
|
||||
case FirefoxChannel.STABLE:
|
||||
case FirefoxChannel.ESR:
|
||||
return [
|
||||
resolvedBuildId,
|
||||
platformName(platform),
|
||||
'en-US',
|
||||
archive(platform, resolvedBuildId),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export function relativeExecutablePath(
|
||||
platform: BrowserPlatform,
|
||||
_buildId: string
|
||||
buildId: string
|
||||
): string {
|
||||
switch (platform) {
|
||||
case BrowserPlatform.MAC_ARM:
|
||||
case BrowserPlatform.MAC:
|
||||
return path.join('Firefox Nightly.app', 'Contents', 'MacOS', 'firefox');
|
||||
case BrowserPlatform.LINUX:
|
||||
return path.join('firefox', 'firefox');
|
||||
case BrowserPlatform.WIN32:
|
||||
case BrowserPlatform.WIN64:
|
||||
return path.join('firefox', 'firefox.exe');
|
||||
const [channel] = parseBuildId(buildId);
|
||||
switch (channel) {
|
||||
case FirefoxChannel.NIGHTLY:
|
||||
switch (platform) {
|
||||
case BrowserPlatform.MAC_ARM:
|
||||
case BrowserPlatform.MAC:
|
||||
return path.join(
|
||||
'Firefox Nightly.app',
|
||||
'Contents',
|
||||
'MacOS',
|
||||
'firefox'
|
||||
);
|
||||
case BrowserPlatform.LINUX:
|
||||
return path.join('firefox', 'firefox');
|
||||
case BrowserPlatform.WIN32:
|
||||
case BrowserPlatform.WIN64:
|
||||
return path.join('firefox', 'firefox.exe');
|
||||
}
|
||||
case FirefoxChannel.BETA:
|
||||
case FirefoxChannel.DEVEDITION:
|
||||
case FirefoxChannel.ESR:
|
||||
case FirefoxChannel.STABLE:
|
||||
switch (platform) {
|
||||
case BrowserPlatform.MAC_ARM:
|
||||
case BrowserPlatform.MAC:
|
||||
return path.join('Firefox.app', 'Contents', 'MacOS', 'firefox');
|
||||
case BrowserPlatform.LINUX:
|
||||
return path.join('firefox', 'firefox');
|
||||
case BrowserPlatform.WIN32:
|
||||
case BrowserPlatform.WIN64:
|
||||
return path.join('core', 'firefox.exe');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum FirefoxChannel {
|
||||
STABLE = 'stable',
|
||||
ESR = 'esr',
|
||||
DEVEDITION = 'devedition',
|
||||
BETA = 'beta',
|
||||
NIGHTLY = 'nightly',
|
||||
}
|
||||
|
||||
export async function resolveBuildId(
|
||||
channel: 'FIREFOX_NIGHTLY' = 'FIREFOX_NIGHTLY'
|
||||
channel: FirefoxChannel = FirefoxChannel.NIGHTLY
|
||||
): Promise<string> {
|
||||
const channelToVersionKey = {
|
||||
[FirefoxChannel.ESR]: 'FIREFOX_ESR',
|
||||
[FirefoxChannel.STABLE]: 'LATEST_FIREFOX_VERSION',
|
||||
[FirefoxChannel.DEVEDITION]: 'FIREFOX_DEVEDITION',
|
||||
[FirefoxChannel.BETA]: 'FIREFOX_DEVEDITION',
|
||||
[FirefoxChannel.NIGHTLY]: 'FIREFOX_NIGHTLY',
|
||||
};
|
||||
const versions = (await getJSON(
|
||||
new URL('https://product-details.mozilla.org/1.0/firefox_versions.json')
|
||||
)) as Record<string, string>;
|
||||
const version = versions[channel];
|
||||
const version = versions[channelToVersionKey[channel]];
|
||||
if (!version) {
|
||||
throw new Error(`Channel ${channel} is not found.`);
|
||||
}
|
||||
return version;
|
||||
return channel + '_' + version;
|
||||
}
|
||||
|
||||
export async function createProfile(options: ProfileOptions): Promise<void> {
|
||||
@ -74,7 +178,7 @@ export async function createProfile(options: ProfileOptions): Promise<void> {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
await writePreferences({
|
||||
await syncPreferences({
|
||||
preferences: {
|
||||
...defaultProfilePreferences(options.preferences),
|
||||
...options.preferences,
|
||||
@ -235,10 +339,6 @@ function defaultProfilePreferences(
|
||||
// Disable the GFX sanity window
|
||||
'media.sanity-test.disabled': true,
|
||||
|
||||
// Prevent various error message on the console
|
||||
// jest-puppeteer asserts that no error message is emitted by the console
|
||||
'network.cookie.cookieBehavior': 0,
|
||||
|
||||
// Disable experimental feature that is only available in Nightly
|
||||
'network.cookie.sameSite.laxByDefault': false,
|
||||
|
||||
@ -301,30 +401,43 @@ function defaultProfilePreferences(
|
||||
return Object.assign(defaultPrefs, extraPrefs);
|
||||
}
|
||||
|
||||
async function backupFile(input: string): Promise<void> {
|
||||
if (!fs.existsSync(input)) {
|
||||
return;
|
||||
}
|
||||
await fs.promises.copyFile(input, input + '.puppeteer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the user.js file with custom preferences as needed to allow
|
||||
* Firefox's CDP support to properly function. These preferences will be
|
||||
* Firefox's support to properly function. These preferences will be
|
||||
* automatically copied over to prefs.js during startup of Firefox. To be
|
||||
* able to restore the original values of preferences a backup of prefs.js
|
||||
* will be created.
|
||||
*
|
||||
* @param prefs - List of preferences to add.
|
||||
* @param profilePath - Firefox profile to write the preferences to.
|
||||
*/
|
||||
async function writePreferences(options: ProfileOptions): Promise<void> {
|
||||
async function syncPreferences(options: ProfileOptions): Promise<void> {
|
||||
const prefsPath = path.join(options.path, 'prefs.js');
|
||||
const userPath = path.join(options.path, 'user.js');
|
||||
|
||||
const lines = Object.entries(options.preferences).map(([key, value]) => {
|
||||
return `user_pref(${JSON.stringify(key)}, ${JSON.stringify(value)});`;
|
||||
});
|
||||
|
||||
await fs.promises.writeFile(
|
||||
path.join(options.path, 'user.js'),
|
||||
lines.join('\n')
|
||||
);
|
||||
|
||||
// Create a backup of the preferences file if it already exitsts.
|
||||
const prefsPath = path.join(options.path, 'prefs.js');
|
||||
if (fs.existsSync(prefsPath)) {
|
||||
const prefsBackupPath = path.join(options.path, 'prefs.js.puppeteer');
|
||||
await fs.promises.copyFile(prefsPath, prefsBackupPath);
|
||||
// Use allSettled to prevent corruption.
|
||||
const result = await Promise.allSettled([
|
||||
backupFile(userPath).then(async () => {
|
||||
await fs.promises.writeFile(userPath, lines.join('\n'));
|
||||
}),
|
||||
backupFile(prefsPath),
|
||||
]);
|
||||
for (const command of result) {
|
||||
if (command.status === 'rejected') {
|
||||
throw command.reason;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function compareVersions(a: string, b: string): number {
|
||||
// TODO: this is a not very reliable check.
|
||||
return parseInt(a.replace('.', ''), 16) - parseInt(b.replace('.', ''), 16);
|
||||
}
|
||||
|
||||
3
Scripts/node_modules/@puppeteer/browsers/src/browser-data/types.ts
generated
vendored
3
Scripts/node_modules/@puppeteer/browsers/src/browser-data/types.ts
generated
vendored
@ -36,9 +36,12 @@ export enum BrowserPlatform {
|
||||
*/
|
||||
export enum BrowserTag {
|
||||
CANARY = 'canary',
|
||||
NIGHTLY = 'nightly',
|
||||
BETA = 'beta',
|
||||
DEV = 'dev',
|
||||
DEVEDITION = 'devedition',
|
||||
STABLE = 'stable',
|
||||
ESR = 'esr',
|
||||
LATEST = 'latest',
|
||||
}
|
||||
|
||||
|
||||
32
Scripts/node_modules/@puppeteer/browsers/src/fileUtil.ts
generated
vendored
32
Scripts/node_modules/@puppeteer/browsers/src/fileUtil.ts
generated
vendored
@ -4,18 +4,15 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {exec as execChildProcess} from 'child_process';
|
||||
import {spawnSync} from 'child_process';
|
||||
import {createReadStream} from 'fs';
|
||||
import {mkdir, readdir} from 'fs/promises';
|
||||
import * as path from 'path';
|
||||
import {promisify} from 'util';
|
||||
|
||||
import extractZip from 'extract-zip';
|
||||
import tar from 'tar-fs';
|
||||
import bzip from 'unbzip2-stream';
|
||||
|
||||
const exec = promisify(execChildProcess);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -30,6 +27,18 @@ export async function unpackArchive(
|
||||
} else if (archivePath.endsWith('.dmg')) {
|
||||
await mkdir(folderPath);
|
||||
await installDMG(archivePath, folderPath);
|
||||
} else if (archivePath.endsWith('.exe')) {
|
||||
// Firefox on Windows.
|
||||
const result = spawnSync(archivePath, [`/ExtractDir=${folderPath}`], {
|
||||
env: {
|
||||
__compat_layer: 'RunAsInvoker',
|
||||
},
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
`Failed to extract ${archivePath} to ${folderPath}: ${result.output}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unsupported archive format: ${archivePath}`);
|
||||
}
|
||||
@ -52,11 +61,14 @@ function extractTar(tarPath: string, folderPath: string): Promise<void> {
|
||||
* @internal
|
||||
*/
|
||||
async function installDMG(dmgPath: string, folderPath: string): Promise<void> {
|
||||
const {stdout} = await exec(
|
||||
`hdiutil attach -nobrowse -noautoopen "${dmgPath}"`
|
||||
);
|
||||
const {stdout} = spawnSync(`hdiutil`, [
|
||||
'attach',
|
||||
'-nobrowse',
|
||||
'-noautoopen',
|
||||
dmgPath,
|
||||
]);
|
||||
|
||||
const volumes = stdout.match(/\/Volumes\/(.*)/m);
|
||||
const volumes = stdout.toString('utf8').match(/\/Volumes\/(.*)/m);
|
||||
if (!volumes) {
|
||||
throw new Error(`Could not find volume path in ${stdout}`);
|
||||
}
|
||||
@ -72,8 +84,8 @@ async function installDMG(dmgPath: string, folderPath: string): Promise<void> {
|
||||
}
|
||||
const mountedPath = path.join(mountPath!, appName);
|
||||
|
||||
await exec(`cp -R "${mountedPath}" "${folderPath}"`);
|
||||
spawnSync('cp', ['-R', mountedPath, folderPath]);
|
||||
} finally {
|
||||
await exec(`hdiutil detach "${mountPath}" -quiet`);
|
||||
spawnSync('hdiutil', ['detach', mountPath, '-quiet']);
|
||||
}
|
||||
}
|
||||
|
||||
3
Scripts/node_modules/@puppeteer/browsers/src/httpUtil.ts
generated
vendored
3
Scripts/node_modules/@puppeteer/browsers/src/httpUtil.ts
generated
vendored
@ -54,6 +54,9 @@ export function httpRequest(
|
||||
res.headers.location
|
||||
) {
|
||||
httpRequest(new URL(res.headers.location), method, response);
|
||||
// consume response data to free up memory
|
||||
// And prevents the connection from being kept alive
|
||||
res.resume();
|
||||
} else {
|
||||
response(res);
|
||||
}
|
||||
|
||||
232
Scripts/node_modules/@puppeteer/browsers/src/install.ts
generated
vendored
232
Scripts/node_modules/@puppeteer/browsers/src/install.ts
generated
vendored
@ -5,21 +5,22 @@
|
||||
*/
|
||||
|
||||
import assert from 'assert';
|
||||
import {existsSync} from 'fs';
|
||||
import {spawnSync} from 'child_process';
|
||||
import {existsSync, readFileSync} from 'fs';
|
||||
import {mkdir, unlink} from 'fs/promises';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import {
|
||||
type Browser,
|
||||
type BrowserPlatform,
|
||||
Browser,
|
||||
BrowserPlatform,
|
||||
downloadUrls,
|
||||
} from './browser-data/browser-data.js';
|
||||
import {Cache, InstalledBrowser} from './Cache.js';
|
||||
import {debug} from './debug.js';
|
||||
import {detectBrowserPlatform} from './detectPlatform.js';
|
||||
import {unpackArchive} from './fileUtil.js';
|
||||
import {downloadFile, headHttpRequest} from './httpUtil.js';
|
||||
import {downloadFile, getJSON, headHttpRequest} from './httpUtil.js';
|
||||
|
||||
const debugInstall = debug('puppeteer:browsers:install');
|
||||
|
||||
@ -62,6 +63,13 @@ export interface InstallOptions {
|
||||
* binaries and they are used for caching.
|
||||
*/
|
||||
buildId: string;
|
||||
/**
|
||||
* An alias for the provided `buildId`. It will be used to maintain local
|
||||
* metadata to support aliases in the `launch` command.
|
||||
*
|
||||
* @example 'canary'
|
||||
*/
|
||||
buildIdAlias?: string;
|
||||
/**
|
||||
* Provides information about the progress of the download.
|
||||
*/
|
||||
@ -74,7 +82,7 @@ export interface InstallOptions {
|
||||
*
|
||||
* @defaultValue Either
|
||||
*
|
||||
* - https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing or
|
||||
* - https://storage.googleapis.com/chrome-for-testing-public or
|
||||
* - https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central
|
||||
*
|
||||
*/
|
||||
@ -85,15 +93,41 @@ export interface InstallOptions {
|
||||
* @defaultValue `true`
|
||||
*/
|
||||
unpack?: boolean;
|
||||
/**
|
||||
* @internal
|
||||
* @defaultValue `false`
|
||||
*/
|
||||
forceFallbackForTesting?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to attempt to install system-level dependencies required
|
||||
* for the browser.
|
||||
*
|
||||
* Only supported for Chrome on Debian or Ubuntu.
|
||||
* Requires system-level privileges to run `apt-get`.
|
||||
*
|
||||
* @defaultValue `false`
|
||||
*/
|
||||
installDeps?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads and unpacks the browser archive according to the
|
||||
* {@link InstallOptions}.
|
||||
*
|
||||
* @returns a {@link InstalledBrowser} instance.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function install(
|
||||
options: InstallOptions & {unpack?: true}
|
||||
): Promise<InstalledBrowser>;
|
||||
/**
|
||||
* Downloads the browser archive according to the {@link InstallOptions} without
|
||||
* unpacking.
|
||||
*
|
||||
* @returns the absolute path to the archive.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function install(
|
||||
@ -115,6 +149,113 @@ export async function install(
|
||||
options.buildId,
|
||||
options.baseUrl
|
||||
);
|
||||
try {
|
||||
return await installUrl(url, options);
|
||||
} catch (err) {
|
||||
// If custom baseUrl is provided, do not fall back to CfT dashboard.
|
||||
if (options.baseUrl && !options.forceFallbackForTesting) {
|
||||
throw err;
|
||||
}
|
||||
debugInstall(`Error downloading from ${url}.`);
|
||||
switch (options.browser) {
|
||||
case Browser.CHROME:
|
||||
case Browser.CHROMEDRIVER:
|
||||
case Browser.CHROMEHEADLESSSHELL: {
|
||||
debugInstall(
|
||||
`Trying to find download URL via https://googlechromelabs.github.io/chrome-for-testing.`
|
||||
);
|
||||
interface Version {
|
||||
downloads: Record<string, Array<{platform: string; url: string}>>;
|
||||
}
|
||||
const version = (await getJSON(
|
||||
new URL(
|
||||
`https://googlechromelabs.github.io/chrome-for-testing/${options.buildId}.json`
|
||||
)
|
||||
)) as Version;
|
||||
let platform = '';
|
||||
switch (options.platform) {
|
||||
case BrowserPlatform.LINUX:
|
||||
platform = 'linux64';
|
||||
break;
|
||||
case BrowserPlatform.MAC_ARM:
|
||||
platform = 'mac-arm64';
|
||||
break;
|
||||
case BrowserPlatform.MAC:
|
||||
platform = 'mac-x64';
|
||||
break;
|
||||
case BrowserPlatform.WIN32:
|
||||
platform = 'win32';
|
||||
break;
|
||||
case BrowserPlatform.WIN64:
|
||||
platform = 'win64';
|
||||
break;
|
||||
}
|
||||
const url = version.downloads[options.browser]?.find(link => {
|
||||
return link['platform'] === platform;
|
||||
})?.url;
|
||||
if (url) {
|
||||
debugInstall(`Falling back to downloading from ${url}.`);
|
||||
return await installUrl(new URL(url), options);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
default:
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function installDeps(installedBrowser: InstalledBrowser) {
|
||||
if (
|
||||
process.platform !== 'linux' ||
|
||||
installedBrowser.platform !== BrowserPlatform.LINUX
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Currently, only Debian-like deps are supported.
|
||||
const depsPath = path.join(
|
||||
path.dirname(installedBrowser.executablePath),
|
||||
'deb.deps'
|
||||
);
|
||||
if (!existsSync(depsPath)) {
|
||||
debugInstall(`deb.deps file was not found at ${depsPath}`);
|
||||
return;
|
||||
}
|
||||
const data = readFileSync(depsPath, 'utf-8').split('\n').join(',');
|
||||
if (process.getuid?.() !== 0) {
|
||||
throw new Error('Installing system dependencies requires root privileges');
|
||||
}
|
||||
let result = spawnSync('apt-get', ['-v']);
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
'Failed to install system dependencies: apt-get does not seem to be available'
|
||||
);
|
||||
}
|
||||
debugInstall(`Trying to install dependencies: ${data}`);
|
||||
result = spawnSync('apt-get', [
|
||||
'satisfy',
|
||||
'-y',
|
||||
data,
|
||||
'--no-install-recommends',
|
||||
]);
|
||||
if (result.status !== 0) {
|
||||
throw new Error(
|
||||
`Failed to install system dependencies: status=${result.status},error=${result.error},stdout=${result.stdout.toString('utf8')},stderr=${result.stderr.toString('utf8')}`
|
||||
);
|
||||
}
|
||||
debugInstall(`Installed system dependencies ${data}`);
|
||||
}
|
||||
|
||||
async function installUrl(
|
||||
url: URL,
|
||||
options: InstallOptions
|
||||
): Promise<InstalledBrowser | string> {
|
||||
options.platform ??= detectBrowserPlatform();
|
||||
if (!options.platform) {
|
||||
throw new Error(
|
||||
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
|
||||
);
|
||||
}
|
||||
const fileName = url.toString().split('/').pop();
|
||||
assert(fileName, `A malformed download URL was found: ${url}.`);
|
||||
const cache = new Cache(options.cacheDir);
|
||||
@ -140,15 +281,26 @@ export async function install(
|
||||
options.platform,
|
||||
options.buildId
|
||||
);
|
||||
if (existsSync(outputPath)) {
|
||||
return new InstalledBrowser(
|
||||
cache,
|
||||
options.browser,
|
||||
options.buildId,
|
||||
options.platform
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if (existsSync(outputPath)) {
|
||||
const installedBrowser = new InstalledBrowser(
|
||||
cache,
|
||||
options.browser,
|
||||
options.buildId,
|
||||
options.platform
|
||||
);
|
||||
if (!existsSync(installedBrowser.executablePath)) {
|
||||
throw new Error(
|
||||
`The browser folder (${outputPath}) exists but the executable (${installedBrowser.executablePath}) is missing`
|
||||
);
|
||||
}
|
||||
await runSetup(installedBrowser);
|
||||
if (options.installDeps) {
|
||||
await installDeps(installedBrowser);
|
||||
}
|
||||
return installedBrowser;
|
||||
}
|
||||
debugInstall(`Downloading binary from ${url}`);
|
||||
try {
|
||||
debugTime('download');
|
||||
@ -164,17 +316,59 @@ export async function install(
|
||||
} finally {
|
||||
debugTimeEnd('extract');
|
||||
}
|
||||
|
||||
const installedBrowser = new InstalledBrowser(
|
||||
cache,
|
||||
options.browser,
|
||||
options.buildId,
|
||||
options.platform
|
||||
);
|
||||
if (options.buildIdAlias) {
|
||||
const metadata = installedBrowser.readMetadata();
|
||||
metadata.aliases[options.buildIdAlias] = options.buildId;
|
||||
installedBrowser.writeMetadata(metadata);
|
||||
}
|
||||
|
||||
await runSetup(installedBrowser);
|
||||
if (options.installDeps) {
|
||||
await installDeps(installedBrowser);
|
||||
}
|
||||
return installedBrowser;
|
||||
} finally {
|
||||
if (existsSync(archivePath)) {
|
||||
await unlink(archivePath);
|
||||
}
|
||||
}
|
||||
return new InstalledBrowser(
|
||||
cache,
|
||||
options.browser,
|
||||
options.buildId,
|
||||
options.platform
|
||||
);
|
||||
}
|
||||
|
||||
async function runSetup(installedBrowser: InstalledBrowser): Promise<void> {
|
||||
// On Windows for Chrome invoke setup.exe to configure sandboxes.
|
||||
if (
|
||||
(installedBrowser.platform === BrowserPlatform.WIN32 ||
|
||||
installedBrowser.platform === BrowserPlatform.WIN64) &&
|
||||
installedBrowser.browser === Browser.CHROME &&
|
||||
installedBrowser.platform === detectBrowserPlatform()
|
||||
) {
|
||||
try {
|
||||
debugTime('permissions');
|
||||
const browserDir = path.dirname(installedBrowser.executablePath);
|
||||
const setupExePath = path.join(browserDir, 'setup.exe');
|
||||
if (!existsSync(setupExePath)) {
|
||||
return;
|
||||
}
|
||||
spawnSync(
|
||||
path.join(browserDir, 'setup.exe'),
|
||||
[`--configure-browser-in-directory=` + browserDir],
|
||||
{
|
||||
shell: true,
|
||||
}
|
||||
);
|
||||
// TODO: Handle error here. Currently the setup.exe sometimes
|
||||
// errors although it sets the permissions correctly.
|
||||
} finally {
|
||||
debugTimeEnd('permissions');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
119
Scripts/node_modules/@puppeteer/browsers/src/launch.ts
generated
vendored
119
Scripts/node_modules/@puppeteer/browsers/src/launch.ts
generated
vendored
@ -104,19 +104,69 @@ export function computeSystemExecutablePath(options: SystemOptions): string {
|
||||
* @public
|
||||
*/
|
||||
export interface LaunchOptions {
|
||||
/**
|
||||
* Absolute path to the browser's executable.
|
||||
*/
|
||||
executablePath: string;
|
||||
/**
|
||||
* Configures stdio streams to open two additional streams for automation over
|
||||
* those streams instead of WebSocket.
|
||||
*
|
||||
* @defaultValue `false`.
|
||||
*/
|
||||
pipe?: boolean;
|
||||
/**
|
||||
* If true, forwards the browser's process stdout and stderr to the Node's
|
||||
* process stdout and stderr.
|
||||
*
|
||||
* @defaultValue `false`.
|
||||
*/
|
||||
dumpio?: boolean;
|
||||
/**
|
||||
* Additional arguments to pass to the executable when launching.
|
||||
*/
|
||||
args?: string[];
|
||||
/**
|
||||
* Environment variables to set for the browser process.
|
||||
*/
|
||||
env?: Record<string, string | undefined>;
|
||||
/**
|
||||
* Handles SIGINT in the Node process and tries to kill the browser process.
|
||||
*
|
||||
* @defaultValue `true`.
|
||||
*/
|
||||
handleSIGINT?: boolean;
|
||||
/**
|
||||
* Handles SIGTERM in the Node process and tries to gracefully close the browser
|
||||
* process.
|
||||
*
|
||||
* @defaultValue `true`.
|
||||
*/
|
||||
handleSIGTERM?: boolean;
|
||||
/**
|
||||
* Handles SIGHUP in the Node process and tries to gracefully close the browser process.
|
||||
*
|
||||
* @defaultValue `true`.
|
||||
*/
|
||||
handleSIGHUP?: boolean;
|
||||
/**
|
||||
* Whether to spawn process in the {@link https://nodejs.org/api/child_process.html#optionsdetached | detached}
|
||||
* mode.
|
||||
*
|
||||
* @defaultValue `true` except on Windows.
|
||||
*/
|
||||
detached?: boolean;
|
||||
/**
|
||||
* A callback to run after the browser process exits or before the process
|
||||
* will be closed via the {@link Process.close} call (including when handling
|
||||
* signals). The callback is only run once.
|
||||
*/
|
||||
onExit?: () => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches a browser process according to {@link LaunchOptions}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function launch(opts: LaunchOptions): Process {
|
||||
@ -135,6 +185,59 @@ export const CDP_WEBSOCKET_ENDPOINT_REGEX =
|
||||
export const WEBDRIVER_BIDI_WEBSOCKET_ENDPOINT_REGEX =
|
||||
/^WebDriver BiDi listening on (ws:\/\/.*)$/;
|
||||
|
||||
type EventHandler = (...args: any[]) => void;
|
||||
const processListeners = new Map<string, EventHandler[]>();
|
||||
const dispatchers = {
|
||||
exit: (...args: any[]) => {
|
||||
processListeners.get('exit')?.forEach(handler => {
|
||||
return handler(...args);
|
||||
});
|
||||
},
|
||||
SIGINT: (...args: any[]) => {
|
||||
processListeners.get('SIGINT')?.forEach(handler => {
|
||||
return handler(...args);
|
||||
});
|
||||
},
|
||||
SIGHUP: (...args: any[]) => {
|
||||
processListeners.get('SIGHUP')?.forEach(handler => {
|
||||
return handler(...args);
|
||||
});
|
||||
},
|
||||
SIGTERM: (...args: any[]) => {
|
||||
processListeners.get('SIGTERM')?.forEach(handler => {
|
||||
return handler(...args);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function subscribeToProcessEvent(
|
||||
event: 'exit' | 'SIGINT' | 'SIGHUP' | 'SIGTERM',
|
||||
handler: EventHandler
|
||||
): void {
|
||||
const listeners = processListeners.get(event) || [];
|
||||
if (listeners.length === 0) {
|
||||
process.on(event, dispatchers[event]);
|
||||
}
|
||||
listeners.push(handler);
|
||||
processListeners.set(event, listeners);
|
||||
}
|
||||
|
||||
function unsubscribeFromProcessEvent(
|
||||
event: 'exit' | 'SIGINT' | 'SIGHUP' | 'SIGTERM',
|
||||
handler: EventHandler
|
||||
): void {
|
||||
const listeners = processListeners.get(event) || [];
|
||||
const existingListenerIdx = listeners.indexOf(handler);
|
||||
if (existingListenerIdx === -1) {
|
||||
return;
|
||||
}
|
||||
listeners.splice(existingListenerIdx, 1);
|
||||
processListeners.set(event, listeners);
|
||||
if (listeners.length === 0) {
|
||||
process.off(event, dispatchers[event]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -201,15 +304,15 @@ export class Process {
|
||||
this.#browserProcess.stderr?.pipe(process.stderr);
|
||||
this.#browserProcess.stdout?.pipe(process.stdout);
|
||||
}
|
||||
process.on('exit', this.#onDriverProcessExit);
|
||||
subscribeToProcessEvent('exit', this.#onDriverProcessExit);
|
||||
if (opts.handleSIGINT) {
|
||||
process.on('SIGINT', this.#onDriverProcessSignal);
|
||||
subscribeToProcessEvent('SIGINT', this.#onDriverProcessSignal);
|
||||
}
|
||||
if (opts.handleSIGTERM) {
|
||||
process.on('SIGTERM', this.#onDriverProcessSignal);
|
||||
subscribeToProcessEvent('SIGTERM', this.#onDriverProcessSignal);
|
||||
}
|
||||
if (opts.handleSIGHUP) {
|
||||
process.on('SIGHUP', this.#onDriverProcessSignal);
|
||||
subscribeToProcessEvent('SIGHUP', this.#onDriverProcessSignal);
|
||||
}
|
||||
if (opts.onExit) {
|
||||
this.#onExitHook = opts.onExit;
|
||||
@ -262,10 +365,10 @@ export class Process {
|
||||
}
|
||||
|
||||
#clearListeners(): void {
|
||||
process.off('exit', this.#onDriverProcessExit);
|
||||
process.off('SIGINT', this.#onDriverProcessSignal);
|
||||
process.off('SIGTERM', this.#onDriverProcessSignal);
|
||||
process.off('SIGHUP', this.#onDriverProcessSignal);
|
||||
unsubscribeFromProcessEvent('exit', this.#onDriverProcessExit);
|
||||
unsubscribeFromProcessEvent('SIGINT', this.#onDriverProcessSignal);
|
||||
unsubscribeFromProcessEvent('SIGTERM', this.#onDriverProcessSignal);
|
||||
unsubscribeFromProcessEvent('SIGHUP', this.#onDriverProcessSignal);
|
||||
}
|
||||
|
||||
#onDriverProcessExit = (_code: number) => {
|
||||
|
||||
1
Scripts/node_modules/@puppeteer/browsers/src/main.ts
generated
vendored
1
Scripts/node_modules/@puppeteer/browsers/src/main.ts
generated
vendored
@ -37,6 +37,7 @@ export {
|
||||
BrowserPlatform,
|
||||
ChromeReleaseChannel,
|
||||
createProfile,
|
||||
getVersionComparator,
|
||||
} from './browser-data/browser-data.js';
|
||||
export {CLI, makeProgressCallback} from './CLI.js';
|
||||
export {Cache, InstalledBrowser} from './Cache.js';
|
||||
|
||||
Reference in New Issue
Block a user