UNPKG

@remotion/renderer

Version:

Render Remotion videos using Node.js or Bun

1,668 lines (1,630 loc) 602 kB
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 + ")");