@remotion/renderer
Version:
Render Remotion videos using Node.js or Bun
1,668 lines (1,630 loc) • 602 kB
JavaScript
import { createRequire } from "node:module";
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, {
get: all[name],
enumerable: true,
configurable: true,
set: (newValue) => all[name] = () => newValue
});
};
var __require = /* @__PURE__ */ createRequire(import.meta.url);
// src/index.ts
import execa2 from "execa";
// src/assets/download-file.ts
import { createWriteStream } from "node:fs";
// src/ensure-output-directory.ts
import fs from "node:fs";
import path from "node:path";
var ensureOutputDirectory = (outputLocation) => {
const dirName = path.dirname(outputLocation);
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName, {
recursive: true
});
}
};
// src/chalk/is-color-supported.ts
import * as tty from "tty";
var isColorSupported = () => {
const env = process.env || {};
const isForceDisabled = "NO_COLOR" in env;
if (isForceDisabled) {
return false;
}
const isForced = "FORCE_COLOR" in env;
if (isForced) {
return true;
}
const isWindows = process.platform === "win32";
const isCompatibleTerminal = tty?.isatty?.(1) && env.TERM && env.TERM !== "dumb";
const isCI = "CI" in env && (("GITHUB_ACTIONS" in env) || ("GITLAB_CI" in env) || ("CIRCLECI" in env));
return isWindows || isCompatibleTerminal || isCI;
};
// src/chalk/index.ts
var chalk = (() => {
const colors = {
enabled: () => isColorSupported(),
visible: true,
styles: {},
keys: {}
};
const ansi = (st) => {
const open = `\x1B[${st.codes[0]}m`;
const close = `\x1B[${st.codes[1]}m`;
const regex = new RegExp(`\\u001b\\[${st.codes[1]}m`, "g");
st.wrap = (input, newline) => {
if (input.includes(close))
input = input.replace(regex, close + open);
const output = open + input + close;
return newline ? output.replace(/\r*\n/g, `${close}$&${open}`) : output;
};
return st;
};
const wrap = (sty, input, newline) => {
return sty.wrap?.(input, newline);
};
const style = (input, stack) => {
if (input === "" || input === null || input === undefined)
return "";
if (colors.enabled() === false)
return input;
if (colors.visible === false)
return "";
let str = String(input);
const nl = str.includes(`
`);
let n = stack.length;
while (n-- > 0)
str = wrap(colors.styles[stack[n]], str, nl);
return str;
};
const define = (name, codes, type) => {
colors.styles[name] = ansi({ name, codes });
const keys = colors.keys[type] || (colors.keys[type] = []);
keys.push(name);
Reflect.defineProperty(colors, name, {
configurable: true,
enumerable: true,
set(value) {
colors.alias?.(name, value);
},
get() {
const color = (input) => style(input, color.stack);
Reflect.setPrototypeOf(color, colors);
color.stack = this.stack ? this.stack.concat(name) : [name];
return color;
}
});
};
define("reset", [0, 0], "modifier");
define("bold", [1, 22], "modifier");
define("dim", [2, 22], "modifier");
define("italic", [3, 23], "modifier");
define("underline", [4, 24], "modifier");
define("inverse", [7, 27], "modifier");
define("hidden", [8, 28], "modifier");
define("strikethrough", [9, 29], "modifier");
define("black", [30, 39], "color");
define("red", [31, 39], "color");
define("green", [32, 39], "color");
define("yellow", [33, 39], "color");
define("blue", [34, 39], "color");
define("magenta", [35, 39], "color");
define("cyan", [36, 39], "color");
define("white", [37, 39], "color");
define("gray", [90, 39], "color");
define("grey", [90, 39], "color");
define("bgBlack", [40, 49], "bg");
define("bgRed", [41, 49], "bg");
define("bgGreen", [42, 49], "bg");
define("bgYellow", [43, 49], "bg");
define("bgBlue", [44, 49], "bg");
define("bgMagenta", [45, 49], "bg");
define("bgWhite", [47, 49], "bg");
define("blackBright", [90, 39], "bright");
define("redBright", [91, 39], "bright");
define("greenBright", [92, 39], "bright");
define("yellowBright", [93, 39], "bright");
define("blueBright", [94, 39], "bright");
define("magentaBright", [95, 39], "bright");
define("whiteBright", [97, 39], "bright");
define("bgBlackBright", [100, 49], "bgBright");
define("bgRedBright", [101, 49], "bgBright");
define("bgGreenBright", [102, 49], "bgBright");
define("bgYellowBright", [103, 49], "bgBright");
define("bgBlueBright", [104, 49], "bgBright");
define("bgMagentaBright", [105, 49], "bgBright");
define("bgWhiteBright", [107, 49], "bgBright");
colors.alias = (name, color) => {
const fn = colors[color];
if (typeof fn !== "function") {
throw new TypeError("Expected alias to be the name of an existing color (string) or a function");
}
if (!fn.stack) {
Reflect.defineProperty(fn, "name", { value: name });
colors.styles[name] = fn;
fn.stack = [name];
}
Reflect.defineProperty(colors, name, {
configurable: true,
enumerable: true,
set(value) {
colors.alias?.(name, value);
},
get() {
const col = (input) => style(input, col.stack);
Reflect.setPrototypeOf(col, colors);
col.stack = this.stack ? this.stack.concat(fn.stack) : fn.stack;
return col;
}
});
};
return colors;
})();
// src/log-level.ts
var logLevels = ["trace", "verbose", "info", "warn", "error"];
var getNumberForLogLevel = (level) => {
return logLevels.indexOf(level);
};
var isValidLogLevel = (level) => {
return getNumberForLogLevel(level) > -1;
};
var isEqualOrBelowLogLevel = (currentLevel, level) => {
return getNumberForLogLevel(currentLevel) <= getNumberForLogLevel(level);
};
// src/repro.ts
import { execSync } from "node:child_process";
import fs3, { rmSync } from "node:fs";
import os from "node:os";
import path3 from "node:path";
import { VERSION } from "remotion/version";
// src/find-closest-package-json.ts
import fs2 from "node:fs";
import path2 from "node:path";
var recursionLimit = 5;
var findClosestPackageJson = () => {
let currentDir = process.cwd();
let possiblePackageJson = "";
for (let i = 0;i < recursionLimit; i++) {
possiblePackageJson = path2.join(currentDir, "package.json");
const exists = fs2.existsSync(possiblePackageJson);
if (exists) {
return possiblePackageJson;
}
currentDir = path2.dirname(currentDir);
}
return null;
};
var findRemotionRoot = () => {
const closestPackageJson = findClosestPackageJson();
if (closestPackageJson === null) {
return process.cwd();
}
return path2.dirname(closestPackageJson);
};
// src/is-serve-url.ts
var isServeUrl = (potentialUrl) => {
if (typeof potentialUrl === "undefined") {
throw new Error("serveUrl is undefined");
}
if (potentialUrl.startsWith("www.") || potentialUrl.includes("amazonaws.com")) {
return true;
}
return potentialUrl.startsWith("https://") || potentialUrl.startsWith("http://");
};
// src/repro.ts
var REPRO_DIR = ".remotionrepro";
var LOG_FILE_NAME = "logs.txt";
var INPUT_DIR = "bundle";
var OUTPUT_DIR = "output";
var LINE_SPLIT = `
`;
var getZipFileName = (name) => `remotion-repro-${name}-${Date.now()}.zip`;
var readyDirSync = (dir) => {
let items;
try {
items = fs3.readdirSync(dir);
} catch {
return fs3.mkdirSync(dir, { recursive: true });
}
items.forEach((item) => {
item = path3.join(dir, item);
fs3.rmSync(item, { recursive: true, force: true });
});
};
var zipFolder = ({
sourceFolder,
targetZip,
indent,
logLevel
}) => {
const platform = os.platform();
try {
Log.info({ indent, logLevel }, "+ Creating reproduction ZIP");
if (platform === "win32") {
execSync(`powershell.exe Compress-Archive -Path "${sourceFolder}" -DestinationPath "${targetZip}"`);
} else {
execSync(`zip -r "${targetZip}" "${sourceFolder}"`);
}
rmSync(sourceFolder, { recursive: true });
Log.info({ indent, logLevel }, `${chalk.blue(`+ Repro: ${targetZip}`)}`);
} catch (error) {
Log.error({ indent, logLevel }, `Failed to zip repro folder, The repro folder is ${sourceFolder}. You can try manually zip it.`);
Log.error({ indent, logLevel }, error);
}
};
var reproWriter = (name) => {
const root = findRemotionRoot();
const reproFolder = path3.join(root, REPRO_DIR);
const logPath = path3.join(reproFolder, LOG_FILE_NAME);
const zipFile = path3.join(root, getZipFileName(name));
readyDirSync(reproFolder);
const reproLogWriteStream = fs3.createWriteStream(logPath, { flags: "a" });
const serializeArgs = (args) => JSON.stringify(args);
const writeLine = (level, ...args) => {
if (!args.length)
return;
const startTime = new Date().toISOString();
const line = `[${startTime}] ${level} ${serializeArgs(args)}`;
reproLogWriteStream.write(line + LINE_SPLIT);
};
const start = ({
serveUrl,
serializedInputPropsWithCustomSchema,
serializedResolvedPropsWithCustomSchema
}) => {
const isServe = isServeUrl(serveUrl);
if (!isServe) {
const inputDir = path3.resolve(reproFolder, INPUT_DIR);
readyDirSync(inputDir);
fs3.cpSync(serveUrl, inputDir, { recursive: true });
}
const serializedProps = path3.resolve(reproFolder, "input-props.json");
fs3.writeFileSync(serializedProps, serializedInputPropsWithCustomSchema);
const serializedResolvedProps = path3.resolve(reproFolder, "resolved-props.json");
fs3.writeFileSync(serializedResolvedProps, serializedResolvedPropsWithCustomSchema);
writeLine("info", [`Args: ${JSON.stringify(process.argv)}`]);
writeLine("info", [`Node/Bun version: ${process.version}`]);
writeLine("info", [`OS: ${process.platform}-${process.arch}`]);
writeLine("info", [`Serve URL: ${serveUrl}`]);
writeLine("info", [`Remotion version: ${VERSION}`]);
};
const onRenderSucceed = ({
indent,
logLevel,
output
}) => {
return new Promise((resolve, reject) => {
try {
if (output) {
const outputDir = path3.resolve(reproFolder, OUTPUT_DIR);
readyDirSync(outputDir);
const fileName = path3.basename(output);
const targetPath = path3.join(outputDir, fileName);
fs3.copyFileSync(output, targetPath);
}
disableRepro();
reproLogWriteStream.end(() => {
reproLogWriteStream.close(() => {
zipFolder({
sourceFolder: reproFolder,
targetZip: zipFile,
indent,
logLevel
});
resolve();
});
});
} catch (error) {
Log.error({ indent: false, logLevel }, `repro render success error:`);
Log.error({ indent: false, logLevel }, error);
reject(error);
}
});
};
return {
start,
writeLine,
onRenderSucceed
};
};
var reproWriteInstance = null;
var getReproWriter = () => {
if (!reproWriteInstance) {
throw new Error("reproWriteInstance is not initialized");
}
return reproWriteInstance;
};
var writeInRepro = (level, ...args) => {
if (isReproEnabled()) {
getReproWriter().writeLine(level, ...args);
}
};
var shouldRepro = false;
var enableRepro = ({
serveUrl,
compositionName,
serializedInputPropsWithCustomSchema,
serializedResolvedPropsWithCustomSchema
}) => {
shouldRepro = true;
reproWriteInstance = reproWriter(compositionName);
getReproWriter().start({
serveUrl,
serializedInputPropsWithCustomSchema,
serializedResolvedPropsWithCustomSchema
});
};
var disableRepro = () => {
shouldRepro = false;
};
var isReproEnabled = () => shouldRepro;
// src/truthy.ts
function truthy(value) {
return Boolean(value);
}
// src/logger.ts
var INDENT_TOKEN = chalk.gray("│");
var verboseTag = (str) => {
return isColorSupported() ? chalk.bgBlack(` ${str} `) : `[${str}]`;
};
var Log = {
trace: (options, ...args) => {
writeInRepro("trace", ...args);
if (isEqualOrBelowLogLevel(options.logLevel, "trace")) {
if (args.length === 0) {
return process.stdout.write(`
`);
}
return console.log(...[
options.indent ? INDENT_TOKEN : null,
options.tag ? verboseTag(options.tag) : null
].filter(truthy).concat(args.map((a) => chalk.gray(a))));
}
},
verbose: (options, ...args) => {
writeInRepro("verbose", ...args);
if (isEqualOrBelowLogLevel(options.logLevel, "verbose")) {
if (args.length === 0) {
return process.stdout.write(`
`);
}
return console.log(...[
options.indent ? INDENT_TOKEN : null,
options.tag ? verboseTag(options.tag) : null
].filter(truthy).concat(args.map((a) => chalk.gray(a))));
}
},
info: (options, ...args) => {
writeInRepro("info", ...args);
if (isEqualOrBelowLogLevel(options.logLevel, "info")) {
if (args.length === 0) {
return process.stdout.write(`
`);
}
return console.log(...[options.indent ? INDENT_TOKEN : null].filter(truthy).concat(args ?? []));
}
},
warn: (options, ...args) => {
writeInRepro("warn", ...args);
if (isEqualOrBelowLogLevel(options.logLevel, "warn")) {
if (args.length === 0) {
return process.stdout.write(`
`);
}
return console.warn(...[options.indent ? chalk.yellow(INDENT_TOKEN) : null].filter(truthy).concat(args.map((a) => chalk.yellow(a))));
}
},
error: (options, ...args) => {
writeInRepro("error", ...args);
if (isEqualOrBelowLogLevel(options.logLevel, "error")) {
if (args.length === 0) {
return process.stdout.write(`
`);
}
return console.error(...[
options.indent ? INDENT_TOKEN : null,
options.tag ? verboseTag(options.tag) : null
].filter(truthy).concat(args.map((a) => chalk.red(a))));
}
}
};
// src/assets/read-file.ts
import https from "https";
import http from "node:http";
// src/redirect-status-codes.ts
var redirectStatusCodes = [301, 302, 303, 307, 308];
// src/assets/read-file.ts
var getClient = (url) => {
if (url.startsWith("https://")) {
return https.get;
}
if (url.startsWith("http://")) {
return http.get;
}
throw new Error(`Can only download URLs starting with http:// or https://, got "${url}"`);
};
var readFileWithoutRedirect = (url) => {
return new Promise((resolve, reject) => {
const client = getClient(url);
const req = client(url, typeof Bun === "undefined" ? {
headers: {
"user-agent": "Mozilla/5.0 (@remotion/renderer - https://remotion.dev)"
}
} : {}, (res) => {
resolve({ request: req, response: res });
});
req.on("error", (err) => {
req.destroy();
return reject(err);
});
});
};
var readFile = async (url, redirectsSoFar = 0) => {
if (redirectsSoFar > 10) {
throw new Error(`Too many redirects while downloading ${url}`);
}
const { request, response } = await readFileWithoutRedirect(url);
if (redirectStatusCodes.includes(response.statusCode)) {
if (!response.headers.location) {
throw new Error(`Received a status code ${response.statusCode} but no "Location" header while calling ${response.headers.location}`);
}
const { origin } = new URL(url);
const redirectUrl = new URL(response.headers.location, origin).toString();
request.destroy();
response.destroy();
return readFile(redirectUrl, redirectsSoFar + 1);
}
if (response.statusCode >= 400) {
const body = await tryToObtainBody(response);
request.destroy();
response.destroy();
throw new Error([
`Received a status code of ${response.statusCode} while downloading file ${url}.`,
body ? `The response body was:` : null,
body ? `---` : null,
body ? body : null,
body ? `---` : null
].filter(truthy).join(`
`));
}
return { request, response };
};
var tryToObtainBody = async (file) => {
const success = new Promise((resolve) => {
let data = "";
file.on("data", (chunk) => {
data += chunk;
});
file.on("end", () => {
resolve(data);
});
file.on("error", () => resolve(data));
});
let timeout = null;
const body = await Promise.race([
success,
new Promise((resolve) => {
timeout = setTimeout(() => {
resolve(null);
}, 5000);
})
]);
if (timeout) {
clearTimeout(timeout);
}
return body;
};
// src/assets/download-file.ts
var incorrectContentLengthToken = "Download finished with";
var downloadFileWithoutRetries = ({ onProgress, url, to: toFn }) => {
return new Promise((resolve, reject) => {
let rejected = false;
let resolved = false;
let timeout;
const resolveAndFlag = (val) => {
resolved = true;
resolve(val);
if (timeout) {
clearTimeout(timeout);
}
};
const rejectAndFlag = (err) => {
if (timeout) {
clearTimeout(timeout);
}
reject(err);
rejected = true;
};
const refreshTimeout = () => {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
if (resolved) {
return;
}
rejectAndFlag(new Error(`Tried to download file ${url}, but the server sent no data for 20 seconds`));
}, 20000);
};
refreshTimeout();
let finishEventSent = false;
let closeConnection = () => {
return;
};
readFile(url).then(({ response, request }) => {
closeConnection = () => {
request.destroy();
response.destroy();
};
const contentDisposition = response.headers["content-disposition"] ?? null;
const contentType = response.headers["content-type"] ?? null;
const to = toFn(contentDisposition, contentType);
ensureOutputDirectory(to);
const sizeHeader = response.headers["content-length"];
const totalSize = typeof sizeHeader === "undefined" ? null : Number(sizeHeader);
const writeStream = createWriteStream(to);
let downloaded = 0;
writeStream.on("close", () => {
if (rejected) {
return;
}
if (!finishEventSent) {
onProgress?.({
downloaded,
percent: 1,
totalSize: downloaded
});
}
refreshTimeout();
return resolveAndFlag({ sizeInBytes: downloaded, to });
});
writeStream.on("error", (err) => rejectAndFlag(err));
response.on("error", (err) => {
closeConnection();
rejectAndFlag(err);
});
response.pipe(writeStream).on("error", (err) => rejectAndFlag(err));
response.on("data", (d) => {
refreshTimeout();
downloaded += d.length;
refreshTimeout();
const percent = totalSize === null ? null : downloaded / totalSize;
onProgress?.({
downloaded,
percent,
totalSize
});
if (percent === 1) {
finishEventSent = true;
}
});
response.on("close", () => {
if (totalSize !== null && downloaded !== totalSize) {
rejectAndFlag(new Error(`${incorrectContentLengthToken} ${downloaded} bytes, but expected ${totalSize} bytes from 'Content-Length'.`));
}
writeStream.close();
closeConnection();
});
}).catch((err) => {
rejectAndFlag(err);
});
});
};
var downloadFile = async (options, retries = 2, attempt = 1) => {
try {
const res = await downloadFileWithoutRetries(options);
return res;
} catch (err) {
const { message } = err;
if (message === "aborted" || message.includes("ECONNRESET") || message.includes(incorrectContentLengthToken) || message.includes("503") || message.includes("502") || message.includes("504") || message.includes("500")) {
if (retries === 0) {
throw err;
}
Log.warn({ indent: options.indent, logLevel: options.logLevel }, `Downloading ${options.url} failed (will retry): ${message}`);
const backoffInSeconds = (attempt + 1) ** 2;
await new Promise((resolve) => {
setTimeout(() => resolve(), backoffInSeconds * 1000);
});
return downloadFile(options, retries - 1, attempt + 1);
}
throw err;
}
};
// src/browser.ts
var DEFAULT_BROWSER = "chrome";
// src/browser/assert.ts
var assert = (value, message) => {
if (!value) {
throw new Error(message);
}
};
// src/browser/BrowserPage.ts
import { NoReactInternals as NoReactInternals2 } from "remotion/no-react";
// src/format-logs.ts
import { NoReactInternals } from "remotion/no-react";
var formatRemoteObject = (remoteObject) => {
if (remoteObject.preview) {
return formatObjectPreview(remoteObject.preview);
}
if (remoteObject.type === "string") {
const isDelayRenderClear = remoteObject.value.includes(NoReactInternals.DELAY_RENDER_CLEAR_TOKEN);
if (isDelayRenderClear) {
return chalk.gray(`${remoteObject.value}`);
}
return chalk.reset(`${remoteObject.value}`);
}
if (remoteObject.type === "number") {
return chalk.yellow(`${remoteObject.value}`);
}
if (remoteObject.type === "bigint") {
return chalk.yellow(`${remoteObject.description}`);
}
if (remoteObject.type === "boolean") {
return chalk.yellow(`${remoteObject.value}`);
}
if (remoteObject.type === "function") {
return chalk.cyan(String(remoteObject.description));
}
if (remoteObject.type === "object") {
if (remoteObject.subtype === "null") {
return chalk.white(`null`);
}
return chalk.reset(`Object`);
}
if (remoteObject.type === "symbol") {
return chalk.green(`${remoteObject.description}`);
}
if (remoteObject.type === "undefined") {
return chalk.gray(`undefined`);
}
throw new Error("unhandled remote object");
};
var formatObjectPreview = (preview) => {
if (typeof preview === "undefined") {
return "";
}
if (preview.type === "object") {
if (preview.subtype === "date") {
return chalk.reset(`Date { ${chalk.magenta(String(preview.description))} }`);
}
const properties = preview.properties.map((property) => {
return chalk.reset(`${property.name}: ${formatProperty(property)}`);
});
if (preview.subtype === "array") {
if (preview.overflow) {
return chalk.reset(`[ ${preview.properties.map((p) => formatProperty(p)).join(", ")}, …]`);
}
return chalk.reset(`[ ${preview.properties.map((p) => formatProperty(p)).join(", ")} ]`);
}
if (preview.subtype === "arraybuffer") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "dataview") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "generator") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "iterator") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "map") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "node") {
return chalk.magenta(`<${preview.description}>`);
}
if (preview.subtype === "null") {
return chalk.white(String(preview.description));
}
if (preview.subtype === "promise") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "proxy") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "regexp") {
return chalk.red(String(preview.description));
}
if (preview.subtype === "set") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "typedarray") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "error") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "wasmvalue") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "weakmap") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "weakset") {
return chalk.reset(String(preview.description));
}
if (preview.subtype === "webassemblymemory") {
return chalk.reset(String(preview.description));
}
if (properties.length === 0) {
return chalk.reset("{}");
}
if (preview.overflow) {
return chalk.reset(`{ ${properties.join(", ")}, …}`);
}
return chalk.reset(`{ ${properties.join(", ")} }`);
}
return "";
};
var formatProperty = (property) => {
if (property.type === "string") {
return chalk.green(`"${property.value}"`);
}
if (property.type === "object") {
if (!property.subtype && property.value === "Object") {
return chalk.reset(`{…}`);
}
if (property.subtype === "date") {
return chalk.reset(`Date { ${chalk.magenta(String(property.value))} }`);
}
if (property.subtype === "arraybuffer") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "array") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "dataview") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "error") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "generator") {
return chalk.reset(`[generator ${property.value}]`);
}
if (property.subtype === "iterator") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "map") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "node") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "null") {
return chalk.white(`${property.value}`);
}
if (property.subtype === "promise") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "proxy") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "regexp") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "set") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "typedarray") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "wasmvalue") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "webassemblymemory") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "weakmap") {
return chalk.reset(`${property.value}`);
}
if (property.subtype === "weakset") {
return chalk.reset(`${property.value}`);
}
return chalk.reset(`${property.value}`);
}
if (property.type === "accessor") {
return chalk.gray(`get()`);
}
if (property.type === "bigint") {
return chalk.yellow(`${property.value}`);
}
if (property.type === "boolean") {
return chalk.yellow(`${property.value}`);
}
if (property.type === "function") {
return chalk.cyan(`Function`);
}
if (property.type === "number") {
return chalk.yellow(`${property.value}`);
}
if (property.type === "symbol") {
return chalk.green(`${property.value}`);
}
if (property.type === "undefined") {
return chalk.gray(`undefined`);
}
throw new Error("unexpected property type " + JSON.stringify(property));
};
// src/browser/ConsoleMessage.ts
class ConsoleMessage {
type;
text;
args;
previewString;
#stackTraceLocations;
constructor({
type,
text,
args,
stackTraceLocations,
previewString
}) {
this.type = type;
this.text = text;
this.args = args;
this.previewString = previewString;
this.#stackTraceLocations = stackTraceLocations;
}
location() {
return this.#stackTraceLocations[0] ?? {};
}
stackTrace() {
return this.#stackTraceLocations;
}
}
// src/browser/mitt/index.ts
function mitt(all) {
all = all || new Map;
return {
all,
on: (type, handler) => {
const handlers = all?.get(type);
const added = handlers?.push(handler);
if (!added) {
all?.set(type, [handler]);
}
},
off: (type, handler) => {
const handlers = all?.get(type);
if (handlers) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
},
emit: (type, evt) => {
(all?.get(type) || []).slice().forEach((handler) => {
handler(evt);
});
(all?.get("*") || []).slice().forEach((handler) => {
handler(type, evt);
});
}
};
}
// src/browser/EventEmitter.ts
class EventEmitter {
emitter;
eventsMap = new Map;
constructor() {
this.emitter = mitt(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) {
return this.eventsMap.get(event)?.length || 0;
}
}
// src/browser/Errors.ts
class CustomError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
}
class TimeoutError extends CustomError {
}
class ProtocolError extends CustomError {
code;
originalMessage = "";
}
// src/browser/Connection.ts
var ConnectionEmittedEvents = {
Disconnected: Symbol("Connection.Disconnected")
};
class Connection extends 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) {
const params = paramArgs.length ? paramArgs[0] : undefined;
const id = this._rawSend({ method, params });
return new Promise((resolve, reject) => {
this.#callbacks.set(id, {
resolve,
reject,
method,
returnSize: true,
stack: new Error().stack ?? "",
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);
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 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();
}
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;
}
}
var CDPSessionEmittedEvents = {
Disconnected: Symbol("CDPSession.Disconnected")
};
class CDPSession extends 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.`));
}
const params = paramArgs.length ? paramArgs[0] : undefined;
const id = this.#connection._rawSend({
sessionId: this.#sessionId,
method,
params
});
return new Promise((resolve, reject) => {
if (this.#callbacks.size > 100) {
for (const callback of this.#callbacks.values()) {
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: new Error().stack ?? "",
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 ProtocolError, `Protocol error (${callback.method}): Target closed. https://www.remotion.dev/docs/target-closed`));
}
this.#callbacks.clear();
this.emit(CDPSessionEmittedEvents.Disconnected);
}
id() {
return this.#sessionId;
}
}
function createProtocolError(method, object) {
let message = `Protocol error (${method}): ${object.error.message}`;
if ("data" in object.error) {
message += ` ${object.error.data}`;
}
return rewriteError(new ProtocolError, message, object.error.message);
}
function rewriteError(error, message, originalMessage) {
error.message = message;
error.originalMessage = originalMessage ?? error.originalMessage;
return error;
}
// src/browser/util.ts
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 += `
at ${functionName} (${location})`;
}
}
return message;
}
function valueFromRemoteObject(remoteObject) {
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(() => {});
}
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;
}
var isString = (obj) => {
return typeof obj === "string" || obj instanceof String;
};
function evaluationString(fun, ...args) {
if (isString(fun)) {
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 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" /* 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));
}
// src/browser/DOMWorld.ts
class DOMWorld {
#frame;
#contextPromise = null;
#contextResolveCallback = null;
#detached = false;
#waitTasks = new Set;
get _waitTasks() {
return this.#waitTasks;
}
constructor(frame) {
this.#frame = frame;
this._setContext(null);
}
frame() {
return this.#frame;
}
_setContext(context) {
if (context) {
assert(this.#contextResolveCallback, "Execution Context has already been set.");
this.#contextResolveCallback?.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
});
}
}
var noop = () => {
return;
};
class WaitTask {
#domWorld;
#timeout;
#predicateBody;
#args;
#runCount = 0;
#resolve = noop;
#reject = noop;
#timeoutTimer;
#terminated = false;
#browser;
promise;
constructor(options) {
function getPredicateBody(predicateBody) {
if (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;
});
if (options.timeout) {
const timeoutError = new 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" /* Closed */, this.onBrowserClose);
this.#browser.on("closed-silent" /* 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;
}
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();
}
if (error.message.includes("Execution context is not available in detached frame")) {
this.terminate(new Error("waitForFunction failed: frame got detached."));
return;
}
if (error.message.includes("Execution context was destroyed")) {
return;
}
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" /* Closed */, this.onBrowserClose);
this.#browser.off("closed-silent" /* 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) {
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();
});
}
// src/browser/JSHandle.ts
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 releaseObject(this.#client, this.#remoteObject);
}
toString() {
if (this.#remoteObject.objectId) {
const type = this.#remoteObject.subtype || this.#remoteObject.type;
return "JSHandle@" + type;
}
return valueFromRemoteObject(this.#remoteObject);
}
}
class ElementHandle extends JSHandle {
asElement() {
return this;
}
}
// src/browser/ExecutionContext.ts
var EVALUATION_SCRIPT_URL = "pptr://__puppeteer_evaluation_script__";
var 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=${EVALUATION_SCRIPT_URL}`;
if (isString(pageFunction)) {
const contextId = this._contextId;
const expression = pageFunction;
const expressionWithSourceUrl = SOURCE_URL_REGEX.test(expression) ? expression : expression + `
` + suffix;
const {
value: { exceptionDetails: _details, result: _remoteObject }
} = await this._client.send("Runtime.evaluate", {
expression: expressionWithSourceUrl,
contextId,
returnByValue,
awaitPromise: true,
userGesture: true
}).catch(rewriteError2);
if (_details) {
throw new Error("Evaluation failed: " + getExceptionMessage(_details));
}
return returnByValue ? valueFromRemoteObject(_remoteObject) : _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) {
if (functionText.startsWith("async ")) {
functionText = "async function " + functionText.substring("async ".length);
} else {
functionText = "function " + functionText;
}
try {
new Function("(" + functionText + ")");