diff --git a/.nx/version-plans/compute-metro-cache-keys.md b/.nx/version-plans/compute-metro-cache-keys.md new file mode 100644 index 00000000..9a8fa590 --- /dev/null +++ b/.nx/version-plans/compute-metro-cache-keys.md @@ -0,0 +1,5 @@ +--- +__default__: minor +--- + +The official GitHub Action now computes the Metro cache key itself instead of hashing a static file list: it accounts for your lockfile(s), Metro/Babel config, the resolved `@react-native-harness/bundler-metro` version, and your `cache.version` salt, so the cache invalidates automatically when you upgrade Metro, not only when a lockfile or config file changes. A new `cacheSavePolicy` action input (`'default-branch'` by default, or `'always'`/`'never'`) controls when a new cache entry is saved, and a run only saves a new entry when its cache contents actually changed. diff --git a/action.yml b/action.yml index 09cf8899..0a6fe34b 100644 --- a/action.yml +++ b/action.yml @@ -43,6 +43,11 @@ inputs: required: false type: string default: '' + cacheSavePolicy: + description: When to save a new Metro cache entry ('default-branch', 'always', or 'never') + required: false + type: string + default: 'default-branch' runs: using: 'composite' steps: @@ -64,21 +69,45 @@ runs: echo "Please provide the path to the built app (.apk for Android, .app for iOS)" exit 1 fi - - name: Metro bundler cache (.harness/cache/metro) - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + - name: Plan Metro cache restore + id: plan-metro-restore + shell: bash + env: + INPUT_PROJECTROOT: ${{ steps.load-config.outputs.projectRoot }} + run: | + node ${{ github.action_path }}/actions/shared/plan-restore.cjs + - name: Restore Metro cache (.harness/cache/metro) + id: restore-metro + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/metro ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/metro-file-map - key: ${{ runner.os }}-metro-cache-v2-${{ hashFiles('**/bun.lock', '**/bun.lockb', '**/package-lock.json', '**/npm-shrinkwrap.json', '**/pnpm-lock.yaml', '**/yarn.lock', '**/metro.config.js', '**/metro.config.cjs', '**/metro.config.mjs', '**/metro.config.ts', '**/babel.config.js', '**/babel.config.cjs', '**/babel.config.mjs', '**/babel.config.ts', '**/babel.config.json') }} - restore-keys: | - ${{ runner.os }}-metro-cache-v2- + key: ${{ steps.plan-metro-restore.outputs.metroRestoreKey }} + # The '\n' here is a literal newline embedded via YAML double-quote + # escaping (not a GitHub Actions expression escape -- expression + # string literals don't interpret backslash sequences), so join() + # actually separates entries onto their own lines, as actions/cache + # requires for restore-keys. + restore-keys: "${{ join(fromJson(steps.plan-metro-restore.outputs.metroRestorePrefixes), '\n') }}" + # Must run after "Restore Metro cache" so this reflects what was actually + # restored (empty on a cache miss), not the always-empty pre-restore state. + - name: Snapshot Metro cache + id: snapshot-metro-restore + shell: bash + env: + INPUT_PROJECTROOT: ${{ steps.load-config.outputs.projectRoot }} + # Empty on a cache miss; snapshot-metro.cjs uses it to report whether + # the Metro cache was actually restored and from which key. + METRO_RESTORED_KEY: ${{ steps.restore-metro.outputs.cache-matched-key }} + run: | + node ${{ github.action_path }}/actions/shared/snapshot-metro.cjs - name: Restore Harness cache id: cache-harness-restore if: fromJson(steps.load-config.outputs.config).platformId == 'ios' uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: - path: ${{ steps.load-config.outputs.projectRoot }}/.harness/cache + path: ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/xctest-agent-simulator-* key: harness-ios-${{ runner.os }}-${{ hashFiles(format('{0}/.harness/cache/**/cache.json', steps.load-config.outputs.projectRoot)) }} restore-keys: | harness-ios-${{ runner.os }}- @@ -242,8 +271,27 @@ runs: if: ${{ always() && fromJson(steps.load-config.outputs.config).platformId == 'ios' && steps.cache-harness-restore.outputs.cache-hit != 'true' }} uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: - path: ${{ steps.load-config.outputs.projectRoot }}/.harness/cache + path: ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/xctest-agent-simulator-* key: ${{ steps.cache-harness-restore.outputs.cache-primary-key }} + - name: Plan Metro cache save + id: plan-metro-save + if: always() + shell: bash + env: + INPUT_PROJECTROOT: ${{ steps.load-config.outputs.projectRoot }} + INPUT_METRO_SNAPSHOT: ${{ steps.snapshot-metro-restore.outputs.metroSnapshot }} + INPUT_CACHESAVEPOLICY: ${{ inputs.cacheSavePolicy }} + IS_DEFAULT_BRANCH: ${{ github.ref_name == github.event.repository.default_branch }} + run: | + node ${{ github.action_path }}/actions/shared/plan-save.cjs + - name: Save Metro cache + if: always() && steps.plan-metro-save.outputs.metroShouldSave == 'true' + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: | + ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/metro + ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/metro-file-map + key: ${{ steps.plan-metro-save.outputs.metroSaveKey }} - name: Upload crash report artifacts if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 diff --git a/actions/shared/plan-restore.cjs b/actions/shared/plan-restore.cjs new file mode 100644 index 00000000..a819c8a5 --- /dev/null +++ b/actions/shared/plan-restore.cjs @@ -0,0 +1,5042 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// ../../node_modules/picocolors/picocolors.js +var require_picocolors = __commonJS({ + "../../node_modules/picocolors/picocolors.js"(exports2, module2) { + "use strict"; + var p = process || {}; + var argv = p.argv || []; + var env = p.env || {}; + var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI); + var formatter = (open, close, replace = open) => (input) => { + let string = "" + input, index = string.indexOf(close, open.length); + return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close; + }; + var replaceClose = (string, close, replace, index) => { + let result = "", cursor = 0; + do { + result += string.substring(cursor, index) + replace; + cursor = index + close.length; + index = string.indexOf(close, cursor); + } while (~index); + return result + string.substring(cursor); + }; + var createColors = (enabled = isColorSupported) => { + let f = enabled ? formatter : () => String; + return { + isColorSupported: enabled, + reset: f("\x1B[0m", "\x1B[0m"), + bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"), + dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"), + italic: f("\x1B[3m", "\x1B[23m"), + underline: f("\x1B[4m", "\x1B[24m"), + inverse: f("\x1B[7m", "\x1B[27m"), + hidden: f("\x1B[8m", "\x1B[28m"), + strikethrough: f("\x1B[9m", "\x1B[29m"), + black: f("\x1B[30m", "\x1B[39m"), + red: f("\x1B[31m", "\x1B[39m"), + green: f("\x1B[32m", "\x1B[39m"), + yellow: f("\x1B[33m", "\x1B[39m"), + blue: f("\x1B[34m", "\x1B[39m"), + magenta: f("\x1B[35m", "\x1B[39m"), + cyan: f("\x1B[36m", "\x1B[39m"), + white: f("\x1B[37m", "\x1B[39m"), + gray: f("\x1B[90m", "\x1B[39m"), + bgBlack: f("\x1B[40m", "\x1B[49m"), + bgRed: f("\x1B[41m", "\x1B[49m"), + bgGreen: f("\x1B[42m", "\x1B[49m"), + bgYellow: f("\x1B[43m", "\x1B[49m"), + bgBlue: f("\x1B[44m", "\x1B[49m"), + bgMagenta: f("\x1B[45m", "\x1B[49m"), + bgCyan: f("\x1B[46m", "\x1B[49m"), + bgWhite: f("\x1B[47m", "\x1B[49m"), + blackBright: f("\x1B[90m", "\x1B[39m"), + redBright: f("\x1B[91m", "\x1B[39m"), + greenBright: f("\x1B[92m", "\x1B[39m"), + yellowBright: f("\x1B[93m", "\x1B[39m"), + blueBright: f("\x1B[94m", "\x1B[39m"), + magentaBright: f("\x1B[95m", "\x1B[39m"), + cyanBright: f("\x1B[96m", "\x1B[39m"), + whiteBright: f("\x1B[97m", "\x1B[39m"), + bgBlackBright: f("\x1B[100m", "\x1B[49m"), + bgRedBright: f("\x1B[101m", "\x1B[49m"), + bgGreenBright: f("\x1B[102m", "\x1B[49m"), + bgYellowBright: f("\x1B[103m", "\x1B[49m"), + bgBlueBright: f("\x1B[104m", "\x1B[49m"), + bgMagentaBright: f("\x1B[105m", "\x1B[49m"), + bgCyanBright: f("\x1B[106m", "\x1B[49m"), + bgWhiteBright: f("\x1B[107m", "\x1B[49m") + }; + }; + module2.exports = createColors(); + module2.exports.createColors = createColors; + } +}); + +// ../../node_modules/sisteransi/src/index.js +var require_src = __commonJS({ + "../../node_modules/sisteransi/src/index.js"(exports2, module2) { + "use strict"; + var ESC = "\x1B"; + var CSI = `${ESC}[`; + var beep = "\x07"; + var cursor = { + to(x2, y) { + if (!y) return `${CSI}${x2 + 1}G`; + return `${CSI}${y + 1};${x2 + 1}H`; + }, + move(x2, y) { + let ret = ""; + if (x2 < 0) ret += `${CSI}${-x2}D`; + else if (x2 > 0) ret += `${CSI}${x2}C`; + if (y < 0) ret += `${CSI}${-y}A`; + else if (y > 0) ret += `${CSI}${y}B`; + return ret; + }, + up: (count = 1) => `${CSI}${count}A`, + down: (count = 1) => `${CSI}${count}B`, + forward: (count = 1) => `${CSI}${count}C`, + backward: (count = 1) => `${CSI}${count}D`, + nextLine: (count = 1) => `${CSI}E`.repeat(count), + prevLine: (count = 1) => `${CSI}F`.repeat(count), + left: `${CSI}G`, + hide: `${CSI}?25l`, + show: `${CSI}?25h`, + save: `${ESC}7`, + restore: `${ESC}8` + }; + var scroll = { + up: (count = 1) => `${CSI}S`.repeat(count), + down: (count = 1) => `${CSI}T`.repeat(count) + }; + var erase = { + screen: `${CSI}2J`, + up: (count = 1) => `${CSI}1J`.repeat(count), + down: (count = 1) => `${CSI}J`.repeat(count), + line: `${CSI}2K`, + lineEnd: `${CSI}K`, + lineStart: `${CSI}1K`, + lines(count) { + let clear = ""; + for (let i = 0; i < count; i++) + clear += this.line + (i < count - 1 ? cursor.up() : ""); + if (count) + clear += cursor.left; + return clear; + } + }; + module2.exports = { cursor, scroll, erase, beep }; + } +}); + +// src/shared/plan-restore.ts +var import_node_fs12 = __toESM(require("fs")); +var import_node_path12 = __toESM(require("path")); + +// ../cache/dist/index.js +var import_node_fs9 = __toESM(require("fs"), 1); + +// ../tools/dist/net.js +var import_node_net = __toESM(require("net"), 1); + +// ../tools/dist/logger.js +var import_node_util = __toESM(require("util"), 1); +var import_picocolors = __toESM(require_picocolors(), 1); +var verbose = !!process.env.HARNESS_DEBUG; +var BASE_TAG = "[harness]"; +var INFO_TAG = import_picocolors.default.isColorSupported ? import_picocolors.default.reset(import_picocolors.default.inverse(import_picocolors.default.bold(import_picocolors.default.magenta(" HARNESS ")))) : "HARNESS"; +var ERROR_TAG = import_picocolors.default.isColorSupported ? import_picocolors.default.reset(import_picocolors.default.inverse(import_picocolors.default.bold(import_picocolors.default.red(" HARNESS ")))) : "HARNESS"; +var getTimestamp = () => (/* @__PURE__ */ new Date()).toISOString(); +var normalizeScope = (scope) => scope.trim().replace(/^\[+|\]+$/g, "").replace(/\]\[/g, "]["); +var formatPrefix = (scopes) => { + const suffix = scopes.map((scope) => `[${normalizeScope(scope)}]`).join(""); + return `${BASE_TAG}${suffix}`; +}; +var mapLines = (text, prefix) => text.split("\n").map((line) => `${prefix} ${line}`).join("\n"); +var writeLog = (level, scopes, messages) => { + if (!verbose && (level === "info" || level === "log" || level === "success")) { + const output2 = import_node_util.default.format(...messages); + const tag = INFO_TAG; + process.stderr.write(`${tag} ${output2} +`); + return; + } + if (!verbose && level === "error") { + const output2 = import_node_util.default.format(...messages); + process.stderr.write(`${ERROR_TAG} ${output2} +`); + return; + } + const method = level === "warn" ? console.warn : console.debug; + const output = import_node_util.default.format(...messages); + const prefix = `${getTimestamp()} ${formatPrefix(scopes)}`; + method(mapLines(output, prefix)); +}; +var setVerbose = (level) => { + verbose = level; +}; +var isVerbose = () => { + return verbose; +}; +var createScopedLogger = (scopes = []) => ({ + debug: (...messages) => { + if (!verbose) { + return; + } + writeLog("debug", scopes, messages); + }, + info: (...messages) => { + writeLog("info", scopes, messages); + }, + warn: (...messages) => { + writeLog("warn", scopes, messages); + }, + error: (...messages) => { + writeLog("error", scopes, messages); + }, + log: (...messages) => { + writeLog("log", scopes, messages); + }, + success: (...messages) => { + writeLog("success", scopes, messages); + }, + child: (scope) => createScopedLogger([...scopes, scope]), + setVerbose, + isVerbose +}); +var logger = createScopedLogger(); + +// ../../node_modules/@clack/core/dist/index.mjs +var import_node_process = require("process"); +var k = __toESM(require("readline"), 1); +var import_node_readline = __toESM(require("readline"), 1); +var import_sisteransi = __toESM(require_src(), 1); +var import_node_tty = require("tty"); +var Ft = { limit: 1 / 0, ellipsis: "" }; +var ft = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 }; +var j = "\x07"; +var Q = "["; +var dt = "]"; +var U = `${dt}8;;`; +var et = new RegExp(`(?:\\${Q}(?\\d+)m|\\${U}(?.*)${j})`, "y"); +var At = ["up", "down", "left", "right", "space", "enter", "cancel"]; +var _ = { actions: new Set(At), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]), messages: { cancel: "Canceled", error: "Something went wrong" }, withGuide: true }; +var bt = globalThis.process.platform.startsWith("win"); + +// ../../node_modules/@clack/prompts/dist/index.mjs +var import_picocolors2 = __toESM(require_picocolors(), 1); +var import_node_process2 = __toESM(require("process"), 1); +var import_node_fs = require("fs"); +var import_node_path = require("path"); +var import_sisteransi2 = __toESM(require_src(), 1); +var import_node_util2 = require("util"); +function ht() { + return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm"; +} +var ee = ht(); +var w = (e, r) => ee ? e : r; +var Me = w("\u25C6", "*"); +var ce = w("\u25A0", "x"); +var de = w("\u25B2", "x"); +var V = w("\u25C7", "o"); +var $e = w("\u250C", "T"); +var h = w("\u2502", "|"); +var x = w("\u2514", "\u2014"); +var Re = w("\u2510", "T"); +var Oe = w("\u2518", "\u2014"); +var Y = w("\u25CF", ">"); +var K = w("\u25CB", " "); +var te = w("\u25FB", "[\u2022]"); +var k2 = w("\u25FC", "[+]"); +var z = w("\u25FB", "[ ]"); +var Pe = w("\u25AA", "\u2022"); +var se = w("\u2500", "-"); +var he = w("\u256E", "+"); +var Ne = w("\u251C", "+"); +var me = w("\u256F", "+"); +var pe = w("\u2570", "+"); +var We = w("\u256D", "+"); +var ge = w("\u25CF", "\u2022"); +var fe = w("\u25C6", "*"); +var Fe = w("\u25B2", "!"); +var ye = w("\u25A0", "x"); +var Ft2 = { limit: 1 / 0, ellipsis: "" }; +var yt2 = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 }; +var Ce = "\x07"; +var Ve = "["; +var vt = "]"; +var we = `${vt}8;;`; +var Ge = new RegExp(`(?:\\${Ve}(?\\d+)m|\\${we}(?.*)${Ce})`, "y"); +var Ut = import_picocolors2.default.magenta; +var Ye = { light: w("\u2500", "-"), heavy: w("\u2501", "="), block: w("\u2588", "#") }; +var ze = `${import_picocolors2.default.gray(h)} `; + +// ../tools/dist/spawn.js +var spawnLogger = logger.child("spawn"); + +// ../tools/dist/react-native.js +var import_node_module = require("module"); +var import_node_path2 = __toESM(require("path"), 1); +var import_node_fs2 = __toESM(require("fs"), 1); + +// ../tools/dist/error.js +var HarnessError = class extends Error { +}; + +// ../tools/dist/packages.js +var import_node_path3 = __toESM(require("path"), 1); +var import_node_fs3 = __toESM(require("fs"), 1); + +// ../tools/dist/path.js +var import_node_path4 = __toESM(require("path"), 1); + +// ../tools/dist/crash-artifacts.js +var import_node_fs4 = __toESM(require("fs"), 1); +var import_node_path5 = __toESM(require("path"), 1); +var DEFAULT_ARTIFACT_ROOT = import_node_path5.default.join(process.cwd(), ".harness", "crash-reports"); + +// ../tools/dist/harness-artifacts.js +var import_node_fs5 = __toESM(require("fs"), 1); +var import_node_path6 = __toESM(require("path"), 1); + +// ../tools/dist/diagnostics.js +var noop = () => void 0; +var noopSpan = { + end: noop, + fail: noop +}; +var createNoopDiagnostics = () => { + const diagnostics = { + enabled: false, + start: () => noopSpan, + measure: async (_label, fn) => fn(), + mark: noop, + record: noop, + child: () => diagnostics, + flush: () => [] + }; + return diagnostics; +}; +var noopDiagnostics = createNoopDiagnostics(); + +// ../cache/dist/paths.js +var import_node_path7 = __toESM(require("path"), 1); +var HARNESS_DIRNAME = ".harness"; +var CACHE_DIRNAME = "cache"; +var METRO_TRANSFORM_DIRNAME = "metro"; +var METRO_FILE_MAP_DIRNAME = "metro-file-map"; +var createHarnessCachePaths = (projectRoot) => { + const root = import_node_path7.default.join(projectRoot, HARNESS_DIRNAME, CACHE_DIRNAME); + return { + root, + metroTransform: import_node_path7.default.join(root, METRO_TRANSFORM_DIRNAME), + metroFileMap: import_node_path7.default.join(root, METRO_FILE_MAP_DIRNAME) + }; +}; +var getDomainDirectories = (domain, paths) => { + switch (domain) { + case "metro": + return [paths.metroTransform, paths.metroFileMap]; + default: + return []; + } +}; + +// ../cache/dist/plan.js +var import_node_crypto = require("crypto"); +var import_node_fs6 = __toESM(require("fs"), 1); +var import_node_path8 = __toESM(require("path"), 1); +var cacheLogger = logger.child("cache"); +var KEYS_FILENAME = "keys.json"; +var HASH_LENGTH = 16; +var ALL_DOMAINS = ["metro"]; +var isAccretiveDomain = (domain) => { + switch (domain) { + case "metro": + return true; + default: + return false; + } +}; +var hashSortedEntries = (entries) => (0, import_node_crypto.createHash)("sha256").update(entries.slice().sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, value]) => `${key}=${value}`).join("\n")).digest("hex").slice(0, HASH_LENGTH); +var buildStaticHashKey = (domain, inputs) => { + const hash = hashSortedEntries(Object.entries(inputs.staticInputs)); + return `harness-${domain}-${inputs.os}-${hash}`; +}; +var buildContentHash = (entries) => hashSortedEntries(entries.map((entry) => [entry.path, String(entry.sizeBytes)])); +var walkDirectory = (root, directory, entries) => { + let dirents; + try { + dirents = import_node_fs6.default.readdirSync(directory, { withFileTypes: true }); + } catch { + return; + } + for (const dirent of dirents) { + const fullPath = import_node_path8.default.join(directory, dirent.name); + if (dirent.isDirectory()) { + walkDirectory(root, fullPath, entries); + } else if (dirent.isFile()) { + const stat = import_node_fs6.default.statSync(fullPath); + entries.push({ + path: import_node_path8.default.relative(root, fullPath), + sizeBytes: stat.size, + mtimeMs: stat.mtimeMs + }); + } + } +}; +var planRestore = (paths, inputs) => { + const domains = {}; + for (const domain of ALL_DOMAINS) { + const domainInputs = inputs[domain]; + if (!domainInputs) { + continue; + } + const restoreKey = buildStaticHashKey(domain, domainInputs); + const accretive = isAccretiveDomain(domain); + domains[domain] = { + paths: [...getDomainDirectories(domain, paths)], + restoreKey, + restorePrefixes: accretive ? [`${restoreKey}-`] : [], + accretive + }; + } + return { version: 1, domains }; +}; +var snapshot = (paths) => { + const result = {}; + for (const domain of ALL_DOMAINS) { + try { + const entries = []; + for (const directory of getDomainDirectories(domain, paths)) { + walkDirectory(paths.root, directory, entries); + } + result[domain] = entries; + } catch (error) { + cacheLogger.warn(`Failed to snapshot cache domain "${domain}". Treating as empty.`, error); + result[domain] = []; + } + } + return result; +}; +var diffSnapshots = (before, after) => { + const beforeByPath = new Map(before.map((entry) => [entry.path, entry])); + const afterByPath = new Map(after.map((entry) => [entry.path, entry])); + let addedEntries = 0; + let removedEntries = 0; + let addedBytes = 0; + for (const [entryPath, afterEntry] of afterByPath) { + const beforeEntry = beforeByPath.get(entryPath); + if (!beforeEntry || beforeEntry.sizeBytes !== afterEntry.sizeBytes) { + addedEntries += 1; + addedBytes += afterEntry.sizeBytes; + } + } + for (const entryPath of beforeByPath.keys()) { + if (!afterByPath.has(entryPath)) { + removedEntries += 1; + } + } + return { addedEntries, removedEntries, addedBytes }; +}; +var shouldSaveForPolicy = (policy, delta) => { + if (delta.addedEntries + delta.removedEntries === 0) { + return false; + } + switch (policy.mode) { + case "always": + return true; + case "default-branch": + return policy.isDefaultBranch; + case "never": + return false; + } +}; +var planSave = (paths, before, policy, inputs) => { + const after = snapshot(paths); + const domains = {}; + for (const domain of ALL_DOMAINS) { + const domainInputs = inputs[domain]; + if (!domainInputs) { + continue; + } + const beforeEntries = before[domain] ?? []; + const afterEntries = after[domain] ?? []; + const delta = diffSnapshots(beforeEntries, afterEntries); + const staticHashKey = buildStaticHashKey(domain, domainInputs); + const accretive = isAccretiveDomain(domain); + domains[domain] = { + shouldSave: shouldSaveForPolicy(policy, delta), + saveKey: accretive ? `${staticHashKey}-${buildContentHash(afterEntries)}` : staticHashKey, + delta + }; + } + return { version: 1, domains }; +}; +var writeKeysFile = (paths, plan) => { + const filePath = import_node_path8.default.join(paths.root, KEYS_FILENAME); + try { + import_node_fs6.default.mkdirSync(paths.root, { recursive: true }); + import_node_fs6.default.writeFileSync(filePath, JSON.stringify(plan, null, 2)); + } catch (error) { + cacheLogger.warn(`Failed to write cache keys file "${filePath}".`, error); + } +}; + +// ../cache/dist/warmth.js +var import_node_fs7 = __toESM(require("fs"), 1); +var cacheLogger2 = logger.child("cache"); +var isDomainWarm = (domain, paths) => { + const directory = getWarmthDirectory(domain, paths); + if (!directory) { + return false; + } + try { + const stat = import_node_fs7.default.statSync(directory); + if (!stat.isDirectory()) { + return false; + } + return import_node_fs7.default.readdirSync(directory).length > 0; + } catch (error) { + if (isMissingPathError(error)) { + return false; + } + cacheLogger2.warn(`Failed to check cache warmth for "${domain}" at "${directory}". Treating as cold.`, error); + return false; + } +}; +var getWarmthDirectory = (domain, paths) => { + switch (domain) { + case "metro": + return paths.metroTransform; + default: + return void 0; + } +}; +var isMissingPathError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT"; + +// ../cache/dist/domains/metro.js +var import_node_crypto2 = require("crypto"); +var import_node_fs8 = __toESM(require("fs"), 1); +var import_node_path9 = __toESM(require("path"), 1); +var cacheLogger3 = logger.child("cache"); +var LOCKFILE_NAMES = [ + "bun.lock", + "bun.lockb", + "package-lock.json", + "npm-shrinkwrap.json", + "pnpm-lock.yaml", + "yarn.lock" +]; +var METRO_CONFIG_EXTENSIONS = [ + "js", + "cjs", + "mjs", + "ts", + "cts", + "mts", + "json" +]; +var METRO_CONFIG_NAMES = METRO_CONFIG_EXTENSIONS.map((extension) => `metro.config.${extension}`); +var METRO_CONFIG_DIR_NAMES = METRO_CONFIG_EXTENSIONS.map((extension) => `.config/metro.${extension}`); +var BABEL_CONFIG_NAMES = [ + "babel.config.json", + "babel.config.js", + "babel.config.cjs", + "babel.config.mjs", + "babel.config.cts" +]; +var METRO_KEY_FILENAMES = [ + ...LOCKFILE_NAMES, + ...METRO_CONFIG_NAMES, + ...METRO_CONFIG_DIR_NAMES, + ...BABEL_CONFIG_NAMES +]; +var collectAncestorDirectories = (projectRoot, repoRoot) => { + const resolvedProjectRoot = import_node_path9.default.resolve(projectRoot); + const resolvedRepoRoot = import_node_path9.default.resolve(repoRoot); + if (resolvedProjectRoot === resolvedRepoRoot) { + return [resolvedProjectRoot]; + } + const relative = import_node_path9.default.relative(resolvedRepoRoot, resolvedProjectRoot); + const isProjectRootInsideRepoRoot = relative !== "" && !relative.startsWith(`..${import_node_path9.default.sep}`) && relative !== ".." && !import_node_path9.default.isAbsolute(relative); + if (!isProjectRootInsideRepoRoot) { + return [resolvedProjectRoot]; + } + const directories = []; + let current = resolvedProjectRoot; + for (; ; ) { + directories.push(current); + if (current === resolvedRepoRoot) { + break; + } + const parent = import_node_path9.default.dirname(current); + if (parent === current) { + break; + } + current = parent; + } + return directories; +}; +var collectKeyFiles = (directories, repoRoot) => { + const matches = []; + for (const directory of directories) { + for (const candidate of METRO_KEY_FILENAMES) { + const fullPath = import_node_path9.default.join(directory, candidate); + if (!import_node_fs8.default.existsSync(fullPath) || !import_node_fs8.default.statSync(fullPath).isFile()) { + continue; + } + matches.push({ + relativePath: import_node_path9.default.relative(repoRoot, fullPath).split(import_node_path9.default.sep).join("/"), + content: import_node_fs8.default.readFileSync(fullPath) + }); + } + } + return matches; +}; +var hashKeyFiles = (matches) => { + const hash = (0, import_node_crypto2.createHash)("sha256"); + for (const match of matches.slice().sort((a, b) => a.relativePath < b.relativePath ? -1 : 1)) { + hash.update(match.relativePath); + hash.update("\0"); + hash.update(match.content); + hash.update("\0"); + } + return hash.digest("hex"); +}; +var computeMetroStaticInputs = (options) => { + try { + const resolvedProjectRoot = import_node_path9.default.resolve(options.projectRoot); + const resolvedRepoRoot = import_node_path9.default.resolve(options.repoRoot); + if (!import_node_fs8.default.statSync(resolvedProjectRoot).isDirectory()) { + throw new Error(`projectRoot "${resolvedProjectRoot}" is not a directory`); + } + if (!import_node_fs8.default.statSync(resolvedRepoRoot).isDirectory()) { + throw new Error(`repoRoot "${resolvedRepoRoot}" is not a directory`); + } + const directories = collectAncestorDirectories(resolvedProjectRoot, resolvedRepoRoot); + const matches = collectKeyFiles(directories, resolvedRepoRoot); + return { + lockfileHash: hashKeyFiles(matches), + bundlerMetroVersion: options.bundlerMetroVersion, + ...options.salt ? { salt: options.salt } : {} + }; + } catch (error) { + cacheLogger3.warn(`Failed to compute Metro cache key inputs for "${options.projectRoot}". Falling back to an always-miss key.`, error); + return { + lockfileHash: "unavailable", + bundlerMetroVersion: options.bundlerMetroVersion + }; + } +}; + +// ../cache/dist/index.js +var cacheLogger4 = logger.child("cache"); +var createHarnessCache = (options) => { + const paths = createHarnessCachePaths(options.projectRoot); + return { + paths, + isWarm: (domain) => isDomainWarm(domain, paths), + ensureDomainDirectories: (domain) => { + for (const directory of getDomainDirectories(domain, paths)) { + try { + import_node_fs9.default.mkdirSync(directory, { recursive: true }); + } catch (error) { + cacheLogger4.warn(`Failed to create cache directory "${directory}". Continuing with a cold cache.`, error); + } + } + }, + planRestore: (inputs) => planRestore(paths, inputs), + snapshot: () => snapshot(paths), + planSave: (before, policy, inputs) => planSave(paths, before, policy, inputs), + writeKeysFile: (plan) => writeKeysFile(paths, plan) + }; +}; + +// ../../node_modules/zod/dist/esm/v3/external.js +var external_exports = {}; +__export(external_exports, { + BRAND: () => BRAND, + DIRTY: () => DIRTY, + EMPTY_PATH: () => EMPTY_PATH, + INVALID: () => INVALID, + NEVER: () => NEVER, + OK: () => OK, + ParseStatus: () => ParseStatus, + Schema: () => ZodType, + ZodAny: () => ZodAny, + ZodArray: () => ZodArray, + ZodBigInt: () => ZodBigInt, + ZodBoolean: () => ZodBoolean, + ZodBranded: () => ZodBranded, + ZodCatch: () => ZodCatch, + ZodDate: () => ZodDate, + ZodDefault: () => ZodDefault, + ZodDiscriminatedUnion: () => ZodDiscriminatedUnion, + ZodEffects: () => ZodEffects, + ZodEnum: () => ZodEnum, + ZodError: () => ZodError, + ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind, + ZodFunction: () => ZodFunction, + ZodIntersection: () => ZodIntersection, + ZodIssueCode: () => ZodIssueCode, + ZodLazy: () => ZodLazy, + ZodLiteral: () => ZodLiteral, + ZodMap: () => ZodMap, + ZodNaN: () => ZodNaN, + ZodNativeEnum: () => ZodNativeEnum, + ZodNever: () => ZodNever, + ZodNull: () => ZodNull, + ZodNullable: () => ZodNullable, + ZodNumber: () => ZodNumber, + ZodObject: () => ZodObject, + ZodOptional: () => ZodOptional, + ZodParsedType: () => ZodParsedType, + ZodPipeline: () => ZodPipeline, + ZodPromise: () => ZodPromise, + ZodReadonly: () => ZodReadonly, + ZodRecord: () => ZodRecord, + ZodSchema: () => ZodType, + ZodSet: () => ZodSet, + ZodString: () => ZodString, + ZodSymbol: () => ZodSymbol, + ZodTransformer: () => ZodEffects, + ZodTuple: () => ZodTuple, + ZodType: () => ZodType, + ZodUndefined: () => ZodUndefined, + ZodUnion: () => ZodUnion, + ZodUnknown: () => ZodUnknown, + ZodVoid: () => ZodVoid, + addIssueToContext: () => addIssueToContext, + any: () => anyType, + array: () => arrayType, + bigint: () => bigIntType, + boolean: () => booleanType, + coerce: () => coerce, + custom: () => custom, + date: () => dateType, + datetimeRegex: () => datetimeRegex, + defaultErrorMap: () => en_default, + discriminatedUnion: () => discriminatedUnionType, + effect: () => effectsType, + enum: () => enumType, + function: () => functionType, + getErrorMap: () => getErrorMap, + getParsedType: () => getParsedType, + instanceof: () => instanceOfType, + intersection: () => intersectionType, + isAborted: () => isAborted, + isAsync: () => isAsync, + isDirty: () => isDirty, + isValid: () => isValid, + late: () => late, + lazy: () => lazyType, + literal: () => literalType, + makeIssue: () => makeIssue, + map: () => mapType, + nan: () => nanType, + nativeEnum: () => nativeEnumType, + never: () => neverType, + null: () => nullType, + nullable: () => nullableType, + number: () => numberType, + object: () => objectType, + objectUtil: () => objectUtil, + oboolean: () => oboolean, + onumber: () => onumber, + optional: () => optionalType, + ostring: () => ostring, + pipeline: () => pipelineType, + preprocess: () => preprocessType, + promise: () => promiseType, + quotelessJson: () => quotelessJson, + record: () => recordType, + set: () => setType, + setErrorMap: () => setErrorMap, + strictObject: () => strictObjectType, + string: () => stringType, + symbol: () => symbolType, + transformer: () => effectsType, + tuple: () => tupleType, + undefined: () => undefinedType, + union: () => unionType, + unknown: () => unknownType, + util: () => util2, + void: () => voidType +}); + +// ../../node_modules/zod/dist/esm/v3/helpers/util.js +var util2; +(function(util3) { + util3.assertEqual = (_2) => { + }; + function assertIs(_arg) { + } + util3.assertIs = assertIs; + function assertNever(_x) { + throw new Error(); + } + util3.assertNever = assertNever; + util3.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util3.getValidEnumValues = (obj) => { + const validKeys = util3.objectKeys(obj).filter((k3) => typeof obj[obj[k3]] !== "number"); + const filtered = {}; + for (const k3 of validKeys) { + filtered[k3] = obj[k3]; + } + return util3.objectValues(filtered); + }; + util3.objectValues = (obj) => { + return util3.objectKeys(obj).map(function(e) { + return obj[e]; + }); + }; + util3.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => { + const keys = []; + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + keys.push(key); + } + } + return keys; + }; + util3.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return void 0; + }; + util3.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val; + function joinValues(array, separator = " | ") { + return array.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator); + } + util3.joinValues = joinValues; + util3.jsonStringifyReplacer = (_2, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util2 || (util2 = {})); +var objectUtil; +(function(objectUtil2) { + objectUtil2.mergeShapes = (first, second) => { + return { + ...first, + ...second + // second overwrites first + }; + }; +})(objectUtil || (objectUtil = {})); +var ZodParsedType = util2.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set" +]); +var getParsedType = (data) => { + const t2 = typeof data; + switch (t2) { + case "undefined": + return ZodParsedType.undefined; + case "string": + return ZodParsedType.string; + case "number": + return Number.isNaN(data) ? ZodParsedType.nan : ZodParsedType.number; + case "boolean": + return ZodParsedType.boolean; + case "function": + return ZodParsedType.function; + case "bigint": + return ZodParsedType.bigint; + case "symbol": + return ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return ZodParsedType.array; + } + if (data === null) { + return ZodParsedType.null; + } + if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") { + return ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return ZodParsedType.date; + } + return ZodParsedType.object; + default: + return ZodParsedType.unknown; + } +}; + +// ../../node_modules/zod/dist/esm/v3/ZodError.js +var ZodIssueCode = util2.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite" +]); +var quotelessJson = (obj) => { + const json = JSON.stringify(obj, null, 2); + return json.replace(/"([^"]+)":/g, "$1:"); +}; +var ZodError = class _ZodError extends Error { + get errors() { + return this.issues; + } + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(this, actualProto); + } else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + format(_mapper) { + const mapper = _mapper || function(issue) { + return issue.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error) => { + for (const issue of error.issues) { + if (issue.code === "invalid_union") { + issue.unionErrors.map(processError); + } else if (issue.code === "invalid_return_type") { + processError(issue.returnTypeError); + } else if (issue.code === "invalid_arguments") { + processError(issue.argumentsError); + } else if (issue.path.length === 0) { + fieldErrors._errors.push(mapper(issue)); + } else { + let curr = fieldErrors; + let i = 0; + while (i < issue.path.length) { + const el = issue.path[i]; + const terminal = i === issue.path.length - 1; + if (!terminal) { + curr[el] = curr[el] || { _errors: [] }; + } else { + curr[el] = curr[el] || { _errors: [] }; + curr[el]._errors.push(mapper(issue)); + } + curr = curr[el]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + static assert(value) { + if (!(value instanceof _ZodError)) { + throw new Error(`Not a ZodError: ${value}`); + } + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util2.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue) => issue.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +}; +ZodError.create = (issues) => { + const error = new ZodError(issues); + return error; +}; + +// ../../node_modules/zod/dist/esm/v3/locales/en.js +var errorMap = (issue, _ctx) => { + let message; + switch (issue.code) { + case ZodIssueCode.invalid_type: + if (issue.received === ZodParsedType.undefined) { + message = "Required"; + } else { + message = `Expected ${issue.expected}, received ${issue.received}`; + } + break; + case ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util2.jsonStringifyReplacer)}`; + break; + case ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util2.joinValues(issue.keys, ", ")}`; + break; + case ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util2.joinValues(issue.options)}`; + break; + case ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util2.joinValues(issue.options)}, received '${issue.received}'`; + break; + case ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodIssueCode.invalid_string: + if (typeof issue.validation === "object") { + if ("includes" in issue.validation) { + message = `Invalid input: must include "${issue.validation.includes}"`; + if (typeof issue.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`; + } + } else if ("startsWith" in issue.validation) { + message = `Invalid input: must start with "${issue.validation.startsWith}"`; + } else if ("endsWith" in issue.validation) { + message = `Invalid input: must end with "${issue.validation.endsWith}"`; + } else { + util2.assertNever(issue.validation); + } + } else if (issue.validation !== "regex") { + message = `Invalid ${issue.validation}`; + } else { + message = "Invalid"; + } + break; + case ZodIssueCode.too_small: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue.minimum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.too_big: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`; + else if (issue.type === "bigint") + message = `BigInt must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue.maximum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue.multipleOf}`; + break; + case ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util2.assertNever(issue); + } + return { message }; +}; +var en_default = errorMap; + +// ../../node_modules/zod/dist/esm/v3/errors.js +var overrideErrorMap = en_default; +function setErrorMap(map) { + overrideErrorMap = map; +} +function getErrorMap() { + return overrideErrorMap; +} + +// ../../node_modules/zod/dist/esm/v3/helpers/parseUtil.js +var makeIssue = (params) => { + const { data, path: path12, errorMaps, issueData } = params; + const fullPath = [...path12, ...issueData.path || []]; + const fullIssue = { + ...issueData, + path: fullPath + }; + if (issueData.message !== void 0) { + return { + ...issueData, + path: fullPath, + message: issueData.message + }; + } + let errorMessage = ""; + const maps = errorMaps.filter((m) => !!m).slice().reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: errorMessage + }; +}; +var EMPTY_PATH = []; +function addIssueToContext(ctx, issueData) { + const overrideMap = getErrorMap(); + const issue = makeIssue({ + issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + // contextual error map is first priority + ctx.schemaErrorMap, + // then schema-bound map if available + overrideMap, + // then global override map + overrideMap === en_default ? void 0 : en_default + // then global default map + ].filter((x2) => !!x2) + }); + ctx.common.issues.push(issue); +} +var ParseStatus = class _ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s of results) { + if (s.status === "aborted") + return INVALID; + if (s.status === "dirty") + status.dirty(); + arrayValue.push(s.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value + }); + } + return _ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return INVALID; + if (value.status === "aborted") + return INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +}; +var INVALID = Object.freeze({ + status: "aborted" +}); +var DIRTY = (value) => ({ status: "dirty", value }); +var OK = (value) => ({ status: "valid", value }); +var isAborted = (x2) => x2.status === "aborted"; +var isDirty = (x2) => x2.status === "dirty"; +var isValid = (x2) => x2.status === "valid"; +var isAsync = (x2) => typeof Promise !== "undefined" && x2 instanceof Promise; + +// ../../node_modules/zod/dist/esm/v3/helpers/errorUtil.js +var errorUtil; +(function(errorUtil2) { + errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message; +})(errorUtil || (errorUtil = {})); + +// ../../node_modules/zod/dist/esm/v3/types.js +var ParseInputLazyPath = class { + constructor(parent, value, path12, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path12; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (Array.isArray(this._key)) { + this._cachedPath.push(...this._path, ...this._key); + } else { + this._cachedPath.push(...this._path, this._key); + } + } + return this._cachedPath; + } +}; +var handleResult = (ctx, result) => { + if (isValid(result)) { + return { success: true, data: result.value }; + } else { + if (!ctx.common.issues.length) { + throw new Error("Validation failed but no issues detected."); + } + return { + success: false, + get error() { + if (this._error) + return this._error; + const error = new ZodError(ctx.common.issues); + this._error = error; + return this._error; + } + }; + } +}; +function processCreateParams(params) { + if (!params) + return {}; + const { errorMap: errorMap2, invalid_type_error, required_error, description } = params; + if (errorMap2 && (invalid_type_error || required_error)) { + throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); + } + if (errorMap2) + return { errorMap: errorMap2, description }; + const customMap = (iss, ctx) => { + const { message } = params; + if (iss.code === "invalid_enum_value") { + return { message: message ?? ctx.defaultError }; + } + if (typeof ctx.data === "undefined") { + return { message: message ?? required_error ?? ctx.defaultError }; + } + if (iss.code !== "invalid_type") + return { message: ctx.defaultError }; + return { message: message ?? invalid_type_error ?? ctx.defaultError }; + }; + return { errorMap: customMap, description }; +} +var ZodType = class { + get description() { + return this._def.description; + } + _getType(input) { + return getParsedType(input.data); + } + _getOrReturnCtx(input, ctx) { + return ctx || { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + }; + } + _processInputParams(input) { + return { + status: new ParseStatus(), + ctx: { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + } + }; + } + _parseSync(input) { + const result = this._parse(input); + if (isAsync(result)) { + throw new Error("Synchronous parse encountered promise."); + } + return result; + } + _parseAsync(input) { + const result = this._parse(input); + return Promise.resolve(result); + } + parse(data, params) { + const result = this.safeParse(data, params); + if (result.success) + return result.data; + throw result.error; + } + safeParse(data, params) { + const ctx = { + common: { + issues: [], + async: params?.async ?? false, + contextualErrorMap: params?.errorMap + }, + path: params?.path || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + const result = this._parseSync({ data, path: ctx.path, parent: ctx }); + return handleResult(ctx, result); + } + "~validate"(data) { + const ctx = { + common: { + issues: [], + async: !!this["~standard"].async + }, + path: [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + if (!this["~standard"].async) { + try { + const result = this._parseSync({ data, path: [], parent: ctx }); + return isValid(result) ? { + value: result.value + } : { + issues: ctx.common.issues + }; + } catch (err) { + if (err?.message?.toLowerCase()?.includes("encountered")) { + this["~standard"].async = true; + } + ctx.common = { + issues: [], + async: true + }; + } + } + return this._parseAsync({ data, path: [], parent: ctx }).then((result) => isValid(result) ? { + value: result.value + } : { + issues: ctx.common.issues + }); + } + async parseAsync(data, params) { + const result = await this.safeParseAsync(data, params); + if (result.success) + return result.data; + throw result.error; + } + async safeParseAsync(data, params) { + const ctx = { + common: { + issues: [], + contextualErrorMap: params?.errorMap, + async: true + }, + path: params?.path || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx }); + const result = await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult)); + return handleResult(ctx, result); + } + refine(check, message) { + const getIssueProperties = (val) => { + if (typeof message === "string" || typeof message === "undefined") { + return { message }; + } else if (typeof message === "function") { + return message(val); + } else { + return message; + } + }; + return this._refinement((val, ctx) => { + const result = check(val); + const setError = () => ctx.addIssue({ + code: ZodIssueCode.custom, + ...getIssueProperties(val) + }); + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then((data) => { + if (!data) { + setError(); + return false; + } else { + return true; + } + }); + } + if (!result) { + setError(); + return false; + } else { + return true; + } + }); + } + refinement(check, refinementData) { + return this._refinement((val, ctx) => { + if (!check(val)) { + ctx.addIssue(typeof refinementData === "function" ? refinementData(val, ctx) : refinementData); + return false; + } else { + return true; + } + }); + } + _refinement(refinement) { + return new ZodEffects({ + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "refinement", refinement } + }); + } + superRefine(refinement) { + return this._refinement(refinement); + } + constructor(def) { + this.spa = this.safeParseAsync; + this._def = def; + this.parse = this.parse.bind(this); + this.safeParse = this.safeParse.bind(this); + this.parseAsync = this.parseAsync.bind(this); + this.safeParseAsync = this.safeParseAsync.bind(this); + this.spa = this.spa.bind(this); + this.refine = this.refine.bind(this); + this.refinement = this.refinement.bind(this); + this.superRefine = this.superRefine.bind(this); + this.optional = this.optional.bind(this); + this.nullable = this.nullable.bind(this); + this.nullish = this.nullish.bind(this); + this.array = this.array.bind(this); + this.promise = this.promise.bind(this); + this.or = this.or.bind(this); + this.and = this.and.bind(this); + this.transform = this.transform.bind(this); + this.brand = this.brand.bind(this); + this.default = this.default.bind(this); + this.catch = this.catch.bind(this); + this.describe = this.describe.bind(this); + this.pipe = this.pipe.bind(this); + this.readonly = this.readonly.bind(this); + this.isNullable = this.isNullable.bind(this); + this.isOptional = this.isOptional.bind(this); + this["~standard"] = { + version: 1, + vendor: "zod", + validate: (data) => this["~validate"](data) + }; + } + optional() { + return ZodOptional.create(this, this._def); + } + nullable() { + return ZodNullable.create(this, this._def); + } + nullish() { + return this.nullable().optional(); + } + array() { + return ZodArray.create(this); + } + promise() { + return ZodPromise.create(this, this._def); + } + or(option) { + return ZodUnion.create([this, option], this._def); + } + and(incoming) { + return ZodIntersection.create(this, incoming, this._def); + } + transform(transform) { + return new ZodEffects({ + ...processCreateParams(this._def), + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "transform", transform } + }); + } + default(def) { + const defaultValueFunc = typeof def === "function" ? def : () => def; + return new ZodDefault({ + ...processCreateParams(this._def), + innerType: this, + defaultValue: defaultValueFunc, + typeName: ZodFirstPartyTypeKind.ZodDefault + }); + } + brand() { + return new ZodBranded({ + typeName: ZodFirstPartyTypeKind.ZodBranded, + type: this, + ...processCreateParams(this._def) + }); + } + catch(def) { + const catchValueFunc = typeof def === "function" ? def : () => def; + return new ZodCatch({ + ...processCreateParams(this._def), + innerType: this, + catchValue: catchValueFunc, + typeName: ZodFirstPartyTypeKind.ZodCatch + }); + } + describe(description) { + const This = this.constructor; + return new This({ + ...this._def, + description + }); + } + pipe(target) { + return ZodPipeline.create(this, target); + } + readonly() { + return ZodReadonly.create(this); + } + isOptional() { + return this.safeParse(void 0).success; + } + isNullable() { + return this.safeParse(null).success; + } +}; +var cuidRegex = /^c[^\s-]{8,}$/i; +var cuid2Regex = /^[0-9a-z]+$/; +var ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; +var uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i; +var nanoidRegex = /^[a-z0-9_-]{21}$/i; +var jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/; +var durationRegex = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/; +var emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i; +var _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`; +var emojiRegex; +var ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; +var ipv4CidrRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/; +var ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; +var ipv6CidrRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/; +var base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; +var base64urlRegex = /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/; +var dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`; +var dateRegex = new RegExp(`^${dateRegexSource}$`); +function timeRegexSource(args) { + let secondsRegexSource = `[0-5]\\d`; + if (args.precision) { + secondsRegexSource = `${secondsRegexSource}\\.\\d{${args.precision}}`; + } else if (args.precision == null) { + secondsRegexSource = `${secondsRegexSource}(\\.\\d+)?`; + } + const secondsQuantifier = args.precision ? "+" : "?"; + return `([01]\\d|2[0-3]):[0-5]\\d(:${secondsRegexSource})${secondsQuantifier}`; +} +function timeRegex(args) { + return new RegExp(`^${timeRegexSource(args)}$`); +} +function datetimeRegex(args) { + let regex = `${dateRegexSource}T${timeRegexSource(args)}`; + const opts = []; + opts.push(args.local ? `Z?` : `Z`); + if (args.offset) + opts.push(`([+-]\\d{2}:?\\d{2})`); + regex = `${regex}(${opts.join("|")})`; + return new RegExp(`^${regex}$`); +} +function isValidIP(ip, version) { + if ((version === "v4" || !version) && ipv4Regex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6Regex.test(ip)) { + return true; + } + return false; +} +function isValidJWT(jwt, alg) { + if (!jwtRegex.test(jwt)) + return false; + try { + const [header] = jwt.split("."); + const base64 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "="); + const decoded = JSON.parse(atob(base64)); + if (typeof decoded !== "object" || decoded === null) + return false; + if ("typ" in decoded && decoded?.typ !== "JWT") + return false; + if (!decoded.alg) + return false; + if (alg && decoded.alg !== alg) + return false; + return true; + } catch { + return false; + } +} +function isValidCidr(ip, version) { + if ((version === "v4" || !version) && ipv4CidrRegex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6CidrRegex.test(ip)) { + return true; + } + return false; +} +var ZodString = class _ZodString extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = String(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.string) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.string, + received: ctx2.parsedType + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.length < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + if (input.data.length > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "length") { + const tooBig = input.data.length > check.value; + const tooSmall = input.data.length < check.value; + if (tooBig || tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + if (tooBig) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message + }); + } else if (tooSmall) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message + }); + } + status.dirty(); + } + } else if (check.kind === "email") { + if (!emailRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "email", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "emoji") { + if (!emojiRegex) { + emojiRegex = new RegExp(_emojiRegex, "u"); + } + if (!emojiRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "emoji", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "uuid") { + if (!uuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "uuid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "nanoid") { + if (!nanoidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "nanoid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cuid") { + if (!cuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cuid2") { + if (!cuid2Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid2", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "ulid") { + if (!ulidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ulid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "url") { + try { + new URL(input.data); + } catch { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "url", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "regex") { + check.regex.lastIndex = 0; + const testResult = check.regex.test(input.data); + if (!testResult) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "regex", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "trim") { + input.data = input.data.trim(); + } else if (check.kind === "includes") { + if (!input.data.includes(check.value, check.position)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { includes: check.value, position: check.position }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "toLowerCase") { + input.data = input.data.toLowerCase(); + } else if (check.kind === "toUpperCase") { + input.data = input.data.toUpperCase(); + } else if (check.kind === "startsWith") { + if (!input.data.startsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { startsWith: check.value }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "endsWith") { + if (!input.data.endsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { endsWith: check.value }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "datetime") { + const regex = datetimeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "datetime", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "date") { + const regex = dateRegex; + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "date", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "time") { + const regex = timeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "time", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "duration") { + if (!durationRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "duration", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "ip") { + if (!isValidIP(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ip", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "jwt") { + if (!isValidJWT(input.data, check.alg)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "jwt", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cidr") { + if (!isValidCidr(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cidr", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "base64") { + if (!base64Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "base64url") { + if (!base64urlRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64url", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _regex(regex, validation, message) { + return this.refinement((data) => regex.test(data), { + validation, + code: ZodIssueCode.invalid_string, + ...errorUtil.errToObj(message) + }); + } + _addCheck(check) { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + email(message) { + return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) }); + } + url(message) { + return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) }); + } + emoji(message) { + return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) }); + } + uuid(message) { + return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) }); + } + nanoid(message) { + return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(message) }); + } + cuid(message) { + return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) }); + } + cuid2(message) { + return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) }); + } + ulid(message) { + return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) }); + } + base64(message) { + return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message) }); + } + base64url(message) { + return this._addCheck({ + kind: "base64url", + ...errorUtil.errToObj(message) + }); + } + jwt(options) { + return this._addCheck({ kind: "jwt", ...errorUtil.errToObj(options) }); + } + ip(options) { + return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) }); + } + cidr(options) { + return this._addCheck({ kind: "cidr", ...errorUtil.errToObj(options) }); + } + datetime(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "datetime", + precision: null, + offset: false, + local: false, + message: options + }); + } + return this._addCheck({ + kind: "datetime", + precision: typeof options?.precision === "undefined" ? null : options?.precision, + offset: options?.offset ?? false, + local: options?.local ?? false, + ...errorUtil.errToObj(options?.message) + }); + } + date(message) { + return this._addCheck({ kind: "date", message }); + } + time(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "time", + precision: null, + message: options + }); + } + return this._addCheck({ + kind: "time", + precision: typeof options?.precision === "undefined" ? null : options?.precision, + ...errorUtil.errToObj(options?.message) + }); + } + duration(message) { + return this._addCheck({ kind: "duration", ...errorUtil.errToObj(message) }); + } + regex(regex, message) { + return this._addCheck({ + kind: "regex", + regex, + ...errorUtil.errToObj(message) + }); + } + includes(value, options) { + return this._addCheck({ + kind: "includes", + value, + position: options?.position, + ...errorUtil.errToObj(options?.message) + }); + } + startsWith(value, message) { + return this._addCheck({ + kind: "startsWith", + value, + ...errorUtil.errToObj(message) + }); + } + endsWith(value, message) { + return this._addCheck({ + kind: "endsWith", + value, + ...errorUtil.errToObj(message) + }); + } + min(minLength, message) { + return this._addCheck({ + kind: "min", + value: minLength, + ...errorUtil.errToObj(message) + }); + } + max(maxLength, message) { + return this._addCheck({ + kind: "max", + value: maxLength, + ...errorUtil.errToObj(message) + }); + } + length(len, message) { + return this._addCheck({ + kind: "length", + value: len, + ...errorUtil.errToObj(message) + }); + } + /** + * Equivalent to `.min(1)` + */ + nonempty(message) { + return this.min(1, errorUtil.errToObj(message)); + } + trim() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "trim" }] + }); + } + toLowerCase() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toLowerCase" }] + }); + } + toUpperCase() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toUpperCase" }] + }); + } + get isDatetime() { + return !!this._def.checks.find((ch) => ch.kind === "datetime"); + } + get isDate() { + return !!this._def.checks.find((ch) => ch.kind === "date"); + } + get isTime() { + return !!this._def.checks.find((ch) => ch.kind === "time"); + } + get isDuration() { + return !!this._def.checks.find((ch) => ch.kind === "duration"); + } + get isEmail() { + return !!this._def.checks.find((ch) => ch.kind === "email"); + } + get isURL() { + return !!this._def.checks.find((ch) => ch.kind === "url"); + } + get isEmoji() { + return !!this._def.checks.find((ch) => ch.kind === "emoji"); + } + get isUUID() { + return !!this._def.checks.find((ch) => ch.kind === "uuid"); + } + get isNANOID() { + return !!this._def.checks.find((ch) => ch.kind === "nanoid"); + } + get isCUID() { + return !!this._def.checks.find((ch) => ch.kind === "cuid"); + } + get isCUID2() { + return !!this._def.checks.find((ch) => ch.kind === "cuid2"); + } + get isULID() { + return !!this._def.checks.find((ch) => ch.kind === "ulid"); + } + get isIP() { + return !!this._def.checks.find((ch) => ch.kind === "ip"); + } + get isCIDR() { + return !!this._def.checks.find((ch) => ch.kind === "cidr"); + } + get isBase64() { + return !!this._def.checks.find((ch) => ch.kind === "base64"); + } + get isBase64url() { + return !!this._def.checks.find((ch) => ch.kind === "base64url"); + } + get minLength() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxLength() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodString.create = (params) => { + return new ZodString({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodString, + coerce: params?.coerce ?? false, + ...processCreateParams(params) + }); +}; +function floatSafeRemainder(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = Number.parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", "")); + return valInt % stepInt / 10 ** decCount; +} +var ZodNumber = class _ZodNumber extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + this.step = this.multipleOf; + } + _parse(input) { + if (this._def.coerce) { + input.data = Number(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.number) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.number, + received: ctx2.parsedType + }); + return INVALID; + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "int") { + if (!util2.isInteger(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: "integer", + received: "float", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "min") { + const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "multipleOf") { + if (floatSafeRemainder(input.data, check.value) !== 0) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "finite") { + if (!Number.isFinite(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_finite, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodNumber({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check) { + return new _ZodNumber({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + int(message) { + return this._addCheck({ + kind: "int", + message: errorUtil.toString(message) + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + finite(message) { + return this._addCheck({ + kind: "finite", + message: errorUtil.toString(message) + }); + } + safe(message) { + return this._addCheck({ + kind: "min", + inclusive: true, + value: Number.MIN_SAFE_INTEGER, + message: errorUtil.toString(message) + })._addCheck({ + kind: "max", + inclusive: true, + value: Number.MAX_SAFE_INTEGER, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } + get isInt() { + return !!this._def.checks.find((ch) => ch.kind === "int" || ch.kind === "multipleOf" && util2.isInteger(ch.value)); + } + get isFinite() { + let max = null; + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "finite" || ch.kind === "int" || ch.kind === "multipleOf") { + return true; + } else if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } else if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return Number.isFinite(min) && Number.isFinite(max); + } +}; +ZodNumber.create = (params) => { + return new ZodNumber({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodNumber, + coerce: params?.coerce || false, + ...processCreateParams(params) + }); +}; +var ZodBigInt = class _ZodBigInt extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + } + _parse(input) { + if (this._def.coerce) { + try { + input.data = BigInt(input.data); + } catch { + return this._getInvalidInput(input); + } + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.bigint) { + return this._getInvalidInput(input); + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "min") { + const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + type: "bigint", + minimum: check.value, + inclusive: check.inclusive, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + type: "bigint", + maximum: check.value, + inclusive: check.inclusive, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "multipleOf") { + if (input.data % check.value !== BigInt(0)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _getInvalidInput(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.bigint, + received: ctx.parsedType + }); + return INVALID; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodBigInt({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check) { + return new _ZodBigInt({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodBigInt.create = (params) => { + return new ZodBigInt({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodBigInt, + coerce: params?.coerce ?? false, + ...processCreateParams(params) + }); +}; +var ZodBoolean = class extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = Boolean(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.boolean) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.boolean, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodBoolean.create = (params) => { + return new ZodBoolean({ + typeName: ZodFirstPartyTypeKind.ZodBoolean, + coerce: params?.coerce || false, + ...processCreateParams(params) + }); +}; +var ZodDate = class _ZodDate extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = new Date(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.date) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.date, + received: ctx2.parsedType + }); + return INVALID; + } + if (Number.isNaN(input.data.getTime())) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_date + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.getTime() < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + message: check.message, + inclusive: true, + exact: false, + minimum: check.value, + type: "date" + }); + status.dirty(); + } + } else if (check.kind === "max") { + if (input.data.getTime() > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + message: check.message, + inclusive: true, + exact: false, + maximum: check.value, + type: "date" + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { + status: status.value, + value: new Date(input.data.getTime()) + }; + } + _addCheck(check) { + return new _ZodDate({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + min(minDate, message) { + return this._addCheck({ + kind: "min", + value: minDate.getTime(), + message: errorUtil.toString(message) + }); + } + max(maxDate, message) { + return this._addCheck({ + kind: "max", + value: maxDate.getTime(), + message: errorUtil.toString(message) + }); + } + get minDate() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min != null ? new Date(min) : null; + } + get maxDate() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max != null ? new Date(max) : null; + } +}; +ZodDate.create = (params) => { + return new ZodDate({ + checks: [], + coerce: params?.coerce || false, + typeName: ZodFirstPartyTypeKind.ZodDate, + ...processCreateParams(params) + }); +}; +var ZodSymbol = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodSymbol.create = (params) => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params) + }); +}; +var ZodUndefined = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.undefined, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodUndefined.create = (params) => { + return new ZodUndefined({ + typeName: ZodFirstPartyTypeKind.ZodUndefined, + ...processCreateParams(params) + }); +}; +var ZodNull = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.null) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.null, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodNull.create = (params) => { + return new ZodNull({ + typeName: ZodFirstPartyTypeKind.ZodNull, + ...processCreateParams(params) + }); +}; +var ZodAny = class extends ZodType { + constructor() { + super(...arguments); + this._any = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodAny.create = (params) => { + return new ZodAny({ + typeName: ZodFirstPartyTypeKind.ZodAny, + ...processCreateParams(params) + }); +}; +var ZodUnknown = class extends ZodType { + constructor() { + super(...arguments); + this._unknown = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodUnknown.create = (params) => { + return new ZodUnknown({ + typeName: ZodFirstPartyTypeKind.ZodUnknown, + ...processCreateParams(params) + }); +}; +var ZodNever = class extends ZodType { + _parse(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.never, + received: ctx.parsedType + }); + return INVALID; + } +}; +ZodNever.create = (params) => { + return new ZodNever({ + typeName: ZodFirstPartyTypeKind.ZodNever, + ...processCreateParams(params) + }); +}; +var ZodVoid = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.void, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodVoid.create = (params) => { + return new ZodVoid({ + typeName: ZodFirstPartyTypeKind.ZodVoid, + ...processCreateParams(params) + }); +}; +var ZodArray = class _ZodArray extends ZodType { + _parse(input) { + const { ctx, status } = this._processInputParams(input); + const def = this._def; + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (def.exactLength !== null) { + const tooBig = ctx.data.length > def.exactLength.value; + const tooSmall = ctx.data.length < def.exactLength.value; + if (tooBig || tooSmall) { + addIssueToContext(ctx, { + code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small, + minimum: tooSmall ? def.exactLength.value : void 0, + maximum: tooBig ? def.exactLength.value : void 0, + type: "array", + inclusive: true, + exact: true, + message: def.exactLength.message + }); + status.dirty(); + } + } + if (def.minLength !== null) { + if (ctx.data.length < def.minLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.minLength.message + }); + status.dirty(); + } + } + if (def.maxLength !== null) { + if (ctx.data.length > def.maxLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.maxLength.message + }); + status.dirty(); + } + } + if (ctx.common.async) { + return Promise.all([...ctx.data].map((item, i) => { + return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + })).then((result2) => { + return ParseStatus.mergeArray(status, result2); + }); + } + const result = [...ctx.data].map((item, i) => { + return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + }); + return ParseStatus.mergeArray(status, result); + } + get element() { + return this._def.type; + } + min(minLength, message) { + return new _ZodArray({ + ...this._def, + minLength: { value: minLength, message: errorUtil.toString(message) } + }); + } + max(maxLength, message) { + return new _ZodArray({ + ...this._def, + maxLength: { value: maxLength, message: errorUtil.toString(message) } + }); + } + length(len, message) { + return new _ZodArray({ + ...this._def, + exactLength: { value: len, message: errorUtil.toString(message) } + }); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodArray.create = (schema, params) => { + return new ZodArray({ + type: schema, + minLength: null, + maxLength: null, + exactLength: null, + typeName: ZodFirstPartyTypeKind.ZodArray, + ...processCreateParams(params) + }); +}; +function deepPartialify(schema) { + if (schema instanceof ZodObject) { + const newShape = {}; + for (const key in schema.shape) { + const fieldSchema = schema.shape[key]; + newShape[key] = ZodOptional.create(deepPartialify(fieldSchema)); + } + return new ZodObject({ + ...schema._def, + shape: () => newShape + }); + } else if (schema instanceof ZodArray) { + return new ZodArray({ + ...schema._def, + type: deepPartialify(schema.element) + }); + } else if (schema instanceof ZodOptional) { + return ZodOptional.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodNullable) { + return ZodNullable.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodTuple) { + return ZodTuple.create(schema.items.map((item) => deepPartialify(item))); + } else { + return schema; + } +} +var ZodObject = class _ZodObject extends ZodType { + constructor() { + super(...arguments); + this._cached = null; + this.nonstrict = this.passthrough; + this.augment = this.extend; + } + _getCached() { + if (this._cached !== null) + return this._cached; + const shape = this._def.shape(); + const keys = util2.objectKeys(shape); + this._cached = { shape, keys }; + return this._cached; + } + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.object) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx2.parsedType + }); + return INVALID; + } + const { status, ctx } = this._processInputParams(input); + const { shape, keys: shapeKeys } = this._getCached(); + const extraKeys = []; + if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) { + for (const key in ctx.data) { + if (!shapeKeys.includes(key)) { + extraKeys.push(key); + } + } + } + const pairs = []; + for (const key of shapeKeys) { + const keyValidator = shape[key]; + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (this._def.catchall instanceof ZodNever) { + const unknownKeys = this._def.unknownKeys; + if (unknownKeys === "passthrough") { + for (const key of extraKeys) { + pairs.push({ + key: { status: "valid", value: key }, + value: { status: "valid", value: ctx.data[key] } + }); + } + } else if (unknownKeys === "strict") { + if (extraKeys.length > 0) { + addIssueToContext(ctx, { + code: ZodIssueCode.unrecognized_keys, + keys: extraKeys + }); + status.dirty(); + } + } else if (unknownKeys === "strip") { + } else { + throw new Error(`Internal ZodObject error: invalid unknownKeys value.`); + } + } else { + const catchall = this._def.catchall; + for (const key of extraKeys) { + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: catchall._parse( + new ParseInputLazyPath(ctx, value, ctx.path, key) + //, ctx.child(key), value, getParsedType(value) + ), + alwaysSet: key in ctx.data + }); + } + } + if (ctx.common.async) { + return Promise.resolve().then(async () => { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value, + alwaysSet: pair.alwaysSet + }); + } + return syncPairs; + }).then((syncPairs) => { + return ParseStatus.mergeObjectSync(status, syncPairs); + }); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get shape() { + return this._def.shape(); + } + strict(message) { + errorUtil.errToObj; + return new _ZodObject({ + ...this._def, + unknownKeys: "strict", + ...message !== void 0 ? { + errorMap: (issue, ctx) => { + const defaultError = this._def.errorMap?.(issue, ctx).message ?? ctx.defaultError; + if (issue.code === "unrecognized_keys") + return { + message: errorUtil.errToObj(message).message ?? defaultError + }; + return { + message: defaultError + }; + } + } : {} + }); + } + strip() { + return new _ZodObject({ + ...this._def, + unknownKeys: "strip" + }); + } + passthrough() { + return new _ZodObject({ + ...this._def, + unknownKeys: "passthrough" + }); + } + // const AugmentFactory = + // (def: Def) => + // ( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, Augmentation>, + // Def["unknownKeys"], + // Def["catchall"] + // > => { + // return new ZodObject({ + // ...def, + // shape: () => ({ + // ...def.shape(), + // ...augmentation, + // }), + // }) as any; + // }; + extend(augmentation) { + return new _ZodObject({ + ...this._def, + shape: () => ({ + ...this._def.shape(), + ...augmentation + }) + }); + } + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging) { + const merged = new _ZodObject({ + unknownKeys: merging._def.unknownKeys, + catchall: merging._def.catchall, + shape: () => ({ + ...this._def.shape(), + ...merging._def.shape() + }), + typeName: ZodFirstPartyTypeKind.ZodObject + }); + return merged; + } + // merge< + // Incoming extends AnyZodObject, + // Augmentation extends Incoming["shape"], + // NewOutput extends { + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }, + // NewInput extends { + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // } + // >( + // merging: Incoming + // ): ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"], + // NewOutput, + // NewInput + // > { + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + setKey(key, schema) { + return this.augment({ [key]: schema }); + } + // merge( + // merging: Incoming + // ): //ZodObject = (merging) => { + // ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"] + // > { + // // const mergedShape = objectUtil.mergeShapes( + // // this._def.shape(), + // // merging._def.shape() + // // ); + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + catchall(index) { + return new _ZodObject({ + ...this._def, + catchall: index + }); + } + pick(mask) { + const shape = {}; + for (const key of util2.objectKeys(mask)) { + if (mask[key] && this.shape[key]) { + shape[key] = this.shape[key]; + } + } + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + omit(mask) { + const shape = {}; + for (const key of util2.objectKeys(this.shape)) { + if (!mask[key]) { + shape[key] = this.shape[key]; + } + } + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + /** + * @deprecated + */ + deepPartial() { + return deepPartialify(this); + } + partial(mask) { + const newShape = {}; + for (const key of util2.objectKeys(this.shape)) { + const fieldSchema = this.shape[key]; + if (mask && !mask[key]) { + newShape[key] = fieldSchema; + } else { + newShape[key] = fieldSchema.optional(); + } + } + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + required(mask) { + const newShape = {}; + for (const key of util2.objectKeys(this.shape)) { + if (mask && !mask[key]) { + newShape[key] = this.shape[key]; + } else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = newField._def.innerType; + } + newShape[key] = newField; + } + } + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + keyof() { + return createZodEnum(util2.objectKeys(this.shape)); + } +}; +ZodObject.create = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject.strictCreate = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strict", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject.lazycreate = (shape, params) => { + return new ZodObject({ + shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +var ZodUnion = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const options = this._def.options; + function handleResults(results) { + for (const result of results) { + if (result.result.status === "valid") { + return result.result; + } + } + for (const result of results) { + if (result.result.status === "dirty") { + ctx.common.issues.push(...result.ctx.common.issues); + return result.result; + } + } + const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + if (ctx.common.async) { + return Promise.all(options.map(async (option) => { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + return { + result: await option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }), + ctx: childCtx + }; + })).then(handleResults); + } else { + let dirty = void 0; + const issues = []; + for (const option of options) { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }); + if (result.status === "valid") { + return result; + } else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + if (childCtx.common.issues.length) { + issues.push(childCtx.common.issues); + } + } + if (dirty) { + ctx.common.issues.push(...dirty.ctx.common.issues); + return dirty.result; + } + const unionErrors = issues.map((issues2) => new ZodError(issues2)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + } + get options() { + return this._def.options; + } +}; +ZodUnion.create = (types, params) => { + return new ZodUnion({ + options: types, + typeName: ZodFirstPartyTypeKind.ZodUnion, + ...processCreateParams(params) + }); +}; +var getDiscriminator = (type) => { + if (type instanceof ZodLazy) { + return getDiscriminator(type.schema); + } else if (type instanceof ZodEffects) { + return getDiscriminator(type.innerType()); + } else if (type instanceof ZodLiteral) { + return [type.value]; + } else if (type instanceof ZodEnum) { + return type.options; + } else if (type instanceof ZodNativeEnum) { + return util2.objectValues(type.enum); + } else if (type instanceof ZodDefault) { + return getDiscriminator(type._def.innerType); + } else if (type instanceof ZodUndefined) { + return [void 0]; + } else if (type instanceof ZodNull) { + return [null]; + } else if (type instanceof ZodOptional) { + return [void 0, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodNullable) { + return [null, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodBranded) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodReadonly) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodCatch) { + return getDiscriminator(type._def.innerType); + } else { + return []; + } +}; +var ZodDiscriminatedUnion = class _ZodDiscriminatedUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const discriminator = this.discriminator; + const discriminatorValue = ctx.data[discriminator]; + const option = this.optionsMap.get(discriminatorValue); + if (!option) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union_discriminator, + options: Array.from(this.optionsMap.keys()), + path: [discriminator] + }); + return INVALID; + } + if (ctx.common.async) { + return option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } else { + return option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } + } + get discriminator() { + return this._def.discriminator; + } + get options() { + return this._def.options; + } + get optionsMap() { + return this._def.optionsMap; + } + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create(discriminator, options, params) { + const optionsMap = /* @__PURE__ */ new Map(); + for (const type of options) { + const discriminatorValues = getDiscriminator(type.shape[discriminator]); + if (!discriminatorValues.length) { + throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`); + } + for (const value of discriminatorValues) { + if (optionsMap.has(value)) { + throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`); + } + optionsMap.set(value, type); + } + } + return new _ZodDiscriminatedUnion({ + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + discriminator, + options, + optionsMap, + ...processCreateParams(params) + }); + } +}; +function mergeValues(a, b) { + const aType = getParsedType(a); + const bType = getParsedType(b); + if (a === b) { + return { valid: true, data: a }; + } else if (aType === ZodParsedType.object && bType === ZodParsedType.object) { + const bKeys = util2.objectKeys(b); + const sharedKeys = util2.objectKeys(a).filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a, ...b }; + for (const key of sharedKeys) { + const sharedValue = mergeValues(a[key], b[key]); + if (!sharedValue.valid) { + return { valid: false }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } else if (aType === ZodParsedType.array && bType === ZodParsedType.array) { + if (a.length !== b.length) { + return { valid: false }; + } + const newArray = []; + for (let index = 0; index < a.length; index++) { + const itemA = a[index]; + const itemB = b[index]; + const sharedValue = mergeValues(itemA, itemB); + if (!sharedValue.valid) { + return { valid: false }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } else if (aType === ZodParsedType.date && bType === ZodParsedType.date && +a === +b) { + return { valid: true, data: a }; + } else { + return { valid: false }; + } +} +var ZodIntersection = class extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const handleParsed = (parsedLeft, parsedRight) => { + if (isAborted(parsedLeft) || isAborted(parsedRight)) { + return INVALID; + } + const merged = mergeValues(parsedLeft.value, parsedRight.value); + if (!merged.valid) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_intersection_types + }); + return INVALID; + } + if (isDirty(parsedLeft) || isDirty(parsedRight)) { + status.dirty(); + } + return { status: status.value, value: merged.data }; + }; + if (ctx.common.async) { + return Promise.all([ + this._def.left._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), + this._def.right._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }) + ]).then(([left, right]) => handleParsed(left, right)); + } else { + return handleParsed(this._def.left._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), this._def.right._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + })); + } + } +}; +ZodIntersection.create = (left, right, params) => { + return new ZodIntersection({ + left, + right, + typeName: ZodFirstPartyTypeKind.ZodIntersection, + ...processCreateParams(params) + }); +}; +var ZodTuple = class _ZodTuple extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (ctx.data.length < this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + return INVALID; + } + const rest = this._def.rest; + if (!rest && ctx.data.length > this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + status.dirty(); + } + const items = [...ctx.data].map((item, itemIndex) => { + const schema = this._def.items[itemIndex] || this._def.rest; + if (!schema) + return null; + return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex)); + }).filter((x2) => !!x2); + if (ctx.common.async) { + return Promise.all(items).then((results) => { + return ParseStatus.mergeArray(status, results); + }); + } else { + return ParseStatus.mergeArray(status, items); + } + } + get items() { + return this._def.items; + } + rest(rest) { + return new _ZodTuple({ + ...this._def, + rest + }); + } +}; +ZodTuple.create = (schemas, params) => { + if (!Array.isArray(schemas)) { + throw new Error("You must pass an array of schemas to z.tuple([ ... ])"); + } + return new ZodTuple({ + items: schemas, + typeName: ZodFirstPartyTypeKind.ZodTuple, + rest: null, + ...processCreateParams(params) + }); +}; +var ZodRecord = class _ZodRecord extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const pairs = []; + const keyType = this._def.keyType; + const valueType = this._def.valueType; + for (const key in ctx.data) { + pairs.push({ + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)), + value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (ctx.common.async) { + return ParseStatus.mergeObjectAsync(status, pairs); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get element() { + return this._def.valueType; + } + static create(first, second, third) { + if (second instanceof ZodType) { + return new _ZodRecord({ + keyType: first, + valueType: second, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(third) + }); + } + return new _ZodRecord({ + keyType: ZodString.create(), + valueType: first, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(second) + }); + } +}; +var ZodMap = class extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.map) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.map, + received: ctx.parsedType + }); + return INVALID; + } + const keyType = this._def.keyType; + const valueType = this._def.valueType; + const pairs = [...ctx.data.entries()].map(([key, value], index) => { + return { + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])), + value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])) + }; + }); + if (ctx.common.async) { + const finalMap = /* @__PURE__ */ new Map(); + return Promise.resolve().then(async () => { + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + }); + } else { + const finalMap = /* @__PURE__ */ new Map(); + for (const pair of pairs) { + const key = pair.key; + const value = pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + } + } +}; +ZodMap.create = (keyType, valueType, params) => { + return new ZodMap({ + valueType, + keyType, + typeName: ZodFirstPartyTypeKind.ZodMap, + ...processCreateParams(params) + }); +}; +var ZodSet = class _ZodSet extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.set) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.set, + received: ctx.parsedType + }); + return INVALID; + } + const def = this._def; + if (def.minSize !== null) { + if (ctx.data.size < def.minSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.minSize.message + }); + status.dirty(); + } + } + if (def.maxSize !== null) { + if (ctx.data.size > def.maxSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.maxSize.message + }); + status.dirty(); + } + } + const valueType = this._def.valueType; + function finalizeSet(elements2) { + const parsedSet = /* @__PURE__ */ new Set(); + for (const element of elements2) { + if (element.status === "aborted") + return INVALID; + if (element.status === "dirty") + status.dirty(); + parsedSet.add(element.value); + } + return { status: status.value, value: parsedSet }; + } + const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i))); + if (ctx.common.async) { + return Promise.all(elements).then((elements2) => finalizeSet(elements2)); + } else { + return finalizeSet(elements); + } + } + min(minSize, message) { + return new _ZodSet({ + ...this._def, + minSize: { value: minSize, message: errorUtil.toString(message) } + }); + } + max(maxSize, message) { + return new _ZodSet({ + ...this._def, + maxSize: { value: maxSize, message: errorUtil.toString(message) } + }); + } + size(size, message) { + return this.min(size, message).max(size, message); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodSet.create = (valueType, params) => { + return new ZodSet({ + valueType, + minSize: null, + maxSize: null, + typeName: ZodFirstPartyTypeKind.ZodSet, + ...processCreateParams(params) + }); +}; +var ZodFunction = class _ZodFunction extends ZodType { + constructor() { + super(...arguments); + this.validate = this.implement; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.function) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.function, + received: ctx.parsedType + }); + return INVALID; + } + function makeArgsIssue(args, error) { + return makeIssue({ + data: args, + path: ctx.path, + errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default].filter((x2) => !!x2), + issueData: { + code: ZodIssueCode.invalid_arguments, + argumentsError: error + } + }); + } + function makeReturnsIssue(returns, error) { + return makeIssue({ + data: returns, + path: ctx.path, + errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default].filter((x2) => !!x2), + issueData: { + code: ZodIssueCode.invalid_return_type, + returnTypeError: error + } + }); + } + const params = { errorMap: ctx.common.contextualErrorMap }; + const fn = ctx.data; + if (this._def.returns instanceof ZodPromise) { + const me2 = this; + return OK(async function(...args) { + const error = new ZodError([]); + const parsedArgs = await me2._def.args.parseAsync(args, params).catch((e) => { + error.addIssue(makeArgsIssue(args, e)); + throw error; + }); + const result = await Reflect.apply(fn, this, parsedArgs); + const parsedReturns = await me2._def.returns._def.type.parseAsync(result, params).catch((e) => { + error.addIssue(makeReturnsIssue(result, e)); + throw error; + }); + return parsedReturns; + }); + } else { + const me2 = this; + return OK(function(...args) { + const parsedArgs = me2._def.args.safeParse(args, params); + if (!parsedArgs.success) { + throw new ZodError([makeArgsIssue(args, parsedArgs.error)]); + } + const result = Reflect.apply(fn, this, parsedArgs.data); + const parsedReturns = me2._def.returns.safeParse(result, params); + if (!parsedReturns.success) { + throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]); + } + return parsedReturns.data; + }); + } + } + parameters() { + return this._def.args; + } + returnType() { + return this._def.returns; + } + args(...items) { + return new _ZodFunction({ + ...this._def, + args: ZodTuple.create(items).rest(ZodUnknown.create()) + }); + } + returns(returnType) { + return new _ZodFunction({ + ...this._def, + returns: returnType + }); + } + implement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + strictImplement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + static create(args, returns, params) { + return new _ZodFunction({ + args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()), + returns: returns || ZodUnknown.create(), + typeName: ZodFirstPartyTypeKind.ZodFunction, + ...processCreateParams(params) + }); + } +}; +var ZodLazy = class extends ZodType { + get schema() { + return this._def.getter(); + } + _parse(input) { + const { ctx } = this._processInputParams(input); + const lazySchema = this._def.getter(); + return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx }); + } +}; +ZodLazy.create = (getter, params) => { + return new ZodLazy({ + getter, + typeName: ZodFirstPartyTypeKind.ZodLazy, + ...processCreateParams(params) + }); +}; +var ZodLiteral = class extends ZodType { + _parse(input) { + if (input.data !== this._def.value) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_literal, + expected: this._def.value + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } + get value() { + return this._def.value; + } +}; +ZodLiteral.create = (value, params) => { + return new ZodLiteral({ + value, + typeName: ZodFirstPartyTypeKind.ZodLiteral, + ...processCreateParams(params) + }); +}; +function createZodEnum(values, params) { + return new ZodEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodEnum, + ...processCreateParams(params) + }); +} +var ZodEnum = class _ZodEnum extends ZodType { + _parse(input) { + if (typeof input.data !== "string") { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + expected: util2.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!this._cache) { + this._cache = new Set(this._def.values); + } + if (!this._cache.has(input.data)) { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get options() { + return this._def.values; + } + get enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Values() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + extract(values, newDef = this._def) { + return _ZodEnum.create(values, { + ...this._def, + ...newDef + }); + } + exclude(values, newDef = this._def) { + return _ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), { + ...this._def, + ...newDef + }); + } +}; +ZodEnum.create = createZodEnum; +var ZodNativeEnum = class extends ZodType { + _parse(input) { + const nativeEnumValues = util2.getValidEnumValues(this._def.values); + const ctx = this._getOrReturnCtx(input); + if (ctx.parsedType !== ZodParsedType.string && ctx.parsedType !== ZodParsedType.number) { + const expectedValues = util2.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + expected: util2.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!this._cache) { + this._cache = new Set(util2.getValidEnumValues(this._def.values)); + } + if (!this._cache.has(input.data)) { + const expectedValues = util2.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get enum() { + return this._def.values; + } +}; +ZodNativeEnum.create = (values, params) => { + return new ZodNativeEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodNativeEnum, + ...processCreateParams(params) + }); +}; +var ZodPromise = class extends ZodType { + unwrap() { + return this._def.type; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.promise && ctx.common.async === false) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.promise, + received: ctx.parsedType + }); + return INVALID; + } + const promisified = ctx.parsedType === ZodParsedType.promise ? ctx.data : Promise.resolve(ctx.data); + return OK(promisified.then((data) => { + return this._def.type.parseAsync(data, { + path: ctx.path, + errorMap: ctx.common.contextualErrorMap + }); + })); + } +}; +ZodPromise.create = (schema, params) => { + return new ZodPromise({ + type: schema, + typeName: ZodFirstPartyTypeKind.ZodPromise, + ...processCreateParams(params) + }); +}; +var ZodEffects = class extends ZodType { + innerType() { + return this._def.schema; + } + sourceType() { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const effect = this._def.effect || null; + const checkCtx = { + addIssue: (arg) => { + addIssueToContext(ctx, arg); + if (arg.fatal) { + status.abort(); + } else { + status.dirty(); + } + }, + get path() { + return ctx.path; + } + }; + checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx); + if (effect.type === "preprocess") { + const processed = effect.transform(ctx.data, checkCtx); + if (ctx.common.async) { + return Promise.resolve(processed).then(async (processed2) => { + if (status.value === "aborted") + return INVALID; + const result = await this._def.schema._parseAsync({ + data: processed2, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + }); + } else { + if (status.value === "aborted") + return INVALID; + const result = this._def.schema._parseSync({ + data: processed, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + } + } + if (effect.type === "refinement") { + const executeRefinement = (acc) => { + const result = effect.refinement(acc, checkCtx); + if (ctx.common.async) { + return Promise.resolve(result); + } + if (result instanceof Promise) { + throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead."); + } + return acc; + }; + if (ctx.common.async === false) { + const inner = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + executeRefinement(inner.value); + return { status: status.value, value: inner.value }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => { + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + return executeRefinement(inner.value).then(() => { + return { status: status.value, value: inner.value }; + }); + }); + } + } + if (effect.type === "transform") { + if (ctx.common.async === false) { + const base = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (!isValid(base)) + return INVALID; + const result = effect.transform(base.value, checkCtx); + if (result instanceof Promise) { + throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`); + } + return { status: status.value, value: result }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => { + if (!isValid(base)) + return INVALID; + return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ + status: status.value, + value: result + })); + }); + } + } + util2.assertNever(effect); + } +}; +ZodEffects.create = (schema, effect, params) => { + return new ZodEffects({ + schema, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect, + ...processCreateParams(params) + }); +}; +ZodEffects.createWithPreprocess = (preprocess, schema, params) => { + return new ZodEffects({ + schema, + effect: { type: "preprocess", transform: preprocess }, + typeName: ZodFirstPartyTypeKind.ZodEffects, + ...processCreateParams(params) + }); +}; +var ZodOptional = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.undefined) { + return OK(void 0); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodOptional.create = (type, params) => { + return new ZodOptional({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodOptional, + ...processCreateParams(params) + }); +}; +var ZodNullable = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.null) { + return OK(null); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodNullable.create = (type, params) => { + return new ZodNullable({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodNullable, + ...processCreateParams(params) + }); +}; +var ZodDefault = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + let data = ctx.data; + if (ctx.parsedType === ZodParsedType.undefined) { + data = this._def.defaultValue(); + } + return this._def.innerType._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + removeDefault() { + return this._def.innerType; + } +}; +ZodDefault.create = (type, params) => { + return new ZodDefault({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodDefault, + defaultValue: typeof params.default === "function" ? params.default : () => params.default, + ...processCreateParams(params) + }); +}; +var ZodCatch = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const newCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + } + }; + const result = this._def.innerType._parse({ + data: newCtx.data, + path: newCtx.path, + parent: { + ...newCtx + } + }); + if (isAsync(result)) { + return result.then((result2) => { + return { + status: "valid", + value: result2.status === "valid" ? result2.value : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + }); + } else { + return { + status: "valid", + value: result.status === "valid" ? result.value : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + } + } + removeCatch() { + return this._def.innerType; + } +}; +ZodCatch.create = (type, params) => { + return new ZodCatch({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodCatch, + catchValue: typeof params.catch === "function" ? params.catch : () => params.catch, + ...processCreateParams(params) + }); +}; +var ZodNaN = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.nan) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.nan, + received: ctx.parsedType + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } +}; +ZodNaN.create = (params) => { + return new ZodNaN({ + typeName: ZodFirstPartyTypeKind.ZodNaN, + ...processCreateParams(params) + }); +}; +var BRAND = /* @__PURE__ */ Symbol("zod_brand"); +var ZodBranded = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const data = ctx.data; + return this._def.type._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + unwrap() { + return this._def.type; + } +}; +var ZodPipeline = class _ZodPipeline extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.common.async) { + const handleAsync = async () => { + const inResult = await this._def.in._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return DIRTY(inResult.value); + } else { + return this._def.out._parseAsync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + }; + return handleAsync(); + } else { + const inResult = this._def.in._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return { + status: "dirty", + value: inResult.value + }; + } else { + return this._def.out._parseSync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + } + } + static create(a, b) { + return new _ZodPipeline({ + in: a, + out: b, + typeName: ZodFirstPartyTypeKind.ZodPipeline + }); + } +}; +var ZodReadonly = class extends ZodType { + _parse(input) { + const result = this._def.innerType._parse(input); + const freeze = (data) => { + if (isValid(data)) { + data.value = Object.freeze(data.value); + } + return data; + }; + return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result); + } + unwrap() { + return this._def.innerType; + } +}; +ZodReadonly.create = (type, params) => { + return new ZodReadonly({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodReadonly, + ...processCreateParams(params) + }); +}; +function cleanParams(params, data) { + const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params; + const p2 = typeof p === "string" ? { message: p } : p; + return p2; +} +function custom(check, _params = {}, fatal) { + if (check) + return ZodAny.create().superRefine((data, ctx) => { + const r = check(data); + if (r instanceof Promise) { + return r.then((r2) => { + if (!r2) { + const params = cleanParams(_params, data); + const _fatal = params.fatal ?? fatal ?? true; + ctx.addIssue({ code: "custom", ...params, fatal: _fatal }); + } + }); + } + if (!r) { + const params = cleanParams(_params, data); + const _fatal = params.fatal ?? fatal ?? true; + ctx.addIssue({ code: "custom", ...params, fatal: _fatal }); + } + return; + }); + return ZodAny.create(); +} +var late = { + object: ZodObject.lazycreate +}; +var ZodFirstPartyTypeKind; +(function(ZodFirstPartyTypeKind2) { + ZodFirstPartyTypeKind2["ZodString"] = "ZodString"; + ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber"; + ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN"; + ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt"; + ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean"; + ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate"; + ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol"; + ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined"; + ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull"; + ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny"; + ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown"; + ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever"; + ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid"; + ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray"; + ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject"; + ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion"; + ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion"; + ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection"; + ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple"; + ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord"; + ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap"; + ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet"; + ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction"; + ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy"; + ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral"; + ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum"; + ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects"; + ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum"; + ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional"; + ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable"; + ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault"; + ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch"; + ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise"; + ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded"; + ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline"; + ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly"; +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); +var instanceOfType = (cls, params = { + message: `Input not instance of ${cls.name}` +}) => custom((data) => data instanceof cls, params); +var stringType = ZodString.create; +var numberType = ZodNumber.create; +var nanType = ZodNaN.create; +var bigIntType = ZodBigInt.create; +var booleanType = ZodBoolean.create; +var dateType = ZodDate.create; +var symbolType = ZodSymbol.create; +var undefinedType = ZodUndefined.create; +var nullType = ZodNull.create; +var anyType = ZodAny.create; +var unknownType = ZodUnknown.create; +var neverType = ZodNever.create; +var voidType = ZodVoid.create; +var arrayType = ZodArray.create; +var objectType = ZodObject.create; +var strictObjectType = ZodObject.strictCreate; +var unionType = ZodUnion.create; +var discriminatedUnionType = ZodDiscriminatedUnion.create; +var intersectionType = ZodIntersection.create; +var tupleType = ZodTuple.create; +var recordType = ZodRecord.create; +var mapType = ZodMap.create; +var setType = ZodSet.create; +var functionType = ZodFunction.create; +var lazyType = ZodLazy.create; +var literalType = ZodLiteral.create; +var enumType = ZodEnum.create; +var nativeEnumType = ZodNativeEnum.create; +var promiseType = ZodPromise.create; +var effectsType = ZodEffects.create; +var optionalType = ZodOptional.create; +var nullableType = ZodNullable.create; +var preprocessType = ZodEffects.createWithPreprocess; +var pipelineType = ZodPipeline.create; +var ostring = () => stringType().optional(); +var onumber = () => numberType().optional(); +var oboolean = () => booleanType().optional(); +var coerce = { + string: ((arg) => ZodString.create({ ...arg, coerce: true })), + number: ((arg) => ZodNumber.create({ ...arg, coerce: true })), + boolean: ((arg) => ZodBoolean.create({ + ...arg, + coerce: true + })), + bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })), + date: ((arg) => ZodDate.create({ ...arg, coerce: true })) +}; +var NEVER = INVALID; + +// ../plugins/dist/utils.js +var isHookTree = (value) => { + if (value == null || typeof value !== "object" || Array.isArray(value)) { + return false; + } + for (const child of Object.values(value)) { + if (child === void 0) { + continue; + } + if (typeof child === "function") { + continue; + } + if (child == null || typeof child !== "object" || Array.isArray(child) || !isHookTree(child)) { + return false; + } + } + return true; +}; + +// ../plugins/dist/plugin.js +var isHarnessPlugin = (value) => { + if (value == null || typeof value !== "object" || Array.isArray(value)) { + return false; + } + const candidate = value; + if (typeof candidate.name !== "string" || candidate.name.length === 0) { + return false; + } + if (candidate.createState != null && typeof candidate.createState !== "function") { + return false; + } + if (candidate.hooks != null && !isHookTree(candidate.hooks)) { + return false; + } + return true; +}; + +// ../plugins/dist/manager.js +var pluginsLogger = logger.child("plugins"); + +// ../config/dist/types.js +var DEFAULT_METRO_PORT = 8081; +var configLogger = logger.child("config"); +var RunnerSchema = external_exports.object({ + name: external_exports.string().min(1, "Runner name is required").regex(/^[a-zA-Z0-9._-]+$/, "Runner name can only contain alphanumeric characters, dots, underscores, and hyphens"), + config: external_exports.record(external_exports.any()), + runner: external_exports.string(), + cli: external_exports.string().optional(), + platformId: external_exports.string() +}); +var PluginSchema = external_exports.custom((value) => isHarnessPlugin(value), "Invalid Harness plugin"); +var ConfigSchema = external_exports.object({ + entryPoint: external_exports.string().min(1, "Entry point is required"), + appRegistryComponentName: external_exports.string().min(1, "App registry component name is required"), + runners: external_exports.array(RunnerSchema).min(1, "At least one runner is required"), + plugins: external_exports.array(PluginSchema).optional().default([]), + defaultRunner: external_exports.string().optional(), + host: external_exports.string().min(1, "Host is required").optional(), + metroPort: external_exports.number().int("Metro port must be an integer").min(1, "Metro port must be at least 1").max(65535, "Metro port must be at most 65535").optional().default(DEFAULT_METRO_PORT), + webSocketPort: external_exports.number().optional().describe("Deprecated. Bridge traffic now uses metroPort and this value is ignored."), + bridgeTimeout: external_exports.number().min(1e3, "Bridge timeout must be at least 1 second").default(6e4), + testTimeout: external_exports.number().min(1e3, "Test timeout must be at least 1 second").default(5e3), + platformReadyTimeout: external_exports.number().min(1e3, "Platform ready timeout must be at least 1 second").default(3e5), + bundleStartTimeout: external_exports.number().min(1e3, "Bundle start timeout must be at least 1 second").default(6e4), + maxAppRestarts: external_exports.number().min(0, "Max app restarts must be at least 0").default(2), + eagerPrewarm: external_exports.boolean().optional().default(true).describe("Start building the Metro bundle while the platform (emulator, simulator, or browser) is still booting, so the first bundle is ready sooner. Disable to defer the first bundle build until app startup."), + resetEnvironmentBetweenTestFiles: external_exports.union([external_exports.boolean(), external_exports.enum(["process", "runtime"])]).optional().default(true).describe("Controls how the environment is reset between test files. `true` (default) and `'process'` kill and cold-restart the app process. `'runtime'` reloads the JS runtime in place (DevSettings.reload() / window.location.reload()), which is cheaper but escalates to a process restart if the reload fails or the app does not reconnect in time. `false` disables resetting the environment between test files entirely."), + skipAlreadyIncludedModules: external_exports.boolean().optional().describe('Skip re-sending modules already served in the main app bundle when Metro serves a per-test-file bundle. Defaults to true; set to false as an escape hatch if it causes issues. Left undefined (rather than defaulted here) so `resolveSkipAlreadyIncludedModules` can tell "not set" apart from "explicitly set" when reconciling with the deprecated `unstable__skipAlreadyIncludedModules` alias.'), + // Deprecated alias for `skipAlreadyIncludedModules` -- see + // `resolveSkipAlreadyIncludedModules`. Left without a `.default()` for the + // same reason: a default value would be indistinguishable from the user + // never having set it, which would break alias-vs-explicit-flag precedence. + unstable__skipAlreadyIncludedModules: external_exports.boolean().optional().describe("Deprecated. Use `skipAlreadyIncludedModules` instead."), + cache: external_exports.object({ + metro: external_exports.boolean().optional().describe('Enable persistent Metro transform and file-map caching under the .harness cache directory. Defaults to true; set to false to always start with a cold Metro cache. Left undefined (rather than defaulted here) so `resolveMetroCacheEnabled` can tell "not set" apart from "explicitly set" when reconciling with the deprecated `unstable__enableMetroCache` alias.'), + version: external_exports.string().optional().describe("User-controlled salt folded into the Metro cacheVersion. Bump it to force-invalidate previously cached transforms.") + }).optional(), + // Deprecated alias for `cache.metro` -- see `resolveMetroCacheEnabled`. + // Left without a `.default()` for the same reason as + // `unstable__skipAlreadyIncludedModules`: a default value would be + // indistinguishable from the user never having set it, which would break + // alias-vs-explicit-flag precedence. + unstable__enableMetroCache: external_exports.boolean().optional().describe("Deprecated. Use `cache.metro` instead."), + permissions: external_exports.boolean().optional().default(false).describe("Enable platform-specific permission prompt automation. When false, Harness does not start permission-handling helpers such as the iOS XCTest agent."), + detectNativeCrashes: external_exports.boolean().optional().default(true), + crashDetectionInterval: external_exports.number().min(100, "Crash detection interval must be at least 100ms").default(500), + disableViewFlattening: external_exports.boolean().optional().default(false).describe("Disable view flattening in React Native. This will set collapsable={true} for all View components to ensure they are not flattened by the native layout engine."), + coverage: external_exports.object({ + root: external_exports.string().optional().describe(`Root directory for coverage instrumentation in monorepo setups. Specifies the directory from which coverage data should be collected. Use ".." for create-react-native-library projects where tests run from example/ but source files are in parent directory. Passed to babel-plugin-istanbul's cwd option.`), + native: external_exports.object({ + ios: external_exports.object({ + pods: external_exports.array(external_exports.string()).min(1, "At least one pod name is required").describe("Pod names to instrument for native code coverage. Coverage flags are injected at pod install time via a CocoaPods hook. After tests, profraw data is collected and converted to lcov format.") + }).optional() + }).optional().describe("Native code coverage configuration.") + }).optional(), + forwardClientLogs: external_exports.boolean().optional().default(false).describe("Enable forwarding of console.log, console.warn, console.error, and other console method calls from the React Native app during the active test run. When enabled, app console output is attached to the active test result's console output."), + diagnostics: external_exports.boolean().optional().default(false).describe("Enable diagnostics tracing for the harness session. Records spans for session setup, Metro bundling, bridge/client events, and per-file test runs, then writes a Chrome Trace Event JSON file and prints a summary after each run. Can also be enabled via the RN_HARNESS_DIAGNOSTICS environment variable."), + // Deprecated property - used for migration detection + include: external_exports.array(external_exports.string()).optional() +}).refine((config) => { + if (config.defaultRunner) { + return config.runners.some((runner) => runner.name === config.defaultRunner); + } + return true; +}, { + message: "Default runner must match one of the configured runner names", + path: ["defaultRunner"] +}); + +// ../config/dist/errors.js +var ConfigValidationError = class extends HarnessError { + filePath; + validationErrors; + constructor(filePath, validationErrors) { + const lines = [ + `Configuration validation failed in ${filePath}`, + "", + "The following issues were found:", + "" + ]; + validationErrors.forEach((error, index) => { + lines.push(` ${index + 1}. ${error}`); + }); + lines.push("", "Please fix these issues and try again.", "For more information, visit: https://react-native-harness.dev/docs/configuration"); + super(lines.join("\n")); + this.filePath = filePath; + this.validationErrors = validationErrors; + this.name = "ConfigValidationError"; + } +}; +var ConfigNotFoundError = class extends HarnessError { + searchPath; + constructor(searchPath) { + const lines = [ + "Configuration file not found", + "", + `Searched for configuration files in: ${searchPath}`, + "and all parent directories.", + "", + "React Native Harness looks for one of these files:", + " \u2022 rn-harness.config.js", + " \u2022 rn-harness.config.mjs", + " \u2022 rn-harness.config.cjs", + " \u2022 rn-harness.config.json", + "", + "For more information, visit: https://www.react-native-harness.dev/docs/getting-started/configuration" + ]; + super(lines.join("\n")); + this.searchPath = searchPath; + this.name = "ConfigNotFoundError"; + } +}; +var ConfigLoadError = class extends HarnessError { + filePath; + cause; + constructor(filePath, cause) { + const lines = [ + "Failed to load configuration file", + "", + `File: ${filePath}`, + "" + ]; + if (cause) { + lines.push("Error details:"); + lines.push(` ${cause.message}`); + lines.push(""); + } + lines.push("This could be due to:", " \u2022 Syntax errors in your configuration file", " \u2022 Missing dependencies or modules", " \u2022 Invalid file format or encoding", " \u2022 File permissions issues", "", "Troubleshooting steps:", " 1. Check the file syntax and format", " 2. Ensure all required dependencies are installed", " 3. Verify file permissions", " 4. Try creating a new configuration file", "", "For more help, visit: https://www.react-native-harness.dev/docs/getting-started/configuration"); + super(lines.join("\n")); + this.filePath = filePath; + this.name = "ConfigLoadError"; + this.cause = cause; + } +}; + +// ../config/dist/reader.js +var import_node_path10 = __toESM(require("path"), 1); +var import_node_fs10 = __toESM(require("fs"), 1); +var import_node_module2 = require("module"); +var import_meta = {}; +var extensions = [".js", ".mjs", ".cjs", ".json"]; +var importUp = async (dir, name) => { + const filePath = import_node_path10.default.join(dir, name); + for (const ext of extensions) { + const filePathWithExt = `${filePath}${ext}`; + if (import_node_fs10.default.existsSync(filePathWithExt)) { + let rawConfig; + try { + if (ext === ".mjs") { + rawConfig = await import(filePathWithExt).then((module2) => module2.default); + } else { + const require2 = (0, import_node_module2.createRequire)(import_meta.url); + rawConfig = require2(filePathWithExt); + } + } catch (error) { + throw new ConfigLoadError(filePathWithExt, error instanceof Error ? error : void 0); + } + try { + const config = ConfigSchema.parse(rawConfig); + return { config, filePathWithExt, configDir: dir }; + } catch (error) { + if (error instanceof ZodError) { + const validationErrors = error.errors.map((err) => { + const path12 = err.path.length > 0 ? ` at "${err.path.join(".")}"` : ""; + return `${err.message}${path12}`; + }); + throw new ConfigValidationError(filePathWithExt, validationErrors); + } + throw error; + } + } + } + const parentDir = import_node_path10.default.dirname(dir); + if (parentDir === dir) { + throw new ConfigNotFoundError(dir); + } + return importUp(parentDir, name); +}; +var getConfig = async (dir) => { + const { config, configDir } = await importUp(dir, "rn-harness.config"); + return { + config, + projectRoot: configDir + }; +}; + +// src/shared/metro-cache-inputs.ts +var import_node_fs11 = __toESM(require("fs")); +var import_node_path11 = __toESM(require("path")); +var FALLBACK_BUNDLER_METRO_VERSION = "unknown"; +var resolveRepoRoot = (projectRoot) => { + let dir = projectRoot; + while (true) { + if (import_node_fs11.default.existsSync(import_node_path11.default.join(dir, ".git"))) { + return dir; + } + const parent = import_node_path11.default.dirname(dir); + if (parent === dir) { + return projectRoot; + } + dir = parent; + } +}; +var resolveBundlerMetroPackageJson = (projectRoot) => { + try { + return require.resolve( + "@react-native-harness/bundler-metro/package.json", + { paths: [projectRoot] } + ); + } catch { + const jestPackageJsonPath = require.resolve( + "@react-native-harness/jest/package.json", + { paths: [projectRoot] } + ); + return require.resolve("@react-native-harness/bundler-metro/package.json", { + paths: [import_node_path11.default.dirname(jestPackageJsonPath)] + }); + } +}; +var resolveBundlerMetroVersion = (projectRoot) => { + try { + const packageJsonPath = resolveBundlerMetroPackageJson(projectRoot); + const packageJson = JSON.parse(import_node_fs11.default.readFileSync(packageJsonPath, "utf8")); + if (typeof packageJson.version !== "string") { + throw new Error('"version" field is missing or not a string'); + } + return packageJson.version; + } catch (error) { + console.warn( + `Failed to resolve @react-native-harness/bundler-metro's version from "${projectRoot}". Falling back to "${FALLBACK_BUNDLER_METRO_VERSION}".`, + error + ); + return FALLBACK_BUNDLER_METRO_VERSION; + } +}; +var resolveMetroStaticInputs = (options) => computeMetroStaticInputs({ + projectRoot: options.projectRoot, + repoRoot: resolveRepoRoot(options.projectRoot), + bundlerMetroVersion: resolveBundlerMetroVersion(options.projectRoot), + salt: options.cacheVersionSalt +}); + +// src/shared/plan-restore.ts +var run = async () => { + try { + const projectRootInput = process.env.INPUT_PROJECTROOT; + const projectRoot = projectRootInput ? import_node_path12.default.resolve(projectRootInput) : process.cwd(); + console.info(`Planning Metro cache restore for: ${projectRoot}`); + const { config, projectRoot: resolvedProjectRoot } = await getConfig( + projectRoot + ); + const githubOutput = process.env.GITHUB_OUTPUT; + if (!githubOutput) { + throw new Error("GITHUB_OUTPUT environment variable is not set"); + } + const staticInputs = resolveMetroStaticInputs({ + projectRoot: resolvedProjectRoot, + cacheVersionSalt: config.cache?.version + }); + const cache = createHarnessCache({ projectRoot: resolvedProjectRoot }); + const os = process.env.RUNNER_OS ?? process.platform; + const restorePlan = cache.planRestore({ + metro: { os, staticInputs } + }); + cache.writeKeysFile(restorePlan); + const metroPlan = restorePlan.domains.metro; + const output = `metroRestoreKey=${metroPlan?.restoreKey ?? ""} +metroRestorePrefixes=${JSON.stringify( + metroPlan?.restorePrefixes ?? [] + )} +`; + import_node_fs12.default.appendFileSync(githubOutput, output); + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } else { + console.error("Failed to plan Metro cache restore"); + } + process.exit(1); + } +}; +run(); diff --git a/actions/shared/plan-save.cjs b/actions/shared/plan-save.cjs new file mode 100644 index 00000000..dfd15ba1 --- /dev/null +++ b/actions/shared/plan-save.cjs @@ -0,0 +1,5090 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// ../../node_modules/picocolors/picocolors.js +var require_picocolors = __commonJS({ + "../../node_modules/picocolors/picocolors.js"(exports2, module2) { + "use strict"; + var p = process || {}; + var argv = p.argv || []; + var env = p.env || {}; + var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI); + var formatter = (open, close, replace = open) => (input) => { + let string = "" + input, index = string.indexOf(close, open.length); + return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close; + }; + var replaceClose = (string, close, replace, index) => { + let result = "", cursor = 0; + do { + result += string.substring(cursor, index) + replace; + cursor = index + close.length; + index = string.indexOf(close, cursor); + } while (~index); + return result + string.substring(cursor); + }; + var createColors = (enabled = isColorSupported) => { + let f = enabled ? formatter : () => String; + return { + isColorSupported: enabled, + reset: f("\x1B[0m", "\x1B[0m"), + bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"), + dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"), + italic: f("\x1B[3m", "\x1B[23m"), + underline: f("\x1B[4m", "\x1B[24m"), + inverse: f("\x1B[7m", "\x1B[27m"), + hidden: f("\x1B[8m", "\x1B[28m"), + strikethrough: f("\x1B[9m", "\x1B[29m"), + black: f("\x1B[30m", "\x1B[39m"), + red: f("\x1B[31m", "\x1B[39m"), + green: f("\x1B[32m", "\x1B[39m"), + yellow: f("\x1B[33m", "\x1B[39m"), + blue: f("\x1B[34m", "\x1B[39m"), + magenta: f("\x1B[35m", "\x1B[39m"), + cyan: f("\x1B[36m", "\x1B[39m"), + white: f("\x1B[37m", "\x1B[39m"), + gray: f("\x1B[90m", "\x1B[39m"), + bgBlack: f("\x1B[40m", "\x1B[49m"), + bgRed: f("\x1B[41m", "\x1B[49m"), + bgGreen: f("\x1B[42m", "\x1B[49m"), + bgYellow: f("\x1B[43m", "\x1B[49m"), + bgBlue: f("\x1B[44m", "\x1B[49m"), + bgMagenta: f("\x1B[45m", "\x1B[49m"), + bgCyan: f("\x1B[46m", "\x1B[49m"), + bgWhite: f("\x1B[47m", "\x1B[49m"), + blackBright: f("\x1B[90m", "\x1B[39m"), + redBright: f("\x1B[91m", "\x1B[39m"), + greenBright: f("\x1B[92m", "\x1B[39m"), + yellowBright: f("\x1B[93m", "\x1B[39m"), + blueBright: f("\x1B[94m", "\x1B[39m"), + magentaBright: f("\x1B[95m", "\x1B[39m"), + cyanBright: f("\x1B[96m", "\x1B[39m"), + whiteBright: f("\x1B[97m", "\x1B[39m"), + bgBlackBright: f("\x1B[100m", "\x1B[49m"), + bgRedBright: f("\x1B[101m", "\x1B[49m"), + bgGreenBright: f("\x1B[102m", "\x1B[49m"), + bgYellowBright: f("\x1B[103m", "\x1B[49m"), + bgBlueBright: f("\x1B[104m", "\x1B[49m"), + bgMagentaBright: f("\x1B[105m", "\x1B[49m"), + bgCyanBright: f("\x1B[106m", "\x1B[49m"), + bgWhiteBright: f("\x1B[107m", "\x1B[49m") + }; + }; + module2.exports = createColors(); + module2.exports.createColors = createColors; + } +}); + +// ../../node_modules/sisteransi/src/index.js +var require_src = __commonJS({ + "../../node_modules/sisteransi/src/index.js"(exports2, module2) { + "use strict"; + var ESC = "\x1B"; + var CSI = `${ESC}[`; + var beep = "\x07"; + var cursor = { + to(x2, y) { + if (!y) return `${CSI}${x2 + 1}G`; + return `${CSI}${y + 1};${x2 + 1}H`; + }, + move(x2, y) { + let ret = ""; + if (x2 < 0) ret += `${CSI}${-x2}D`; + else if (x2 > 0) ret += `${CSI}${x2}C`; + if (y < 0) ret += `${CSI}${-y}A`; + else if (y > 0) ret += `${CSI}${y}B`; + return ret; + }, + up: (count = 1) => `${CSI}${count}A`, + down: (count = 1) => `${CSI}${count}B`, + forward: (count = 1) => `${CSI}${count}C`, + backward: (count = 1) => `${CSI}${count}D`, + nextLine: (count = 1) => `${CSI}E`.repeat(count), + prevLine: (count = 1) => `${CSI}F`.repeat(count), + left: `${CSI}G`, + hide: `${CSI}?25l`, + show: `${CSI}?25h`, + save: `${ESC}7`, + restore: `${ESC}8` + }; + var scroll = { + up: (count = 1) => `${CSI}S`.repeat(count), + down: (count = 1) => `${CSI}T`.repeat(count) + }; + var erase = { + screen: `${CSI}2J`, + up: (count = 1) => `${CSI}1J`.repeat(count), + down: (count = 1) => `${CSI}J`.repeat(count), + line: `${CSI}2K`, + lineEnd: `${CSI}K`, + lineStart: `${CSI}1K`, + lines(count) { + let clear = ""; + for (let i = 0; i < count; i++) + clear += this.line + (i < count - 1 ? cursor.up() : ""); + if (count) + clear += cursor.left; + return clear; + } + }; + module2.exports = { cursor, scroll, erase, beep }; + } +}); + +// src/shared/plan-save.ts +var import_node_fs12 = __toESM(require("fs")); +var import_node_path12 = __toESM(require("path")); + +// ../cache/dist/index.js +var import_node_fs9 = __toESM(require("fs"), 1); + +// ../tools/dist/net.js +var import_node_net = __toESM(require("net"), 1); + +// ../tools/dist/logger.js +var import_node_util = __toESM(require("util"), 1); +var import_picocolors = __toESM(require_picocolors(), 1); +var verbose = !!process.env.HARNESS_DEBUG; +var BASE_TAG = "[harness]"; +var INFO_TAG = import_picocolors.default.isColorSupported ? import_picocolors.default.reset(import_picocolors.default.inverse(import_picocolors.default.bold(import_picocolors.default.magenta(" HARNESS ")))) : "HARNESS"; +var ERROR_TAG = import_picocolors.default.isColorSupported ? import_picocolors.default.reset(import_picocolors.default.inverse(import_picocolors.default.bold(import_picocolors.default.red(" HARNESS ")))) : "HARNESS"; +var getTimestamp = () => (/* @__PURE__ */ new Date()).toISOString(); +var normalizeScope = (scope) => scope.trim().replace(/^\[+|\]+$/g, "").replace(/\]\[/g, "]["); +var formatPrefix = (scopes) => { + const suffix = scopes.map((scope) => `[${normalizeScope(scope)}]`).join(""); + return `${BASE_TAG}${suffix}`; +}; +var mapLines = (text, prefix) => text.split("\n").map((line) => `${prefix} ${line}`).join("\n"); +var writeLog = (level, scopes, messages) => { + if (!verbose && (level === "info" || level === "log" || level === "success")) { + const output2 = import_node_util.default.format(...messages); + const tag = INFO_TAG; + process.stderr.write(`${tag} ${output2} +`); + return; + } + if (!verbose && level === "error") { + const output2 = import_node_util.default.format(...messages); + process.stderr.write(`${ERROR_TAG} ${output2} +`); + return; + } + const method = level === "warn" ? console.warn : console.debug; + const output = import_node_util.default.format(...messages); + const prefix = `${getTimestamp()} ${formatPrefix(scopes)}`; + method(mapLines(output, prefix)); +}; +var setVerbose = (level) => { + verbose = level; +}; +var isVerbose = () => { + return verbose; +}; +var createScopedLogger = (scopes = []) => ({ + debug: (...messages) => { + if (!verbose) { + return; + } + writeLog("debug", scopes, messages); + }, + info: (...messages) => { + writeLog("info", scopes, messages); + }, + warn: (...messages) => { + writeLog("warn", scopes, messages); + }, + error: (...messages) => { + writeLog("error", scopes, messages); + }, + log: (...messages) => { + writeLog("log", scopes, messages); + }, + success: (...messages) => { + writeLog("success", scopes, messages); + }, + child: (scope) => createScopedLogger([...scopes, scope]), + setVerbose, + isVerbose +}); +var logger = createScopedLogger(); + +// ../../node_modules/@clack/core/dist/index.mjs +var import_node_process = require("process"); +var k = __toESM(require("readline"), 1); +var import_node_readline = __toESM(require("readline"), 1); +var import_sisteransi = __toESM(require_src(), 1); +var import_node_tty = require("tty"); +var Ft = { limit: 1 / 0, ellipsis: "" }; +var ft = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 }; +var j = "\x07"; +var Q = "["; +var dt = "]"; +var U = `${dt}8;;`; +var et = new RegExp(`(?:\\${Q}(?\\d+)m|\\${U}(?.*)${j})`, "y"); +var At = ["up", "down", "left", "right", "space", "enter", "cancel"]; +var _ = { actions: new Set(At), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]), messages: { cancel: "Canceled", error: "Something went wrong" }, withGuide: true }; +var bt = globalThis.process.platform.startsWith("win"); + +// ../../node_modules/@clack/prompts/dist/index.mjs +var import_picocolors2 = __toESM(require_picocolors(), 1); +var import_node_process2 = __toESM(require("process"), 1); +var import_node_fs = require("fs"); +var import_node_path = require("path"); +var import_sisteransi2 = __toESM(require_src(), 1); +var import_node_util2 = require("util"); +function ht() { + return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm"; +} +var ee = ht(); +var w = (e, r) => ee ? e : r; +var Me = w("\u25C6", "*"); +var ce = w("\u25A0", "x"); +var de = w("\u25B2", "x"); +var V = w("\u25C7", "o"); +var $e = w("\u250C", "T"); +var h = w("\u2502", "|"); +var x = w("\u2514", "\u2014"); +var Re = w("\u2510", "T"); +var Oe = w("\u2518", "\u2014"); +var Y = w("\u25CF", ">"); +var K = w("\u25CB", " "); +var te = w("\u25FB", "[\u2022]"); +var k2 = w("\u25FC", "[+]"); +var z = w("\u25FB", "[ ]"); +var Pe = w("\u25AA", "\u2022"); +var se = w("\u2500", "-"); +var he = w("\u256E", "+"); +var Ne = w("\u251C", "+"); +var me = w("\u256F", "+"); +var pe = w("\u2570", "+"); +var We = w("\u256D", "+"); +var ge = w("\u25CF", "\u2022"); +var fe = w("\u25C6", "*"); +var Fe = w("\u25B2", "!"); +var ye = w("\u25A0", "x"); +var Ft2 = { limit: 1 / 0, ellipsis: "" }; +var yt2 = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 }; +var Ce = "\x07"; +var Ve = "["; +var vt = "]"; +var we = `${vt}8;;`; +var Ge = new RegExp(`(?:\\${Ve}(?\\d+)m|\\${we}(?.*)${Ce})`, "y"); +var Ut = import_picocolors2.default.magenta; +var Ye = { light: w("\u2500", "-"), heavy: w("\u2501", "="), block: w("\u2588", "#") }; +var ze = `${import_picocolors2.default.gray(h)} `; + +// ../tools/dist/spawn.js +var spawnLogger = logger.child("spawn"); + +// ../tools/dist/react-native.js +var import_node_module = require("module"); +var import_node_path2 = __toESM(require("path"), 1); +var import_node_fs2 = __toESM(require("fs"), 1); + +// ../tools/dist/error.js +var HarnessError = class extends Error { +}; + +// ../tools/dist/packages.js +var import_node_path3 = __toESM(require("path"), 1); +var import_node_fs3 = __toESM(require("fs"), 1); + +// ../tools/dist/path.js +var import_node_path4 = __toESM(require("path"), 1); + +// ../tools/dist/crash-artifacts.js +var import_node_fs4 = __toESM(require("fs"), 1); +var import_node_path5 = __toESM(require("path"), 1); +var DEFAULT_ARTIFACT_ROOT = import_node_path5.default.join(process.cwd(), ".harness", "crash-reports"); + +// ../tools/dist/harness-artifacts.js +var import_node_fs5 = __toESM(require("fs"), 1); +var import_node_path6 = __toESM(require("path"), 1); + +// ../tools/dist/diagnostics.js +var noop = () => void 0; +var noopSpan = { + end: noop, + fail: noop +}; +var createNoopDiagnostics = () => { + const diagnostics = { + enabled: false, + start: () => noopSpan, + measure: async (_label, fn) => fn(), + mark: noop, + record: noop, + child: () => diagnostics, + flush: () => [] + }; + return diagnostics; +}; +var noopDiagnostics = createNoopDiagnostics(); + +// ../cache/dist/paths.js +var import_node_path7 = __toESM(require("path"), 1); +var HARNESS_DIRNAME = ".harness"; +var CACHE_DIRNAME = "cache"; +var METRO_TRANSFORM_DIRNAME = "metro"; +var METRO_FILE_MAP_DIRNAME = "metro-file-map"; +var createHarnessCachePaths = (projectRoot) => { + const root = import_node_path7.default.join(projectRoot, HARNESS_DIRNAME, CACHE_DIRNAME); + return { + root, + metroTransform: import_node_path7.default.join(root, METRO_TRANSFORM_DIRNAME), + metroFileMap: import_node_path7.default.join(root, METRO_FILE_MAP_DIRNAME) + }; +}; +var getDomainDirectories = (domain, paths) => { + switch (domain) { + case "metro": + return [paths.metroTransform, paths.metroFileMap]; + default: + return []; + } +}; + +// ../cache/dist/plan.js +var import_node_crypto = require("crypto"); +var import_node_fs6 = __toESM(require("fs"), 1); +var import_node_path8 = __toESM(require("path"), 1); +var cacheLogger = logger.child("cache"); +var KEYS_FILENAME = "keys.json"; +var HASH_LENGTH = 16; +var ALL_DOMAINS = ["metro"]; +var isAccretiveDomain = (domain) => { + switch (domain) { + case "metro": + return true; + default: + return false; + } +}; +var hashSortedEntries = (entries) => (0, import_node_crypto.createHash)("sha256").update(entries.slice().sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, value]) => `${key}=${value}`).join("\n")).digest("hex").slice(0, HASH_LENGTH); +var buildStaticHashKey = (domain, inputs) => { + const hash = hashSortedEntries(Object.entries(inputs.staticInputs)); + return `harness-${domain}-${inputs.os}-${hash}`; +}; +var buildContentHash = (entries) => hashSortedEntries(entries.map((entry) => [entry.path, String(entry.sizeBytes)])); +var walkDirectory = (root, directory, entries) => { + let dirents; + try { + dirents = import_node_fs6.default.readdirSync(directory, { withFileTypes: true }); + } catch { + return; + } + for (const dirent of dirents) { + const fullPath = import_node_path8.default.join(directory, dirent.name); + if (dirent.isDirectory()) { + walkDirectory(root, fullPath, entries); + } else if (dirent.isFile()) { + const stat = import_node_fs6.default.statSync(fullPath); + entries.push({ + path: import_node_path8.default.relative(root, fullPath), + sizeBytes: stat.size, + mtimeMs: stat.mtimeMs + }); + } + } +}; +var planRestore = (paths, inputs) => { + const domains = {}; + for (const domain of ALL_DOMAINS) { + const domainInputs = inputs[domain]; + if (!domainInputs) { + continue; + } + const restoreKey = buildStaticHashKey(domain, domainInputs); + const accretive = isAccretiveDomain(domain); + domains[domain] = { + paths: [...getDomainDirectories(domain, paths)], + restoreKey, + restorePrefixes: accretive ? [`${restoreKey}-`] : [], + accretive + }; + } + return { version: 1, domains }; +}; +var snapshot = (paths) => { + const result = {}; + for (const domain of ALL_DOMAINS) { + try { + const entries = []; + for (const directory of getDomainDirectories(domain, paths)) { + walkDirectory(paths.root, directory, entries); + } + result[domain] = entries; + } catch (error) { + cacheLogger.warn(`Failed to snapshot cache domain "${domain}". Treating as empty.`, error); + result[domain] = []; + } + } + return result; +}; +var diffSnapshots = (before, after) => { + const beforeByPath = new Map(before.map((entry) => [entry.path, entry])); + const afterByPath = new Map(after.map((entry) => [entry.path, entry])); + let addedEntries = 0; + let removedEntries = 0; + let addedBytes = 0; + for (const [entryPath, afterEntry] of afterByPath) { + const beforeEntry = beforeByPath.get(entryPath); + if (!beforeEntry || beforeEntry.sizeBytes !== afterEntry.sizeBytes) { + addedEntries += 1; + addedBytes += afterEntry.sizeBytes; + } + } + for (const entryPath of beforeByPath.keys()) { + if (!afterByPath.has(entryPath)) { + removedEntries += 1; + } + } + return { addedEntries, removedEntries, addedBytes }; +}; +var shouldSaveForPolicy = (policy, delta) => { + if (delta.addedEntries + delta.removedEntries === 0) { + return false; + } + switch (policy.mode) { + case "always": + return true; + case "default-branch": + return policy.isDefaultBranch; + case "never": + return false; + } +}; +var planSave = (paths, before, policy, inputs) => { + const after = snapshot(paths); + const domains = {}; + for (const domain of ALL_DOMAINS) { + const domainInputs = inputs[domain]; + if (!domainInputs) { + continue; + } + const beforeEntries = before[domain] ?? []; + const afterEntries = after[domain] ?? []; + const delta = diffSnapshots(beforeEntries, afterEntries); + const staticHashKey = buildStaticHashKey(domain, domainInputs); + const accretive = isAccretiveDomain(domain); + domains[domain] = { + shouldSave: shouldSaveForPolicy(policy, delta), + saveKey: accretive ? `${staticHashKey}-${buildContentHash(afterEntries)}` : staticHashKey, + delta + }; + } + return { version: 1, domains }; +}; +var writeKeysFile = (paths, plan) => { + const filePath = import_node_path8.default.join(paths.root, KEYS_FILENAME); + try { + import_node_fs6.default.mkdirSync(paths.root, { recursive: true }); + import_node_fs6.default.writeFileSync(filePath, JSON.stringify(plan, null, 2)); + } catch (error) { + cacheLogger.warn(`Failed to write cache keys file "${filePath}".`, error); + } +}; + +// ../cache/dist/warmth.js +var import_node_fs7 = __toESM(require("fs"), 1); +var cacheLogger2 = logger.child("cache"); +var isDomainWarm = (domain, paths) => { + const directory = getWarmthDirectory(domain, paths); + if (!directory) { + return false; + } + try { + const stat = import_node_fs7.default.statSync(directory); + if (!stat.isDirectory()) { + return false; + } + return import_node_fs7.default.readdirSync(directory).length > 0; + } catch (error) { + if (isMissingPathError(error)) { + return false; + } + cacheLogger2.warn(`Failed to check cache warmth for "${domain}" at "${directory}". Treating as cold.`, error); + return false; + } +}; +var getWarmthDirectory = (domain, paths) => { + switch (domain) { + case "metro": + return paths.metroTransform; + default: + return void 0; + } +}; +var isMissingPathError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT"; + +// ../cache/dist/domains/metro.js +var import_node_crypto2 = require("crypto"); +var import_node_fs8 = __toESM(require("fs"), 1); +var import_node_path9 = __toESM(require("path"), 1); +var cacheLogger3 = logger.child("cache"); +var LOCKFILE_NAMES = [ + "bun.lock", + "bun.lockb", + "package-lock.json", + "npm-shrinkwrap.json", + "pnpm-lock.yaml", + "yarn.lock" +]; +var METRO_CONFIG_EXTENSIONS = [ + "js", + "cjs", + "mjs", + "ts", + "cts", + "mts", + "json" +]; +var METRO_CONFIG_NAMES = METRO_CONFIG_EXTENSIONS.map((extension) => `metro.config.${extension}`); +var METRO_CONFIG_DIR_NAMES = METRO_CONFIG_EXTENSIONS.map((extension) => `.config/metro.${extension}`); +var BABEL_CONFIG_NAMES = [ + "babel.config.json", + "babel.config.js", + "babel.config.cjs", + "babel.config.mjs", + "babel.config.cts" +]; +var METRO_KEY_FILENAMES = [ + ...LOCKFILE_NAMES, + ...METRO_CONFIG_NAMES, + ...METRO_CONFIG_DIR_NAMES, + ...BABEL_CONFIG_NAMES +]; +var collectAncestorDirectories = (projectRoot, repoRoot) => { + const resolvedProjectRoot = import_node_path9.default.resolve(projectRoot); + const resolvedRepoRoot = import_node_path9.default.resolve(repoRoot); + if (resolvedProjectRoot === resolvedRepoRoot) { + return [resolvedProjectRoot]; + } + const relative = import_node_path9.default.relative(resolvedRepoRoot, resolvedProjectRoot); + const isProjectRootInsideRepoRoot = relative !== "" && !relative.startsWith(`..${import_node_path9.default.sep}`) && relative !== ".." && !import_node_path9.default.isAbsolute(relative); + if (!isProjectRootInsideRepoRoot) { + return [resolvedProjectRoot]; + } + const directories = []; + let current = resolvedProjectRoot; + for (; ; ) { + directories.push(current); + if (current === resolvedRepoRoot) { + break; + } + const parent = import_node_path9.default.dirname(current); + if (parent === current) { + break; + } + current = parent; + } + return directories; +}; +var collectKeyFiles = (directories, repoRoot) => { + const matches = []; + for (const directory of directories) { + for (const candidate of METRO_KEY_FILENAMES) { + const fullPath = import_node_path9.default.join(directory, candidate); + if (!import_node_fs8.default.existsSync(fullPath) || !import_node_fs8.default.statSync(fullPath).isFile()) { + continue; + } + matches.push({ + relativePath: import_node_path9.default.relative(repoRoot, fullPath).split(import_node_path9.default.sep).join("/"), + content: import_node_fs8.default.readFileSync(fullPath) + }); + } + } + return matches; +}; +var hashKeyFiles = (matches) => { + const hash = (0, import_node_crypto2.createHash)("sha256"); + for (const match of matches.slice().sort((a, b) => a.relativePath < b.relativePath ? -1 : 1)) { + hash.update(match.relativePath); + hash.update("\0"); + hash.update(match.content); + hash.update("\0"); + } + return hash.digest("hex"); +}; +var computeMetroStaticInputs = (options) => { + try { + const resolvedProjectRoot = import_node_path9.default.resolve(options.projectRoot); + const resolvedRepoRoot = import_node_path9.default.resolve(options.repoRoot); + if (!import_node_fs8.default.statSync(resolvedProjectRoot).isDirectory()) { + throw new Error(`projectRoot "${resolvedProjectRoot}" is not a directory`); + } + if (!import_node_fs8.default.statSync(resolvedRepoRoot).isDirectory()) { + throw new Error(`repoRoot "${resolvedRepoRoot}" is not a directory`); + } + const directories = collectAncestorDirectories(resolvedProjectRoot, resolvedRepoRoot); + const matches = collectKeyFiles(directories, resolvedRepoRoot); + return { + lockfileHash: hashKeyFiles(matches), + bundlerMetroVersion: options.bundlerMetroVersion, + ...options.salt ? { salt: options.salt } : {} + }; + } catch (error) { + cacheLogger3.warn(`Failed to compute Metro cache key inputs for "${options.projectRoot}". Falling back to an always-miss key.`, error); + return { + lockfileHash: "unavailable", + bundlerMetroVersion: options.bundlerMetroVersion + }; + } +}; + +// ../cache/dist/index.js +var cacheLogger4 = logger.child("cache"); +var createHarnessCache = (options) => { + const paths = createHarnessCachePaths(options.projectRoot); + return { + paths, + isWarm: (domain) => isDomainWarm(domain, paths), + ensureDomainDirectories: (domain) => { + for (const directory of getDomainDirectories(domain, paths)) { + try { + import_node_fs9.default.mkdirSync(directory, { recursive: true }); + } catch (error) { + cacheLogger4.warn(`Failed to create cache directory "${directory}". Continuing with a cold cache.`, error); + } + } + }, + planRestore: (inputs) => planRestore(paths, inputs), + snapshot: () => snapshot(paths), + planSave: (before, policy, inputs) => planSave(paths, before, policy, inputs), + writeKeysFile: (plan) => writeKeysFile(paths, plan) + }; +}; + +// ../../node_modules/zod/dist/esm/v3/external.js +var external_exports = {}; +__export(external_exports, { + BRAND: () => BRAND, + DIRTY: () => DIRTY, + EMPTY_PATH: () => EMPTY_PATH, + INVALID: () => INVALID, + NEVER: () => NEVER, + OK: () => OK, + ParseStatus: () => ParseStatus, + Schema: () => ZodType, + ZodAny: () => ZodAny, + ZodArray: () => ZodArray, + ZodBigInt: () => ZodBigInt, + ZodBoolean: () => ZodBoolean, + ZodBranded: () => ZodBranded, + ZodCatch: () => ZodCatch, + ZodDate: () => ZodDate, + ZodDefault: () => ZodDefault, + ZodDiscriminatedUnion: () => ZodDiscriminatedUnion, + ZodEffects: () => ZodEffects, + ZodEnum: () => ZodEnum, + ZodError: () => ZodError, + ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind, + ZodFunction: () => ZodFunction, + ZodIntersection: () => ZodIntersection, + ZodIssueCode: () => ZodIssueCode, + ZodLazy: () => ZodLazy, + ZodLiteral: () => ZodLiteral, + ZodMap: () => ZodMap, + ZodNaN: () => ZodNaN, + ZodNativeEnum: () => ZodNativeEnum, + ZodNever: () => ZodNever, + ZodNull: () => ZodNull, + ZodNullable: () => ZodNullable, + ZodNumber: () => ZodNumber, + ZodObject: () => ZodObject, + ZodOptional: () => ZodOptional, + ZodParsedType: () => ZodParsedType, + ZodPipeline: () => ZodPipeline, + ZodPromise: () => ZodPromise, + ZodReadonly: () => ZodReadonly, + ZodRecord: () => ZodRecord, + ZodSchema: () => ZodType, + ZodSet: () => ZodSet, + ZodString: () => ZodString, + ZodSymbol: () => ZodSymbol, + ZodTransformer: () => ZodEffects, + ZodTuple: () => ZodTuple, + ZodType: () => ZodType, + ZodUndefined: () => ZodUndefined, + ZodUnion: () => ZodUnion, + ZodUnknown: () => ZodUnknown, + ZodVoid: () => ZodVoid, + addIssueToContext: () => addIssueToContext, + any: () => anyType, + array: () => arrayType, + bigint: () => bigIntType, + boolean: () => booleanType, + coerce: () => coerce, + custom: () => custom, + date: () => dateType, + datetimeRegex: () => datetimeRegex, + defaultErrorMap: () => en_default, + discriminatedUnion: () => discriminatedUnionType, + effect: () => effectsType, + enum: () => enumType, + function: () => functionType, + getErrorMap: () => getErrorMap, + getParsedType: () => getParsedType, + instanceof: () => instanceOfType, + intersection: () => intersectionType, + isAborted: () => isAborted, + isAsync: () => isAsync, + isDirty: () => isDirty, + isValid: () => isValid, + late: () => late, + lazy: () => lazyType, + literal: () => literalType, + makeIssue: () => makeIssue, + map: () => mapType, + nan: () => nanType, + nativeEnum: () => nativeEnumType, + never: () => neverType, + null: () => nullType, + nullable: () => nullableType, + number: () => numberType, + object: () => objectType, + objectUtil: () => objectUtil, + oboolean: () => oboolean, + onumber: () => onumber, + optional: () => optionalType, + ostring: () => ostring, + pipeline: () => pipelineType, + preprocess: () => preprocessType, + promise: () => promiseType, + quotelessJson: () => quotelessJson, + record: () => recordType, + set: () => setType, + setErrorMap: () => setErrorMap, + strictObject: () => strictObjectType, + string: () => stringType, + symbol: () => symbolType, + transformer: () => effectsType, + tuple: () => tupleType, + undefined: () => undefinedType, + union: () => unionType, + unknown: () => unknownType, + util: () => util2, + void: () => voidType +}); + +// ../../node_modules/zod/dist/esm/v3/helpers/util.js +var util2; +(function(util3) { + util3.assertEqual = (_2) => { + }; + function assertIs(_arg) { + } + util3.assertIs = assertIs; + function assertNever(_x) { + throw new Error(); + } + util3.assertNever = assertNever; + util3.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util3.getValidEnumValues = (obj) => { + const validKeys = util3.objectKeys(obj).filter((k3) => typeof obj[obj[k3]] !== "number"); + const filtered = {}; + for (const k3 of validKeys) { + filtered[k3] = obj[k3]; + } + return util3.objectValues(filtered); + }; + util3.objectValues = (obj) => { + return util3.objectKeys(obj).map(function(e) { + return obj[e]; + }); + }; + util3.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => { + const keys = []; + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + keys.push(key); + } + } + return keys; + }; + util3.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return void 0; + }; + util3.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val; + function joinValues(array, separator = " | ") { + return array.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator); + } + util3.joinValues = joinValues; + util3.jsonStringifyReplacer = (_2, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util2 || (util2 = {})); +var objectUtil; +(function(objectUtil2) { + objectUtil2.mergeShapes = (first, second) => { + return { + ...first, + ...second + // second overwrites first + }; + }; +})(objectUtil || (objectUtil = {})); +var ZodParsedType = util2.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set" +]); +var getParsedType = (data) => { + const t2 = typeof data; + switch (t2) { + case "undefined": + return ZodParsedType.undefined; + case "string": + return ZodParsedType.string; + case "number": + return Number.isNaN(data) ? ZodParsedType.nan : ZodParsedType.number; + case "boolean": + return ZodParsedType.boolean; + case "function": + return ZodParsedType.function; + case "bigint": + return ZodParsedType.bigint; + case "symbol": + return ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return ZodParsedType.array; + } + if (data === null) { + return ZodParsedType.null; + } + if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") { + return ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return ZodParsedType.date; + } + return ZodParsedType.object; + default: + return ZodParsedType.unknown; + } +}; + +// ../../node_modules/zod/dist/esm/v3/ZodError.js +var ZodIssueCode = util2.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite" +]); +var quotelessJson = (obj) => { + const json = JSON.stringify(obj, null, 2); + return json.replace(/"([^"]+)":/g, "$1:"); +}; +var ZodError = class _ZodError extends Error { + get errors() { + return this.issues; + } + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(this, actualProto); + } else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + format(_mapper) { + const mapper = _mapper || function(issue) { + return issue.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error) => { + for (const issue of error.issues) { + if (issue.code === "invalid_union") { + issue.unionErrors.map(processError); + } else if (issue.code === "invalid_return_type") { + processError(issue.returnTypeError); + } else if (issue.code === "invalid_arguments") { + processError(issue.argumentsError); + } else if (issue.path.length === 0) { + fieldErrors._errors.push(mapper(issue)); + } else { + let curr = fieldErrors; + let i = 0; + while (i < issue.path.length) { + const el = issue.path[i]; + const terminal = i === issue.path.length - 1; + if (!terminal) { + curr[el] = curr[el] || { _errors: [] }; + } else { + curr[el] = curr[el] || { _errors: [] }; + curr[el]._errors.push(mapper(issue)); + } + curr = curr[el]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + static assert(value) { + if (!(value instanceof _ZodError)) { + throw new Error(`Not a ZodError: ${value}`); + } + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util2.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue) => issue.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +}; +ZodError.create = (issues) => { + const error = new ZodError(issues); + return error; +}; + +// ../../node_modules/zod/dist/esm/v3/locales/en.js +var errorMap = (issue, _ctx) => { + let message; + switch (issue.code) { + case ZodIssueCode.invalid_type: + if (issue.received === ZodParsedType.undefined) { + message = "Required"; + } else { + message = `Expected ${issue.expected}, received ${issue.received}`; + } + break; + case ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util2.jsonStringifyReplacer)}`; + break; + case ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util2.joinValues(issue.keys, ", ")}`; + break; + case ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util2.joinValues(issue.options)}`; + break; + case ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util2.joinValues(issue.options)}, received '${issue.received}'`; + break; + case ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodIssueCode.invalid_string: + if (typeof issue.validation === "object") { + if ("includes" in issue.validation) { + message = `Invalid input: must include "${issue.validation.includes}"`; + if (typeof issue.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`; + } + } else if ("startsWith" in issue.validation) { + message = `Invalid input: must start with "${issue.validation.startsWith}"`; + } else if ("endsWith" in issue.validation) { + message = `Invalid input: must end with "${issue.validation.endsWith}"`; + } else { + util2.assertNever(issue.validation); + } + } else if (issue.validation !== "regex") { + message = `Invalid ${issue.validation}`; + } else { + message = "Invalid"; + } + break; + case ZodIssueCode.too_small: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue.minimum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.too_big: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`; + else if (issue.type === "bigint") + message = `BigInt must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue.maximum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue.multipleOf}`; + break; + case ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util2.assertNever(issue); + } + return { message }; +}; +var en_default = errorMap; + +// ../../node_modules/zod/dist/esm/v3/errors.js +var overrideErrorMap = en_default; +function setErrorMap(map) { + overrideErrorMap = map; +} +function getErrorMap() { + return overrideErrorMap; +} + +// ../../node_modules/zod/dist/esm/v3/helpers/parseUtil.js +var makeIssue = (params) => { + const { data, path: path12, errorMaps, issueData } = params; + const fullPath = [...path12, ...issueData.path || []]; + const fullIssue = { + ...issueData, + path: fullPath + }; + if (issueData.message !== void 0) { + return { + ...issueData, + path: fullPath, + message: issueData.message + }; + } + let errorMessage = ""; + const maps = errorMaps.filter((m) => !!m).slice().reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: errorMessage + }; +}; +var EMPTY_PATH = []; +function addIssueToContext(ctx, issueData) { + const overrideMap = getErrorMap(); + const issue = makeIssue({ + issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + // contextual error map is first priority + ctx.schemaErrorMap, + // then schema-bound map if available + overrideMap, + // then global override map + overrideMap === en_default ? void 0 : en_default + // then global default map + ].filter((x2) => !!x2) + }); + ctx.common.issues.push(issue); +} +var ParseStatus = class _ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s of results) { + if (s.status === "aborted") + return INVALID; + if (s.status === "dirty") + status.dirty(); + arrayValue.push(s.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value + }); + } + return _ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return INVALID; + if (value.status === "aborted") + return INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +}; +var INVALID = Object.freeze({ + status: "aborted" +}); +var DIRTY = (value) => ({ status: "dirty", value }); +var OK = (value) => ({ status: "valid", value }); +var isAborted = (x2) => x2.status === "aborted"; +var isDirty = (x2) => x2.status === "dirty"; +var isValid = (x2) => x2.status === "valid"; +var isAsync = (x2) => typeof Promise !== "undefined" && x2 instanceof Promise; + +// ../../node_modules/zod/dist/esm/v3/helpers/errorUtil.js +var errorUtil; +(function(errorUtil2) { + errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message; +})(errorUtil || (errorUtil = {})); + +// ../../node_modules/zod/dist/esm/v3/types.js +var ParseInputLazyPath = class { + constructor(parent, value, path12, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path12; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (Array.isArray(this._key)) { + this._cachedPath.push(...this._path, ...this._key); + } else { + this._cachedPath.push(...this._path, this._key); + } + } + return this._cachedPath; + } +}; +var handleResult = (ctx, result) => { + if (isValid(result)) { + return { success: true, data: result.value }; + } else { + if (!ctx.common.issues.length) { + throw new Error("Validation failed but no issues detected."); + } + return { + success: false, + get error() { + if (this._error) + return this._error; + const error = new ZodError(ctx.common.issues); + this._error = error; + return this._error; + } + }; + } +}; +function processCreateParams(params) { + if (!params) + return {}; + const { errorMap: errorMap2, invalid_type_error, required_error, description } = params; + if (errorMap2 && (invalid_type_error || required_error)) { + throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); + } + if (errorMap2) + return { errorMap: errorMap2, description }; + const customMap = (iss, ctx) => { + const { message } = params; + if (iss.code === "invalid_enum_value") { + return { message: message ?? ctx.defaultError }; + } + if (typeof ctx.data === "undefined") { + return { message: message ?? required_error ?? ctx.defaultError }; + } + if (iss.code !== "invalid_type") + return { message: ctx.defaultError }; + return { message: message ?? invalid_type_error ?? ctx.defaultError }; + }; + return { errorMap: customMap, description }; +} +var ZodType = class { + get description() { + return this._def.description; + } + _getType(input) { + return getParsedType(input.data); + } + _getOrReturnCtx(input, ctx) { + return ctx || { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + }; + } + _processInputParams(input) { + return { + status: new ParseStatus(), + ctx: { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + } + }; + } + _parseSync(input) { + const result = this._parse(input); + if (isAsync(result)) { + throw new Error("Synchronous parse encountered promise."); + } + return result; + } + _parseAsync(input) { + const result = this._parse(input); + return Promise.resolve(result); + } + parse(data, params) { + const result = this.safeParse(data, params); + if (result.success) + return result.data; + throw result.error; + } + safeParse(data, params) { + const ctx = { + common: { + issues: [], + async: params?.async ?? false, + contextualErrorMap: params?.errorMap + }, + path: params?.path || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + const result = this._parseSync({ data, path: ctx.path, parent: ctx }); + return handleResult(ctx, result); + } + "~validate"(data) { + const ctx = { + common: { + issues: [], + async: !!this["~standard"].async + }, + path: [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + if (!this["~standard"].async) { + try { + const result = this._parseSync({ data, path: [], parent: ctx }); + return isValid(result) ? { + value: result.value + } : { + issues: ctx.common.issues + }; + } catch (err) { + if (err?.message?.toLowerCase()?.includes("encountered")) { + this["~standard"].async = true; + } + ctx.common = { + issues: [], + async: true + }; + } + } + return this._parseAsync({ data, path: [], parent: ctx }).then((result) => isValid(result) ? { + value: result.value + } : { + issues: ctx.common.issues + }); + } + async parseAsync(data, params) { + const result = await this.safeParseAsync(data, params); + if (result.success) + return result.data; + throw result.error; + } + async safeParseAsync(data, params) { + const ctx = { + common: { + issues: [], + contextualErrorMap: params?.errorMap, + async: true + }, + path: params?.path || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx }); + const result = await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult)); + return handleResult(ctx, result); + } + refine(check, message) { + const getIssueProperties = (val) => { + if (typeof message === "string" || typeof message === "undefined") { + return { message }; + } else if (typeof message === "function") { + return message(val); + } else { + return message; + } + }; + return this._refinement((val, ctx) => { + const result = check(val); + const setError = () => ctx.addIssue({ + code: ZodIssueCode.custom, + ...getIssueProperties(val) + }); + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then((data) => { + if (!data) { + setError(); + return false; + } else { + return true; + } + }); + } + if (!result) { + setError(); + return false; + } else { + return true; + } + }); + } + refinement(check, refinementData) { + return this._refinement((val, ctx) => { + if (!check(val)) { + ctx.addIssue(typeof refinementData === "function" ? refinementData(val, ctx) : refinementData); + return false; + } else { + return true; + } + }); + } + _refinement(refinement) { + return new ZodEffects({ + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "refinement", refinement } + }); + } + superRefine(refinement) { + return this._refinement(refinement); + } + constructor(def) { + this.spa = this.safeParseAsync; + this._def = def; + this.parse = this.parse.bind(this); + this.safeParse = this.safeParse.bind(this); + this.parseAsync = this.parseAsync.bind(this); + this.safeParseAsync = this.safeParseAsync.bind(this); + this.spa = this.spa.bind(this); + this.refine = this.refine.bind(this); + this.refinement = this.refinement.bind(this); + this.superRefine = this.superRefine.bind(this); + this.optional = this.optional.bind(this); + this.nullable = this.nullable.bind(this); + this.nullish = this.nullish.bind(this); + this.array = this.array.bind(this); + this.promise = this.promise.bind(this); + this.or = this.or.bind(this); + this.and = this.and.bind(this); + this.transform = this.transform.bind(this); + this.brand = this.brand.bind(this); + this.default = this.default.bind(this); + this.catch = this.catch.bind(this); + this.describe = this.describe.bind(this); + this.pipe = this.pipe.bind(this); + this.readonly = this.readonly.bind(this); + this.isNullable = this.isNullable.bind(this); + this.isOptional = this.isOptional.bind(this); + this["~standard"] = { + version: 1, + vendor: "zod", + validate: (data) => this["~validate"](data) + }; + } + optional() { + return ZodOptional.create(this, this._def); + } + nullable() { + return ZodNullable.create(this, this._def); + } + nullish() { + return this.nullable().optional(); + } + array() { + return ZodArray.create(this); + } + promise() { + return ZodPromise.create(this, this._def); + } + or(option) { + return ZodUnion.create([this, option], this._def); + } + and(incoming) { + return ZodIntersection.create(this, incoming, this._def); + } + transform(transform) { + return new ZodEffects({ + ...processCreateParams(this._def), + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "transform", transform } + }); + } + default(def) { + const defaultValueFunc = typeof def === "function" ? def : () => def; + return new ZodDefault({ + ...processCreateParams(this._def), + innerType: this, + defaultValue: defaultValueFunc, + typeName: ZodFirstPartyTypeKind.ZodDefault + }); + } + brand() { + return new ZodBranded({ + typeName: ZodFirstPartyTypeKind.ZodBranded, + type: this, + ...processCreateParams(this._def) + }); + } + catch(def) { + const catchValueFunc = typeof def === "function" ? def : () => def; + return new ZodCatch({ + ...processCreateParams(this._def), + innerType: this, + catchValue: catchValueFunc, + typeName: ZodFirstPartyTypeKind.ZodCatch + }); + } + describe(description) { + const This = this.constructor; + return new This({ + ...this._def, + description + }); + } + pipe(target) { + return ZodPipeline.create(this, target); + } + readonly() { + return ZodReadonly.create(this); + } + isOptional() { + return this.safeParse(void 0).success; + } + isNullable() { + return this.safeParse(null).success; + } +}; +var cuidRegex = /^c[^\s-]{8,}$/i; +var cuid2Regex = /^[0-9a-z]+$/; +var ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; +var uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i; +var nanoidRegex = /^[a-z0-9_-]{21}$/i; +var jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/; +var durationRegex = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/; +var emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i; +var _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`; +var emojiRegex; +var ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; +var ipv4CidrRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/; +var ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; +var ipv6CidrRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/; +var base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; +var base64urlRegex = /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/; +var dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`; +var dateRegex = new RegExp(`^${dateRegexSource}$`); +function timeRegexSource(args) { + let secondsRegexSource = `[0-5]\\d`; + if (args.precision) { + secondsRegexSource = `${secondsRegexSource}\\.\\d{${args.precision}}`; + } else if (args.precision == null) { + secondsRegexSource = `${secondsRegexSource}(\\.\\d+)?`; + } + const secondsQuantifier = args.precision ? "+" : "?"; + return `([01]\\d|2[0-3]):[0-5]\\d(:${secondsRegexSource})${secondsQuantifier}`; +} +function timeRegex(args) { + return new RegExp(`^${timeRegexSource(args)}$`); +} +function datetimeRegex(args) { + let regex = `${dateRegexSource}T${timeRegexSource(args)}`; + const opts = []; + opts.push(args.local ? `Z?` : `Z`); + if (args.offset) + opts.push(`([+-]\\d{2}:?\\d{2})`); + regex = `${regex}(${opts.join("|")})`; + return new RegExp(`^${regex}$`); +} +function isValidIP(ip, version) { + if ((version === "v4" || !version) && ipv4Regex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6Regex.test(ip)) { + return true; + } + return false; +} +function isValidJWT(jwt, alg) { + if (!jwtRegex.test(jwt)) + return false; + try { + const [header] = jwt.split("."); + const base64 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "="); + const decoded = JSON.parse(atob(base64)); + if (typeof decoded !== "object" || decoded === null) + return false; + if ("typ" in decoded && decoded?.typ !== "JWT") + return false; + if (!decoded.alg) + return false; + if (alg && decoded.alg !== alg) + return false; + return true; + } catch { + return false; + } +} +function isValidCidr(ip, version) { + if ((version === "v4" || !version) && ipv4CidrRegex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6CidrRegex.test(ip)) { + return true; + } + return false; +} +var ZodString = class _ZodString extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = String(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.string) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.string, + received: ctx2.parsedType + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.length < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + if (input.data.length > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "length") { + const tooBig = input.data.length > check.value; + const tooSmall = input.data.length < check.value; + if (tooBig || tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + if (tooBig) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message + }); + } else if (tooSmall) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message + }); + } + status.dirty(); + } + } else if (check.kind === "email") { + if (!emailRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "email", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "emoji") { + if (!emojiRegex) { + emojiRegex = new RegExp(_emojiRegex, "u"); + } + if (!emojiRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "emoji", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "uuid") { + if (!uuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "uuid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "nanoid") { + if (!nanoidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "nanoid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cuid") { + if (!cuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cuid2") { + if (!cuid2Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid2", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "ulid") { + if (!ulidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ulid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "url") { + try { + new URL(input.data); + } catch { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "url", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "regex") { + check.regex.lastIndex = 0; + const testResult = check.regex.test(input.data); + if (!testResult) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "regex", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "trim") { + input.data = input.data.trim(); + } else if (check.kind === "includes") { + if (!input.data.includes(check.value, check.position)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { includes: check.value, position: check.position }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "toLowerCase") { + input.data = input.data.toLowerCase(); + } else if (check.kind === "toUpperCase") { + input.data = input.data.toUpperCase(); + } else if (check.kind === "startsWith") { + if (!input.data.startsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { startsWith: check.value }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "endsWith") { + if (!input.data.endsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { endsWith: check.value }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "datetime") { + const regex = datetimeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "datetime", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "date") { + const regex = dateRegex; + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "date", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "time") { + const regex = timeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "time", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "duration") { + if (!durationRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "duration", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "ip") { + if (!isValidIP(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ip", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "jwt") { + if (!isValidJWT(input.data, check.alg)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "jwt", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cidr") { + if (!isValidCidr(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cidr", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "base64") { + if (!base64Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "base64url") { + if (!base64urlRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64url", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _regex(regex, validation, message) { + return this.refinement((data) => regex.test(data), { + validation, + code: ZodIssueCode.invalid_string, + ...errorUtil.errToObj(message) + }); + } + _addCheck(check) { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + email(message) { + return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) }); + } + url(message) { + return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) }); + } + emoji(message) { + return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) }); + } + uuid(message) { + return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) }); + } + nanoid(message) { + return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(message) }); + } + cuid(message) { + return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) }); + } + cuid2(message) { + return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) }); + } + ulid(message) { + return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) }); + } + base64(message) { + return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message) }); + } + base64url(message) { + return this._addCheck({ + kind: "base64url", + ...errorUtil.errToObj(message) + }); + } + jwt(options) { + return this._addCheck({ kind: "jwt", ...errorUtil.errToObj(options) }); + } + ip(options) { + return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) }); + } + cidr(options) { + return this._addCheck({ kind: "cidr", ...errorUtil.errToObj(options) }); + } + datetime(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "datetime", + precision: null, + offset: false, + local: false, + message: options + }); + } + return this._addCheck({ + kind: "datetime", + precision: typeof options?.precision === "undefined" ? null : options?.precision, + offset: options?.offset ?? false, + local: options?.local ?? false, + ...errorUtil.errToObj(options?.message) + }); + } + date(message) { + return this._addCheck({ kind: "date", message }); + } + time(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "time", + precision: null, + message: options + }); + } + return this._addCheck({ + kind: "time", + precision: typeof options?.precision === "undefined" ? null : options?.precision, + ...errorUtil.errToObj(options?.message) + }); + } + duration(message) { + return this._addCheck({ kind: "duration", ...errorUtil.errToObj(message) }); + } + regex(regex, message) { + return this._addCheck({ + kind: "regex", + regex, + ...errorUtil.errToObj(message) + }); + } + includes(value, options) { + return this._addCheck({ + kind: "includes", + value, + position: options?.position, + ...errorUtil.errToObj(options?.message) + }); + } + startsWith(value, message) { + return this._addCheck({ + kind: "startsWith", + value, + ...errorUtil.errToObj(message) + }); + } + endsWith(value, message) { + return this._addCheck({ + kind: "endsWith", + value, + ...errorUtil.errToObj(message) + }); + } + min(minLength, message) { + return this._addCheck({ + kind: "min", + value: minLength, + ...errorUtil.errToObj(message) + }); + } + max(maxLength, message) { + return this._addCheck({ + kind: "max", + value: maxLength, + ...errorUtil.errToObj(message) + }); + } + length(len, message) { + return this._addCheck({ + kind: "length", + value: len, + ...errorUtil.errToObj(message) + }); + } + /** + * Equivalent to `.min(1)` + */ + nonempty(message) { + return this.min(1, errorUtil.errToObj(message)); + } + trim() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "trim" }] + }); + } + toLowerCase() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toLowerCase" }] + }); + } + toUpperCase() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toUpperCase" }] + }); + } + get isDatetime() { + return !!this._def.checks.find((ch) => ch.kind === "datetime"); + } + get isDate() { + return !!this._def.checks.find((ch) => ch.kind === "date"); + } + get isTime() { + return !!this._def.checks.find((ch) => ch.kind === "time"); + } + get isDuration() { + return !!this._def.checks.find((ch) => ch.kind === "duration"); + } + get isEmail() { + return !!this._def.checks.find((ch) => ch.kind === "email"); + } + get isURL() { + return !!this._def.checks.find((ch) => ch.kind === "url"); + } + get isEmoji() { + return !!this._def.checks.find((ch) => ch.kind === "emoji"); + } + get isUUID() { + return !!this._def.checks.find((ch) => ch.kind === "uuid"); + } + get isNANOID() { + return !!this._def.checks.find((ch) => ch.kind === "nanoid"); + } + get isCUID() { + return !!this._def.checks.find((ch) => ch.kind === "cuid"); + } + get isCUID2() { + return !!this._def.checks.find((ch) => ch.kind === "cuid2"); + } + get isULID() { + return !!this._def.checks.find((ch) => ch.kind === "ulid"); + } + get isIP() { + return !!this._def.checks.find((ch) => ch.kind === "ip"); + } + get isCIDR() { + return !!this._def.checks.find((ch) => ch.kind === "cidr"); + } + get isBase64() { + return !!this._def.checks.find((ch) => ch.kind === "base64"); + } + get isBase64url() { + return !!this._def.checks.find((ch) => ch.kind === "base64url"); + } + get minLength() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxLength() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodString.create = (params) => { + return new ZodString({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodString, + coerce: params?.coerce ?? false, + ...processCreateParams(params) + }); +}; +function floatSafeRemainder(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = Number.parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", "")); + return valInt % stepInt / 10 ** decCount; +} +var ZodNumber = class _ZodNumber extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + this.step = this.multipleOf; + } + _parse(input) { + if (this._def.coerce) { + input.data = Number(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.number) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.number, + received: ctx2.parsedType + }); + return INVALID; + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "int") { + if (!util2.isInteger(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: "integer", + received: "float", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "min") { + const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "multipleOf") { + if (floatSafeRemainder(input.data, check.value) !== 0) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "finite") { + if (!Number.isFinite(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_finite, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodNumber({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check) { + return new _ZodNumber({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + int(message) { + return this._addCheck({ + kind: "int", + message: errorUtil.toString(message) + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + finite(message) { + return this._addCheck({ + kind: "finite", + message: errorUtil.toString(message) + }); + } + safe(message) { + return this._addCheck({ + kind: "min", + inclusive: true, + value: Number.MIN_SAFE_INTEGER, + message: errorUtil.toString(message) + })._addCheck({ + kind: "max", + inclusive: true, + value: Number.MAX_SAFE_INTEGER, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } + get isInt() { + return !!this._def.checks.find((ch) => ch.kind === "int" || ch.kind === "multipleOf" && util2.isInteger(ch.value)); + } + get isFinite() { + let max = null; + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "finite" || ch.kind === "int" || ch.kind === "multipleOf") { + return true; + } else if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } else if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return Number.isFinite(min) && Number.isFinite(max); + } +}; +ZodNumber.create = (params) => { + return new ZodNumber({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodNumber, + coerce: params?.coerce || false, + ...processCreateParams(params) + }); +}; +var ZodBigInt = class _ZodBigInt extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + } + _parse(input) { + if (this._def.coerce) { + try { + input.data = BigInt(input.data); + } catch { + return this._getInvalidInput(input); + } + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.bigint) { + return this._getInvalidInput(input); + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "min") { + const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + type: "bigint", + minimum: check.value, + inclusive: check.inclusive, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + type: "bigint", + maximum: check.value, + inclusive: check.inclusive, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "multipleOf") { + if (input.data % check.value !== BigInt(0)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _getInvalidInput(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.bigint, + received: ctx.parsedType + }); + return INVALID; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodBigInt({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check) { + return new _ZodBigInt({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodBigInt.create = (params) => { + return new ZodBigInt({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodBigInt, + coerce: params?.coerce ?? false, + ...processCreateParams(params) + }); +}; +var ZodBoolean = class extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = Boolean(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.boolean) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.boolean, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodBoolean.create = (params) => { + return new ZodBoolean({ + typeName: ZodFirstPartyTypeKind.ZodBoolean, + coerce: params?.coerce || false, + ...processCreateParams(params) + }); +}; +var ZodDate = class _ZodDate extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = new Date(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.date) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.date, + received: ctx2.parsedType + }); + return INVALID; + } + if (Number.isNaN(input.data.getTime())) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_date + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.getTime() < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + message: check.message, + inclusive: true, + exact: false, + minimum: check.value, + type: "date" + }); + status.dirty(); + } + } else if (check.kind === "max") { + if (input.data.getTime() > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + message: check.message, + inclusive: true, + exact: false, + maximum: check.value, + type: "date" + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { + status: status.value, + value: new Date(input.data.getTime()) + }; + } + _addCheck(check) { + return new _ZodDate({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + min(minDate, message) { + return this._addCheck({ + kind: "min", + value: minDate.getTime(), + message: errorUtil.toString(message) + }); + } + max(maxDate, message) { + return this._addCheck({ + kind: "max", + value: maxDate.getTime(), + message: errorUtil.toString(message) + }); + } + get minDate() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min != null ? new Date(min) : null; + } + get maxDate() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max != null ? new Date(max) : null; + } +}; +ZodDate.create = (params) => { + return new ZodDate({ + checks: [], + coerce: params?.coerce || false, + typeName: ZodFirstPartyTypeKind.ZodDate, + ...processCreateParams(params) + }); +}; +var ZodSymbol = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodSymbol.create = (params) => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params) + }); +}; +var ZodUndefined = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.undefined, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodUndefined.create = (params) => { + return new ZodUndefined({ + typeName: ZodFirstPartyTypeKind.ZodUndefined, + ...processCreateParams(params) + }); +}; +var ZodNull = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.null) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.null, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodNull.create = (params) => { + return new ZodNull({ + typeName: ZodFirstPartyTypeKind.ZodNull, + ...processCreateParams(params) + }); +}; +var ZodAny = class extends ZodType { + constructor() { + super(...arguments); + this._any = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodAny.create = (params) => { + return new ZodAny({ + typeName: ZodFirstPartyTypeKind.ZodAny, + ...processCreateParams(params) + }); +}; +var ZodUnknown = class extends ZodType { + constructor() { + super(...arguments); + this._unknown = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodUnknown.create = (params) => { + return new ZodUnknown({ + typeName: ZodFirstPartyTypeKind.ZodUnknown, + ...processCreateParams(params) + }); +}; +var ZodNever = class extends ZodType { + _parse(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.never, + received: ctx.parsedType + }); + return INVALID; + } +}; +ZodNever.create = (params) => { + return new ZodNever({ + typeName: ZodFirstPartyTypeKind.ZodNever, + ...processCreateParams(params) + }); +}; +var ZodVoid = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.void, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodVoid.create = (params) => { + return new ZodVoid({ + typeName: ZodFirstPartyTypeKind.ZodVoid, + ...processCreateParams(params) + }); +}; +var ZodArray = class _ZodArray extends ZodType { + _parse(input) { + const { ctx, status } = this._processInputParams(input); + const def = this._def; + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (def.exactLength !== null) { + const tooBig = ctx.data.length > def.exactLength.value; + const tooSmall = ctx.data.length < def.exactLength.value; + if (tooBig || tooSmall) { + addIssueToContext(ctx, { + code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small, + minimum: tooSmall ? def.exactLength.value : void 0, + maximum: tooBig ? def.exactLength.value : void 0, + type: "array", + inclusive: true, + exact: true, + message: def.exactLength.message + }); + status.dirty(); + } + } + if (def.minLength !== null) { + if (ctx.data.length < def.minLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.minLength.message + }); + status.dirty(); + } + } + if (def.maxLength !== null) { + if (ctx.data.length > def.maxLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.maxLength.message + }); + status.dirty(); + } + } + if (ctx.common.async) { + return Promise.all([...ctx.data].map((item, i) => { + return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + })).then((result2) => { + return ParseStatus.mergeArray(status, result2); + }); + } + const result = [...ctx.data].map((item, i) => { + return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + }); + return ParseStatus.mergeArray(status, result); + } + get element() { + return this._def.type; + } + min(minLength, message) { + return new _ZodArray({ + ...this._def, + minLength: { value: minLength, message: errorUtil.toString(message) } + }); + } + max(maxLength, message) { + return new _ZodArray({ + ...this._def, + maxLength: { value: maxLength, message: errorUtil.toString(message) } + }); + } + length(len, message) { + return new _ZodArray({ + ...this._def, + exactLength: { value: len, message: errorUtil.toString(message) } + }); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodArray.create = (schema, params) => { + return new ZodArray({ + type: schema, + minLength: null, + maxLength: null, + exactLength: null, + typeName: ZodFirstPartyTypeKind.ZodArray, + ...processCreateParams(params) + }); +}; +function deepPartialify(schema) { + if (schema instanceof ZodObject) { + const newShape = {}; + for (const key in schema.shape) { + const fieldSchema = schema.shape[key]; + newShape[key] = ZodOptional.create(deepPartialify(fieldSchema)); + } + return new ZodObject({ + ...schema._def, + shape: () => newShape + }); + } else if (schema instanceof ZodArray) { + return new ZodArray({ + ...schema._def, + type: deepPartialify(schema.element) + }); + } else if (schema instanceof ZodOptional) { + return ZodOptional.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodNullable) { + return ZodNullable.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodTuple) { + return ZodTuple.create(schema.items.map((item) => deepPartialify(item))); + } else { + return schema; + } +} +var ZodObject = class _ZodObject extends ZodType { + constructor() { + super(...arguments); + this._cached = null; + this.nonstrict = this.passthrough; + this.augment = this.extend; + } + _getCached() { + if (this._cached !== null) + return this._cached; + const shape = this._def.shape(); + const keys = util2.objectKeys(shape); + this._cached = { shape, keys }; + return this._cached; + } + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.object) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx2.parsedType + }); + return INVALID; + } + const { status, ctx } = this._processInputParams(input); + const { shape, keys: shapeKeys } = this._getCached(); + const extraKeys = []; + if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) { + for (const key in ctx.data) { + if (!shapeKeys.includes(key)) { + extraKeys.push(key); + } + } + } + const pairs = []; + for (const key of shapeKeys) { + const keyValidator = shape[key]; + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (this._def.catchall instanceof ZodNever) { + const unknownKeys = this._def.unknownKeys; + if (unknownKeys === "passthrough") { + for (const key of extraKeys) { + pairs.push({ + key: { status: "valid", value: key }, + value: { status: "valid", value: ctx.data[key] } + }); + } + } else if (unknownKeys === "strict") { + if (extraKeys.length > 0) { + addIssueToContext(ctx, { + code: ZodIssueCode.unrecognized_keys, + keys: extraKeys + }); + status.dirty(); + } + } else if (unknownKeys === "strip") { + } else { + throw new Error(`Internal ZodObject error: invalid unknownKeys value.`); + } + } else { + const catchall = this._def.catchall; + for (const key of extraKeys) { + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: catchall._parse( + new ParseInputLazyPath(ctx, value, ctx.path, key) + //, ctx.child(key), value, getParsedType(value) + ), + alwaysSet: key in ctx.data + }); + } + } + if (ctx.common.async) { + return Promise.resolve().then(async () => { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value, + alwaysSet: pair.alwaysSet + }); + } + return syncPairs; + }).then((syncPairs) => { + return ParseStatus.mergeObjectSync(status, syncPairs); + }); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get shape() { + return this._def.shape(); + } + strict(message) { + errorUtil.errToObj; + return new _ZodObject({ + ...this._def, + unknownKeys: "strict", + ...message !== void 0 ? { + errorMap: (issue, ctx) => { + const defaultError = this._def.errorMap?.(issue, ctx).message ?? ctx.defaultError; + if (issue.code === "unrecognized_keys") + return { + message: errorUtil.errToObj(message).message ?? defaultError + }; + return { + message: defaultError + }; + } + } : {} + }); + } + strip() { + return new _ZodObject({ + ...this._def, + unknownKeys: "strip" + }); + } + passthrough() { + return new _ZodObject({ + ...this._def, + unknownKeys: "passthrough" + }); + } + // const AugmentFactory = + // (def: Def) => + // ( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, Augmentation>, + // Def["unknownKeys"], + // Def["catchall"] + // > => { + // return new ZodObject({ + // ...def, + // shape: () => ({ + // ...def.shape(), + // ...augmentation, + // }), + // }) as any; + // }; + extend(augmentation) { + return new _ZodObject({ + ...this._def, + shape: () => ({ + ...this._def.shape(), + ...augmentation + }) + }); + } + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging) { + const merged = new _ZodObject({ + unknownKeys: merging._def.unknownKeys, + catchall: merging._def.catchall, + shape: () => ({ + ...this._def.shape(), + ...merging._def.shape() + }), + typeName: ZodFirstPartyTypeKind.ZodObject + }); + return merged; + } + // merge< + // Incoming extends AnyZodObject, + // Augmentation extends Incoming["shape"], + // NewOutput extends { + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }, + // NewInput extends { + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // } + // >( + // merging: Incoming + // ): ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"], + // NewOutput, + // NewInput + // > { + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + setKey(key, schema) { + return this.augment({ [key]: schema }); + } + // merge( + // merging: Incoming + // ): //ZodObject = (merging) => { + // ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"] + // > { + // // const mergedShape = objectUtil.mergeShapes( + // // this._def.shape(), + // // merging._def.shape() + // // ); + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + catchall(index) { + return new _ZodObject({ + ...this._def, + catchall: index + }); + } + pick(mask) { + const shape = {}; + for (const key of util2.objectKeys(mask)) { + if (mask[key] && this.shape[key]) { + shape[key] = this.shape[key]; + } + } + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + omit(mask) { + const shape = {}; + for (const key of util2.objectKeys(this.shape)) { + if (!mask[key]) { + shape[key] = this.shape[key]; + } + } + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + /** + * @deprecated + */ + deepPartial() { + return deepPartialify(this); + } + partial(mask) { + const newShape = {}; + for (const key of util2.objectKeys(this.shape)) { + const fieldSchema = this.shape[key]; + if (mask && !mask[key]) { + newShape[key] = fieldSchema; + } else { + newShape[key] = fieldSchema.optional(); + } + } + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + required(mask) { + const newShape = {}; + for (const key of util2.objectKeys(this.shape)) { + if (mask && !mask[key]) { + newShape[key] = this.shape[key]; + } else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = newField._def.innerType; + } + newShape[key] = newField; + } + } + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + keyof() { + return createZodEnum(util2.objectKeys(this.shape)); + } +}; +ZodObject.create = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject.strictCreate = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strict", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject.lazycreate = (shape, params) => { + return new ZodObject({ + shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +var ZodUnion = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const options = this._def.options; + function handleResults(results) { + for (const result of results) { + if (result.result.status === "valid") { + return result.result; + } + } + for (const result of results) { + if (result.result.status === "dirty") { + ctx.common.issues.push(...result.ctx.common.issues); + return result.result; + } + } + const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + if (ctx.common.async) { + return Promise.all(options.map(async (option) => { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + return { + result: await option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }), + ctx: childCtx + }; + })).then(handleResults); + } else { + let dirty = void 0; + const issues = []; + for (const option of options) { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }); + if (result.status === "valid") { + return result; + } else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + if (childCtx.common.issues.length) { + issues.push(childCtx.common.issues); + } + } + if (dirty) { + ctx.common.issues.push(...dirty.ctx.common.issues); + return dirty.result; + } + const unionErrors = issues.map((issues2) => new ZodError(issues2)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + } + get options() { + return this._def.options; + } +}; +ZodUnion.create = (types, params) => { + return new ZodUnion({ + options: types, + typeName: ZodFirstPartyTypeKind.ZodUnion, + ...processCreateParams(params) + }); +}; +var getDiscriminator = (type) => { + if (type instanceof ZodLazy) { + return getDiscriminator(type.schema); + } else if (type instanceof ZodEffects) { + return getDiscriminator(type.innerType()); + } else if (type instanceof ZodLiteral) { + return [type.value]; + } else if (type instanceof ZodEnum) { + return type.options; + } else if (type instanceof ZodNativeEnum) { + return util2.objectValues(type.enum); + } else if (type instanceof ZodDefault) { + return getDiscriminator(type._def.innerType); + } else if (type instanceof ZodUndefined) { + return [void 0]; + } else if (type instanceof ZodNull) { + return [null]; + } else if (type instanceof ZodOptional) { + return [void 0, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodNullable) { + return [null, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodBranded) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodReadonly) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodCatch) { + return getDiscriminator(type._def.innerType); + } else { + return []; + } +}; +var ZodDiscriminatedUnion = class _ZodDiscriminatedUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const discriminator = this.discriminator; + const discriminatorValue = ctx.data[discriminator]; + const option = this.optionsMap.get(discriminatorValue); + if (!option) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union_discriminator, + options: Array.from(this.optionsMap.keys()), + path: [discriminator] + }); + return INVALID; + } + if (ctx.common.async) { + return option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } else { + return option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } + } + get discriminator() { + return this._def.discriminator; + } + get options() { + return this._def.options; + } + get optionsMap() { + return this._def.optionsMap; + } + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create(discriminator, options, params) { + const optionsMap = /* @__PURE__ */ new Map(); + for (const type of options) { + const discriminatorValues = getDiscriminator(type.shape[discriminator]); + if (!discriminatorValues.length) { + throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`); + } + for (const value of discriminatorValues) { + if (optionsMap.has(value)) { + throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`); + } + optionsMap.set(value, type); + } + } + return new _ZodDiscriminatedUnion({ + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + discriminator, + options, + optionsMap, + ...processCreateParams(params) + }); + } +}; +function mergeValues(a, b) { + const aType = getParsedType(a); + const bType = getParsedType(b); + if (a === b) { + return { valid: true, data: a }; + } else if (aType === ZodParsedType.object && bType === ZodParsedType.object) { + const bKeys = util2.objectKeys(b); + const sharedKeys = util2.objectKeys(a).filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a, ...b }; + for (const key of sharedKeys) { + const sharedValue = mergeValues(a[key], b[key]); + if (!sharedValue.valid) { + return { valid: false }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } else if (aType === ZodParsedType.array && bType === ZodParsedType.array) { + if (a.length !== b.length) { + return { valid: false }; + } + const newArray = []; + for (let index = 0; index < a.length; index++) { + const itemA = a[index]; + const itemB = b[index]; + const sharedValue = mergeValues(itemA, itemB); + if (!sharedValue.valid) { + return { valid: false }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } else if (aType === ZodParsedType.date && bType === ZodParsedType.date && +a === +b) { + return { valid: true, data: a }; + } else { + return { valid: false }; + } +} +var ZodIntersection = class extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const handleParsed = (parsedLeft, parsedRight) => { + if (isAborted(parsedLeft) || isAborted(parsedRight)) { + return INVALID; + } + const merged = mergeValues(parsedLeft.value, parsedRight.value); + if (!merged.valid) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_intersection_types + }); + return INVALID; + } + if (isDirty(parsedLeft) || isDirty(parsedRight)) { + status.dirty(); + } + return { status: status.value, value: merged.data }; + }; + if (ctx.common.async) { + return Promise.all([ + this._def.left._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), + this._def.right._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }) + ]).then(([left, right]) => handleParsed(left, right)); + } else { + return handleParsed(this._def.left._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), this._def.right._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + })); + } + } +}; +ZodIntersection.create = (left, right, params) => { + return new ZodIntersection({ + left, + right, + typeName: ZodFirstPartyTypeKind.ZodIntersection, + ...processCreateParams(params) + }); +}; +var ZodTuple = class _ZodTuple extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (ctx.data.length < this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + return INVALID; + } + const rest = this._def.rest; + if (!rest && ctx.data.length > this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + status.dirty(); + } + const items = [...ctx.data].map((item, itemIndex) => { + const schema = this._def.items[itemIndex] || this._def.rest; + if (!schema) + return null; + return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex)); + }).filter((x2) => !!x2); + if (ctx.common.async) { + return Promise.all(items).then((results) => { + return ParseStatus.mergeArray(status, results); + }); + } else { + return ParseStatus.mergeArray(status, items); + } + } + get items() { + return this._def.items; + } + rest(rest) { + return new _ZodTuple({ + ...this._def, + rest + }); + } +}; +ZodTuple.create = (schemas, params) => { + if (!Array.isArray(schemas)) { + throw new Error("You must pass an array of schemas to z.tuple([ ... ])"); + } + return new ZodTuple({ + items: schemas, + typeName: ZodFirstPartyTypeKind.ZodTuple, + rest: null, + ...processCreateParams(params) + }); +}; +var ZodRecord = class _ZodRecord extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const pairs = []; + const keyType = this._def.keyType; + const valueType = this._def.valueType; + for (const key in ctx.data) { + pairs.push({ + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)), + value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (ctx.common.async) { + return ParseStatus.mergeObjectAsync(status, pairs); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get element() { + return this._def.valueType; + } + static create(first, second, third) { + if (second instanceof ZodType) { + return new _ZodRecord({ + keyType: first, + valueType: second, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(third) + }); + } + return new _ZodRecord({ + keyType: ZodString.create(), + valueType: first, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(second) + }); + } +}; +var ZodMap = class extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.map) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.map, + received: ctx.parsedType + }); + return INVALID; + } + const keyType = this._def.keyType; + const valueType = this._def.valueType; + const pairs = [...ctx.data.entries()].map(([key, value], index) => { + return { + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])), + value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])) + }; + }); + if (ctx.common.async) { + const finalMap = /* @__PURE__ */ new Map(); + return Promise.resolve().then(async () => { + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + }); + } else { + const finalMap = /* @__PURE__ */ new Map(); + for (const pair of pairs) { + const key = pair.key; + const value = pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + } + } +}; +ZodMap.create = (keyType, valueType, params) => { + return new ZodMap({ + valueType, + keyType, + typeName: ZodFirstPartyTypeKind.ZodMap, + ...processCreateParams(params) + }); +}; +var ZodSet = class _ZodSet extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.set) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.set, + received: ctx.parsedType + }); + return INVALID; + } + const def = this._def; + if (def.minSize !== null) { + if (ctx.data.size < def.minSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.minSize.message + }); + status.dirty(); + } + } + if (def.maxSize !== null) { + if (ctx.data.size > def.maxSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.maxSize.message + }); + status.dirty(); + } + } + const valueType = this._def.valueType; + function finalizeSet(elements2) { + const parsedSet = /* @__PURE__ */ new Set(); + for (const element of elements2) { + if (element.status === "aborted") + return INVALID; + if (element.status === "dirty") + status.dirty(); + parsedSet.add(element.value); + } + return { status: status.value, value: parsedSet }; + } + const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i))); + if (ctx.common.async) { + return Promise.all(elements).then((elements2) => finalizeSet(elements2)); + } else { + return finalizeSet(elements); + } + } + min(minSize, message) { + return new _ZodSet({ + ...this._def, + minSize: { value: minSize, message: errorUtil.toString(message) } + }); + } + max(maxSize, message) { + return new _ZodSet({ + ...this._def, + maxSize: { value: maxSize, message: errorUtil.toString(message) } + }); + } + size(size, message) { + return this.min(size, message).max(size, message); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodSet.create = (valueType, params) => { + return new ZodSet({ + valueType, + minSize: null, + maxSize: null, + typeName: ZodFirstPartyTypeKind.ZodSet, + ...processCreateParams(params) + }); +}; +var ZodFunction = class _ZodFunction extends ZodType { + constructor() { + super(...arguments); + this.validate = this.implement; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.function) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.function, + received: ctx.parsedType + }); + return INVALID; + } + function makeArgsIssue(args, error) { + return makeIssue({ + data: args, + path: ctx.path, + errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default].filter((x2) => !!x2), + issueData: { + code: ZodIssueCode.invalid_arguments, + argumentsError: error + } + }); + } + function makeReturnsIssue(returns, error) { + return makeIssue({ + data: returns, + path: ctx.path, + errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default].filter((x2) => !!x2), + issueData: { + code: ZodIssueCode.invalid_return_type, + returnTypeError: error + } + }); + } + const params = { errorMap: ctx.common.contextualErrorMap }; + const fn = ctx.data; + if (this._def.returns instanceof ZodPromise) { + const me2 = this; + return OK(async function(...args) { + const error = new ZodError([]); + const parsedArgs = await me2._def.args.parseAsync(args, params).catch((e) => { + error.addIssue(makeArgsIssue(args, e)); + throw error; + }); + const result = await Reflect.apply(fn, this, parsedArgs); + const parsedReturns = await me2._def.returns._def.type.parseAsync(result, params).catch((e) => { + error.addIssue(makeReturnsIssue(result, e)); + throw error; + }); + return parsedReturns; + }); + } else { + const me2 = this; + return OK(function(...args) { + const parsedArgs = me2._def.args.safeParse(args, params); + if (!parsedArgs.success) { + throw new ZodError([makeArgsIssue(args, parsedArgs.error)]); + } + const result = Reflect.apply(fn, this, parsedArgs.data); + const parsedReturns = me2._def.returns.safeParse(result, params); + if (!parsedReturns.success) { + throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]); + } + return parsedReturns.data; + }); + } + } + parameters() { + return this._def.args; + } + returnType() { + return this._def.returns; + } + args(...items) { + return new _ZodFunction({ + ...this._def, + args: ZodTuple.create(items).rest(ZodUnknown.create()) + }); + } + returns(returnType) { + return new _ZodFunction({ + ...this._def, + returns: returnType + }); + } + implement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + strictImplement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + static create(args, returns, params) { + return new _ZodFunction({ + args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()), + returns: returns || ZodUnknown.create(), + typeName: ZodFirstPartyTypeKind.ZodFunction, + ...processCreateParams(params) + }); + } +}; +var ZodLazy = class extends ZodType { + get schema() { + return this._def.getter(); + } + _parse(input) { + const { ctx } = this._processInputParams(input); + const lazySchema = this._def.getter(); + return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx }); + } +}; +ZodLazy.create = (getter, params) => { + return new ZodLazy({ + getter, + typeName: ZodFirstPartyTypeKind.ZodLazy, + ...processCreateParams(params) + }); +}; +var ZodLiteral = class extends ZodType { + _parse(input) { + if (input.data !== this._def.value) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_literal, + expected: this._def.value + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } + get value() { + return this._def.value; + } +}; +ZodLiteral.create = (value, params) => { + return new ZodLiteral({ + value, + typeName: ZodFirstPartyTypeKind.ZodLiteral, + ...processCreateParams(params) + }); +}; +function createZodEnum(values, params) { + return new ZodEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodEnum, + ...processCreateParams(params) + }); +} +var ZodEnum = class _ZodEnum extends ZodType { + _parse(input) { + if (typeof input.data !== "string") { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + expected: util2.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!this._cache) { + this._cache = new Set(this._def.values); + } + if (!this._cache.has(input.data)) { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get options() { + return this._def.values; + } + get enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Values() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + extract(values, newDef = this._def) { + return _ZodEnum.create(values, { + ...this._def, + ...newDef + }); + } + exclude(values, newDef = this._def) { + return _ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), { + ...this._def, + ...newDef + }); + } +}; +ZodEnum.create = createZodEnum; +var ZodNativeEnum = class extends ZodType { + _parse(input) { + const nativeEnumValues = util2.getValidEnumValues(this._def.values); + const ctx = this._getOrReturnCtx(input); + if (ctx.parsedType !== ZodParsedType.string && ctx.parsedType !== ZodParsedType.number) { + const expectedValues = util2.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + expected: util2.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!this._cache) { + this._cache = new Set(util2.getValidEnumValues(this._def.values)); + } + if (!this._cache.has(input.data)) { + const expectedValues = util2.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get enum() { + return this._def.values; + } +}; +ZodNativeEnum.create = (values, params) => { + return new ZodNativeEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodNativeEnum, + ...processCreateParams(params) + }); +}; +var ZodPromise = class extends ZodType { + unwrap() { + return this._def.type; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.promise && ctx.common.async === false) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.promise, + received: ctx.parsedType + }); + return INVALID; + } + const promisified = ctx.parsedType === ZodParsedType.promise ? ctx.data : Promise.resolve(ctx.data); + return OK(promisified.then((data) => { + return this._def.type.parseAsync(data, { + path: ctx.path, + errorMap: ctx.common.contextualErrorMap + }); + })); + } +}; +ZodPromise.create = (schema, params) => { + return new ZodPromise({ + type: schema, + typeName: ZodFirstPartyTypeKind.ZodPromise, + ...processCreateParams(params) + }); +}; +var ZodEffects = class extends ZodType { + innerType() { + return this._def.schema; + } + sourceType() { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const effect = this._def.effect || null; + const checkCtx = { + addIssue: (arg) => { + addIssueToContext(ctx, arg); + if (arg.fatal) { + status.abort(); + } else { + status.dirty(); + } + }, + get path() { + return ctx.path; + } + }; + checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx); + if (effect.type === "preprocess") { + const processed = effect.transform(ctx.data, checkCtx); + if (ctx.common.async) { + return Promise.resolve(processed).then(async (processed2) => { + if (status.value === "aborted") + return INVALID; + const result = await this._def.schema._parseAsync({ + data: processed2, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + }); + } else { + if (status.value === "aborted") + return INVALID; + const result = this._def.schema._parseSync({ + data: processed, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + } + } + if (effect.type === "refinement") { + const executeRefinement = (acc) => { + const result = effect.refinement(acc, checkCtx); + if (ctx.common.async) { + return Promise.resolve(result); + } + if (result instanceof Promise) { + throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead."); + } + return acc; + }; + if (ctx.common.async === false) { + const inner = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + executeRefinement(inner.value); + return { status: status.value, value: inner.value }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => { + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + return executeRefinement(inner.value).then(() => { + return { status: status.value, value: inner.value }; + }); + }); + } + } + if (effect.type === "transform") { + if (ctx.common.async === false) { + const base = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (!isValid(base)) + return INVALID; + const result = effect.transform(base.value, checkCtx); + if (result instanceof Promise) { + throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`); + } + return { status: status.value, value: result }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => { + if (!isValid(base)) + return INVALID; + return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ + status: status.value, + value: result + })); + }); + } + } + util2.assertNever(effect); + } +}; +ZodEffects.create = (schema, effect, params) => { + return new ZodEffects({ + schema, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect, + ...processCreateParams(params) + }); +}; +ZodEffects.createWithPreprocess = (preprocess, schema, params) => { + return new ZodEffects({ + schema, + effect: { type: "preprocess", transform: preprocess }, + typeName: ZodFirstPartyTypeKind.ZodEffects, + ...processCreateParams(params) + }); +}; +var ZodOptional = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.undefined) { + return OK(void 0); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodOptional.create = (type, params) => { + return new ZodOptional({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodOptional, + ...processCreateParams(params) + }); +}; +var ZodNullable = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.null) { + return OK(null); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodNullable.create = (type, params) => { + return new ZodNullable({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodNullable, + ...processCreateParams(params) + }); +}; +var ZodDefault = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + let data = ctx.data; + if (ctx.parsedType === ZodParsedType.undefined) { + data = this._def.defaultValue(); + } + return this._def.innerType._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + removeDefault() { + return this._def.innerType; + } +}; +ZodDefault.create = (type, params) => { + return new ZodDefault({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodDefault, + defaultValue: typeof params.default === "function" ? params.default : () => params.default, + ...processCreateParams(params) + }); +}; +var ZodCatch = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const newCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + } + }; + const result = this._def.innerType._parse({ + data: newCtx.data, + path: newCtx.path, + parent: { + ...newCtx + } + }); + if (isAsync(result)) { + return result.then((result2) => { + return { + status: "valid", + value: result2.status === "valid" ? result2.value : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + }); + } else { + return { + status: "valid", + value: result.status === "valid" ? result.value : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + } + } + removeCatch() { + return this._def.innerType; + } +}; +ZodCatch.create = (type, params) => { + return new ZodCatch({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodCatch, + catchValue: typeof params.catch === "function" ? params.catch : () => params.catch, + ...processCreateParams(params) + }); +}; +var ZodNaN = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.nan) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.nan, + received: ctx.parsedType + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } +}; +ZodNaN.create = (params) => { + return new ZodNaN({ + typeName: ZodFirstPartyTypeKind.ZodNaN, + ...processCreateParams(params) + }); +}; +var BRAND = /* @__PURE__ */ Symbol("zod_brand"); +var ZodBranded = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const data = ctx.data; + return this._def.type._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + unwrap() { + return this._def.type; + } +}; +var ZodPipeline = class _ZodPipeline extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.common.async) { + const handleAsync = async () => { + const inResult = await this._def.in._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return DIRTY(inResult.value); + } else { + return this._def.out._parseAsync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + }; + return handleAsync(); + } else { + const inResult = this._def.in._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return { + status: "dirty", + value: inResult.value + }; + } else { + return this._def.out._parseSync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + } + } + static create(a, b) { + return new _ZodPipeline({ + in: a, + out: b, + typeName: ZodFirstPartyTypeKind.ZodPipeline + }); + } +}; +var ZodReadonly = class extends ZodType { + _parse(input) { + const result = this._def.innerType._parse(input); + const freeze = (data) => { + if (isValid(data)) { + data.value = Object.freeze(data.value); + } + return data; + }; + return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result); + } + unwrap() { + return this._def.innerType; + } +}; +ZodReadonly.create = (type, params) => { + return new ZodReadonly({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodReadonly, + ...processCreateParams(params) + }); +}; +function cleanParams(params, data) { + const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params; + const p2 = typeof p === "string" ? { message: p } : p; + return p2; +} +function custom(check, _params = {}, fatal) { + if (check) + return ZodAny.create().superRefine((data, ctx) => { + const r = check(data); + if (r instanceof Promise) { + return r.then((r2) => { + if (!r2) { + const params = cleanParams(_params, data); + const _fatal = params.fatal ?? fatal ?? true; + ctx.addIssue({ code: "custom", ...params, fatal: _fatal }); + } + }); + } + if (!r) { + const params = cleanParams(_params, data); + const _fatal = params.fatal ?? fatal ?? true; + ctx.addIssue({ code: "custom", ...params, fatal: _fatal }); + } + return; + }); + return ZodAny.create(); +} +var late = { + object: ZodObject.lazycreate +}; +var ZodFirstPartyTypeKind; +(function(ZodFirstPartyTypeKind2) { + ZodFirstPartyTypeKind2["ZodString"] = "ZodString"; + ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber"; + ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN"; + ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt"; + ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean"; + ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate"; + ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol"; + ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined"; + ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull"; + ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny"; + ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown"; + ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever"; + ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid"; + ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray"; + ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject"; + ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion"; + ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion"; + ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection"; + ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple"; + ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord"; + ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap"; + ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet"; + ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction"; + ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy"; + ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral"; + ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum"; + ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects"; + ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum"; + ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional"; + ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable"; + ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault"; + ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch"; + ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise"; + ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded"; + ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline"; + ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly"; +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); +var instanceOfType = (cls, params = { + message: `Input not instance of ${cls.name}` +}) => custom((data) => data instanceof cls, params); +var stringType = ZodString.create; +var numberType = ZodNumber.create; +var nanType = ZodNaN.create; +var bigIntType = ZodBigInt.create; +var booleanType = ZodBoolean.create; +var dateType = ZodDate.create; +var symbolType = ZodSymbol.create; +var undefinedType = ZodUndefined.create; +var nullType = ZodNull.create; +var anyType = ZodAny.create; +var unknownType = ZodUnknown.create; +var neverType = ZodNever.create; +var voidType = ZodVoid.create; +var arrayType = ZodArray.create; +var objectType = ZodObject.create; +var strictObjectType = ZodObject.strictCreate; +var unionType = ZodUnion.create; +var discriminatedUnionType = ZodDiscriminatedUnion.create; +var intersectionType = ZodIntersection.create; +var tupleType = ZodTuple.create; +var recordType = ZodRecord.create; +var mapType = ZodMap.create; +var setType = ZodSet.create; +var functionType = ZodFunction.create; +var lazyType = ZodLazy.create; +var literalType = ZodLiteral.create; +var enumType = ZodEnum.create; +var nativeEnumType = ZodNativeEnum.create; +var promiseType = ZodPromise.create; +var effectsType = ZodEffects.create; +var optionalType = ZodOptional.create; +var nullableType = ZodNullable.create; +var preprocessType = ZodEffects.createWithPreprocess; +var pipelineType = ZodPipeline.create; +var ostring = () => stringType().optional(); +var onumber = () => numberType().optional(); +var oboolean = () => booleanType().optional(); +var coerce = { + string: ((arg) => ZodString.create({ ...arg, coerce: true })), + number: ((arg) => ZodNumber.create({ ...arg, coerce: true })), + boolean: ((arg) => ZodBoolean.create({ + ...arg, + coerce: true + })), + bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })), + date: ((arg) => ZodDate.create({ ...arg, coerce: true })) +}; +var NEVER = INVALID; + +// ../plugins/dist/utils.js +var isHookTree = (value) => { + if (value == null || typeof value !== "object" || Array.isArray(value)) { + return false; + } + for (const child of Object.values(value)) { + if (child === void 0) { + continue; + } + if (typeof child === "function") { + continue; + } + if (child == null || typeof child !== "object" || Array.isArray(child) || !isHookTree(child)) { + return false; + } + } + return true; +}; + +// ../plugins/dist/plugin.js +var isHarnessPlugin = (value) => { + if (value == null || typeof value !== "object" || Array.isArray(value)) { + return false; + } + const candidate = value; + if (typeof candidate.name !== "string" || candidate.name.length === 0) { + return false; + } + if (candidate.createState != null && typeof candidate.createState !== "function") { + return false; + } + if (candidate.hooks != null && !isHookTree(candidate.hooks)) { + return false; + } + return true; +}; + +// ../plugins/dist/manager.js +var pluginsLogger = logger.child("plugins"); + +// ../config/dist/types.js +var DEFAULT_METRO_PORT = 8081; +var configLogger = logger.child("config"); +var RunnerSchema = external_exports.object({ + name: external_exports.string().min(1, "Runner name is required").regex(/^[a-zA-Z0-9._-]+$/, "Runner name can only contain alphanumeric characters, dots, underscores, and hyphens"), + config: external_exports.record(external_exports.any()), + runner: external_exports.string(), + cli: external_exports.string().optional(), + platformId: external_exports.string() +}); +var PluginSchema = external_exports.custom((value) => isHarnessPlugin(value), "Invalid Harness plugin"); +var ConfigSchema = external_exports.object({ + entryPoint: external_exports.string().min(1, "Entry point is required"), + appRegistryComponentName: external_exports.string().min(1, "App registry component name is required"), + runners: external_exports.array(RunnerSchema).min(1, "At least one runner is required"), + plugins: external_exports.array(PluginSchema).optional().default([]), + defaultRunner: external_exports.string().optional(), + host: external_exports.string().min(1, "Host is required").optional(), + metroPort: external_exports.number().int("Metro port must be an integer").min(1, "Metro port must be at least 1").max(65535, "Metro port must be at most 65535").optional().default(DEFAULT_METRO_PORT), + webSocketPort: external_exports.number().optional().describe("Deprecated. Bridge traffic now uses metroPort and this value is ignored."), + bridgeTimeout: external_exports.number().min(1e3, "Bridge timeout must be at least 1 second").default(6e4), + testTimeout: external_exports.number().min(1e3, "Test timeout must be at least 1 second").default(5e3), + platformReadyTimeout: external_exports.number().min(1e3, "Platform ready timeout must be at least 1 second").default(3e5), + bundleStartTimeout: external_exports.number().min(1e3, "Bundle start timeout must be at least 1 second").default(6e4), + maxAppRestarts: external_exports.number().min(0, "Max app restarts must be at least 0").default(2), + eagerPrewarm: external_exports.boolean().optional().default(true).describe("Start building the Metro bundle while the platform (emulator, simulator, or browser) is still booting, so the first bundle is ready sooner. Disable to defer the first bundle build until app startup."), + resetEnvironmentBetweenTestFiles: external_exports.union([external_exports.boolean(), external_exports.enum(["process", "runtime"])]).optional().default(true).describe("Controls how the environment is reset between test files. `true` (default) and `'process'` kill and cold-restart the app process. `'runtime'` reloads the JS runtime in place (DevSettings.reload() / window.location.reload()), which is cheaper but escalates to a process restart if the reload fails or the app does not reconnect in time. `false` disables resetting the environment between test files entirely."), + skipAlreadyIncludedModules: external_exports.boolean().optional().describe('Skip re-sending modules already served in the main app bundle when Metro serves a per-test-file bundle. Defaults to true; set to false as an escape hatch if it causes issues. Left undefined (rather than defaulted here) so `resolveSkipAlreadyIncludedModules` can tell "not set" apart from "explicitly set" when reconciling with the deprecated `unstable__skipAlreadyIncludedModules` alias.'), + // Deprecated alias for `skipAlreadyIncludedModules` -- see + // `resolveSkipAlreadyIncludedModules`. Left without a `.default()` for the + // same reason: a default value would be indistinguishable from the user + // never having set it, which would break alias-vs-explicit-flag precedence. + unstable__skipAlreadyIncludedModules: external_exports.boolean().optional().describe("Deprecated. Use `skipAlreadyIncludedModules` instead."), + cache: external_exports.object({ + metro: external_exports.boolean().optional().describe('Enable persistent Metro transform and file-map caching under the .harness cache directory. Defaults to true; set to false to always start with a cold Metro cache. Left undefined (rather than defaulted here) so `resolveMetroCacheEnabled` can tell "not set" apart from "explicitly set" when reconciling with the deprecated `unstable__enableMetroCache` alias.'), + version: external_exports.string().optional().describe("User-controlled salt folded into the Metro cacheVersion. Bump it to force-invalidate previously cached transforms.") + }).optional(), + // Deprecated alias for `cache.metro` -- see `resolveMetroCacheEnabled`. + // Left without a `.default()` for the same reason as + // `unstable__skipAlreadyIncludedModules`: a default value would be + // indistinguishable from the user never having set it, which would break + // alias-vs-explicit-flag precedence. + unstable__enableMetroCache: external_exports.boolean().optional().describe("Deprecated. Use `cache.metro` instead."), + permissions: external_exports.boolean().optional().default(false).describe("Enable platform-specific permission prompt automation. When false, Harness does not start permission-handling helpers such as the iOS XCTest agent."), + detectNativeCrashes: external_exports.boolean().optional().default(true), + crashDetectionInterval: external_exports.number().min(100, "Crash detection interval must be at least 100ms").default(500), + disableViewFlattening: external_exports.boolean().optional().default(false).describe("Disable view flattening in React Native. This will set collapsable={true} for all View components to ensure they are not flattened by the native layout engine."), + coverage: external_exports.object({ + root: external_exports.string().optional().describe(`Root directory for coverage instrumentation in monorepo setups. Specifies the directory from which coverage data should be collected. Use ".." for create-react-native-library projects where tests run from example/ but source files are in parent directory. Passed to babel-plugin-istanbul's cwd option.`), + native: external_exports.object({ + ios: external_exports.object({ + pods: external_exports.array(external_exports.string()).min(1, "At least one pod name is required").describe("Pod names to instrument for native code coverage. Coverage flags are injected at pod install time via a CocoaPods hook. After tests, profraw data is collected and converted to lcov format.") + }).optional() + }).optional().describe("Native code coverage configuration.") + }).optional(), + forwardClientLogs: external_exports.boolean().optional().default(false).describe("Enable forwarding of console.log, console.warn, console.error, and other console method calls from the React Native app during the active test run. When enabled, app console output is attached to the active test result's console output."), + diagnostics: external_exports.boolean().optional().default(false).describe("Enable diagnostics tracing for the harness session. Records spans for session setup, Metro bundling, bridge/client events, and per-file test runs, then writes a Chrome Trace Event JSON file and prints a summary after each run. Can also be enabled via the RN_HARNESS_DIAGNOSTICS environment variable."), + // Deprecated property - used for migration detection + include: external_exports.array(external_exports.string()).optional() +}).refine((config) => { + if (config.defaultRunner) { + return config.runners.some((runner) => runner.name === config.defaultRunner); + } + return true; +}, { + message: "Default runner must match one of the configured runner names", + path: ["defaultRunner"] +}); + +// ../config/dist/errors.js +var ConfigValidationError = class extends HarnessError { + filePath; + validationErrors; + constructor(filePath, validationErrors) { + const lines = [ + `Configuration validation failed in ${filePath}`, + "", + "The following issues were found:", + "" + ]; + validationErrors.forEach((error, index) => { + lines.push(` ${index + 1}. ${error}`); + }); + lines.push("", "Please fix these issues and try again.", "For more information, visit: https://react-native-harness.dev/docs/configuration"); + super(lines.join("\n")); + this.filePath = filePath; + this.validationErrors = validationErrors; + this.name = "ConfigValidationError"; + } +}; +var ConfigNotFoundError = class extends HarnessError { + searchPath; + constructor(searchPath) { + const lines = [ + "Configuration file not found", + "", + `Searched for configuration files in: ${searchPath}`, + "and all parent directories.", + "", + "React Native Harness looks for one of these files:", + " \u2022 rn-harness.config.js", + " \u2022 rn-harness.config.mjs", + " \u2022 rn-harness.config.cjs", + " \u2022 rn-harness.config.json", + "", + "For more information, visit: https://www.react-native-harness.dev/docs/getting-started/configuration" + ]; + super(lines.join("\n")); + this.searchPath = searchPath; + this.name = "ConfigNotFoundError"; + } +}; +var ConfigLoadError = class extends HarnessError { + filePath; + cause; + constructor(filePath, cause) { + const lines = [ + "Failed to load configuration file", + "", + `File: ${filePath}`, + "" + ]; + if (cause) { + lines.push("Error details:"); + lines.push(` ${cause.message}`); + lines.push(""); + } + lines.push("This could be due to:", " \u2022 Syntax errors in your configuration file", " \u2022 Missing dependencies or modules", " \u2022 Invalid file format or encoding", " \u2022 File permissions issues", "", "Troubleshooting steps:", " 1. Check the file syntax and format", " 2. Ensure all required dependencies are installed", " 3. Verify file permissions", " 4. Try creating a new configuration file", "", "For more help, visit: https://www.react-native-harness.dev/docs/getting-started/configuration"); + super(lines.join("\n")); + this.filePath = filePath; + this.name = "ConfigLoadError"; + this.cause = cause; + } +}; + +// ../config/dist/reader.js +var import_node_path10 = __toESM(require("path"), 1); +var import_node_fs10 = __toESM(require("fs"), 1); +var import_node_module2 = require("module"); +var import_meta = {}; +var extensions = [".js", ".mjs", ".cjs", ".json"]; +var importUp = async (dir, name) => { + const filePath = import_node_path10.default.join(dir, name); + for (const ext of extensions) { + const filePathWithExt = `${filePath}${ext}`; + if (import_node_fs10.default.existsSync(filePathWithExt)) { + let rawConfig; + try { + if (ext === ".mjs") { + rawConfig = await import(filePathWithExt).then((module2) => module2.default); + } else { + const require2 = (0, import_node_module2.createRequire)(import_meta.url); + rawConfig = require2(filePathWithExt); + } + } catch (error) { + throw new ConfigLoadError(filePathWithExt, error instanceof Error ? error : void 0); + } + try { + const config = ConfigSchema.parse(rawConfig); + return { config, filePathWithExt, configDir: dir }; + } catch (error) { + if (error instanceof ZodError) { + const validationErrors = error.errors.map((err) => { + const path12 = err.path.length > 0 ? ` at "${err.path.join(".")}"` : ""; + return `${err.message}${path12}`; + }); + throw new ConfigValidationError(filePathWithExt, validationErrors); + } + throw error; + } + } + } + const parentDir = import_node_path10.default.dirname(dir); + if (parentDir === dir) { + throw new ConfigNotFoundError(dir); + } + return importUp(parentDir, name); +}; +var getConfig = async (dir) => { + const { config, configDir } = await importUp(dir, "rn-harness.config"); + return { + config, + projectRoot: configDir + }; +}; + +// src/shared/format-bytes.ts +var formatMegabytes = (bytes) => `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + +// src/shared/metro-cache-inputs.ts +var import_node_fs11 = __toESM(require("fs")); +var import_node_path11 = __toESM(require("path")); +var FALLBACK_BUNDLER_METRO_VERSION = "unknown"; +var resolveRepoRoot = (projectRoot) => { + let dir = projectRoot; + while (true) { + if (import_node_fs11.default.existsSync(import_node_path11.default.join(dir, ".git"))) { + return dir; + } + const parent = import_node_path11.default.dirname(dir); + if (parent === dir) { + return projectRoot; + } + dir = parent; + } +}; +var resolveBundlerMetroPackageJson = (projectRoot) => { + try { + return require.resolve( + "@react-native-harness/bundler-metro/package.json", + { paths: [projectRoot] } + ); + } catch { + const jestPackageJsonPath = require.resolve( + "@react-native-harness/jest/package.json", + { paths: [projectRoot] } + ); + return require.resolve("@react-native-harness/bundler-metro/package.json", { + paths: [import_node_path11.default.dirname(jestPackageJsonPath)] + }); + } +}; +var resolveBundlerMetroVersion = (projectRoot) => { + try { + const packageJsonPath = resolveBundlerMetroPackageJson(projectRoot); + const packageJson = JSON.parse(import_node_fs11.default.readFileSync(packageJsonPath, "utf8")); + if (typeof packageJson.version !== "string") { + throw new Error('"version" field is missing or not a string'); + } + return packageJson.version; + } catch (error) { + console.warn( + `Failed to resolve @react-native-harness/bundler-metro's version from "${projectRoot}". Falling back to "${FALLBACK_BUNDLER_METRO_VERSION}".`, + error + ); + return FALLBACK_BUNDLER_METRO_VERSION; + } +}; +var resolveMetroStaticInputs = (options) => computeMetroStaticInputs({ + projectRoot: options.projectRoot, + repoRoot: resolveRepoRoot(options.projectRoot), + bundlerMetroVersion: resolveBundlerMetroVersion(options.projectRoot), + salt: options.cacheVersionSalt +}); + +// src/shared/plan-save.ts +var parseSnapshot = (raw) => { + if (!raw) { + return {}; + } + try { + return JSON.parse(raw); + } catch (error) { + console.warn( + "Failed to parse the metroSnapshot input. Treating the pre-run cache state as empty.", + error + ); + return {}; + } +}; +var parseSavePolicyMode = (raw) => { + if (raw === "always" || raw === "never" || raw === "default-branch") { + return raw; + } + return "default-branch"; +}; +var logMetroSaveDecision = (plan, policy) => { + if (!plan) { + return; + } + const { delta, shouldSave, saveKey } = plan; + if (delta.addedEntries + delta.removedEntries === 0) { + console.info("Metro cache: unchanged \u2014 skipping save."); + return; + } + const change = `+${delta.addedEntries} files / ${formatMegabytes(delta.addedBytes)} added, ${delta.removedEntries} removed`; + if (shouldSave) { + console.info( + `Metro cache: changed (${change}) \u2014 saving new entry with key "${saveKey}".` + ); + return; + } + const reason = policy.mode === "default-branch" ? 'policy "default-branch" (current branch is not the default branch)' : `policy "${policy.mode}"`; + console.info( + `Metro cache: changed (${change}) but save skipped by ${reason}.` + ); +}; +var run = async () => { + try { + const projectRootInput = process.env.INPUT_PROJECTROOT; + const projectRoot = projectRootInput ? import_node_path12.default.resolve(projectRootInput) : process.cwd(); + console.info(`Planning Metro cache save for: ${projectRoot}`); + const { config, projectRoot: resolvedProjectRoot } = await getConfig( + projectRoot + ); + const githubOutput = process.env.GITHUB_OUTPUT; + if (!githubOutput) { + throw new Error("GITHUB_OUTPUT environment variable is not set"); + } + const staticInputs = resolveMetroStaticInputs({ + projectRoot: resolvedProjectRoot, + cacheVersionSalt: config.cache?.version + }); + const before = parseSnapshot(process.env.INPUT_METRO_SNAPSHOT); + const policy = { + mode: parseSavePolicyMode(process.env.INPUT_CACHESAVEPOLICY), + isDefaultBranch: process.env.IS_DEFAULT_BRANCH === "true" + }; + const cache = createHarnessCache({ projectRoot: resolvedProjectRoot }); + const os = process.env.RUNNER_OS ?? process.platform; + const savePlan = cache.planSave(before, policy, { + metro: { os, staticInputs } + }); + cache.writeKeysFile(savePlan); + const metroPlan = savePlan.domains.metro; + logMetroSaveDecision(metroPlan, policy); + const output = `metroShouldSave=${metroPlan?.shouldSave ? "true" : "false"} +metroSaveKey=${metroPlan?.saveKey ?? ""} +`; + import_node_fs12.default.appendFileSync(githubOutput, output); + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } else { + console.error("Failed to plan Metro cache save"); + } + process.exit(1); + } +}; +run(); diff --git a/actions/shared/snapshot-metro.cjs b/actions/shared/snapshot-metro.cjs new file mode 100644 index 00000000..74e94c5e --- /dev/null +++ b/actions/shared/snapshot-metro.cjs @@ -0,0 +1,4913 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// ../../node_modules/picocolors/picocolors.js +var require_picocolors = __commonJS({ + "../../node_modules/picocolors/picocolors.js"(exports2, module2) { + "use strict"; + var p = process || {}; + var argv = p.argv || []; + var env = p.env || {}; + var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI); + var formatter = (open, close, replace = open) => (input) => { + let string = "" + input, index = string.indexOf(close, open.length); + return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close; + }; + var replaceClose = (string, close, replace, index) => { + let result = "", cursor = 0; + do { + result += string.substring(cursor, index) + replace; + cursor = index + close.length; + index = string.indexOf(close, cursor); + } while (~index); + return result + string.substring(cursor); + }; + var createColors = (enabled = isColorSupported) => { + let f = enabled ? formatter : () => String; + return { + isColorSupported: enabled, + reset: f("\x1B[0m", "\x1B[0m"), + bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"), + dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"), + italic: f("\x1B[3m", "\x1B[23m"), + underline: f("\x1B[4m", "\x1B[24m"), + inverse: f("\x1B[7m", "\x1B[27m"), + hidden: f("\x1B[8m", "\x1B[28m"), + strikethrough: f("\x1B[9m", "\x1B[29m"), + black: f("\x1B[30m", "\x1B[39m"), + red: f("\x1B[31m", "\x1B[39m"), + green: f("\x1B[32m", "\x1B[39m"), + yellow: f("\x1B[33m", "\x1B[39m"), + blue: f("\x1B[34m", "\x1B[39m"), + magenta: f("\x1B[35m", "\x1B[39m"), + cyan: f("\x1B[36m", "\x1B[39m"), + white: f("\x1B[37m", "\x1B[39m"), + gray: f("\x1B[90m", "\x1B[39m"), + bgBlack: f("\x1B[40m", "\x1B[49m"), + bgRed: f("\x1B[41m", "\x1B[49m"), + bgGreen: f("\x1B[42m", "\x1B[49m"), + bgYellow: f("\x1B[43m", "\x1B[49m"), + bgBlue: f("\x1B[44m", "\x1B[49m"), + bgMagenta: f("\x1B[45m", "\x1B[49m"), + bgCyan: f("\x1B[46m", "\x1B[49m"), + bgWhite: f("\x1B[47m", "\x1B[49m"), + blackBright: f("\x1B[90m", "\x1B[39m"), + redBright: f("\x1B[91m", "\x1B[39m"), + greenBright: f("\x1B[92m", "\x1B[39m"), + yellowBright: f("\x1B[93m", "\x1B[39m"), + blueBright: f("\x1B[94m", "\x1B[39m"), + magentaBright: f("\x1B[95m", "\x1B[39m"), + cyanBright: f("\x1B[96m", "\x1B[39m"), + whiteBright: f("\x1B[97m", "\x1B[39m"), + bgBlackBright: f("\x1B[100m", "\x1B[49m"), + bgRedBright: f("\x1B[101m", "\x1B[49m"), + bgGreenBright: f("\x1B[102m", "\x1B[49m"), + bgYellowBright: f("\x1B[103m", "\x1B[49m"), + bgBlueBright: f("\x1B[104m", "\x1B[49m"), + bgMagentaBright: f("\x1B[105m", "\x1B[49m"), + bgCyanBright: f("\x1B[106m", "\x1B[49m"), + bgWhiteBright: f("\x1B[107m", "\x1B[49m") + }; + }; + module2.exports = createColors(); + module2.exports.createColors = createColors; + } +}); + +// ../../node_modules/sisteransi/src/index.js +var require_src = __commonJS({ + "../../node_modules/sisteransi/src/index.js"(exports2, module2) { + "use strict"; + var ESC = "\x1B"; + var CSI = `${ESC}[`; + var beep = "\x07"; + var cursor = { + to(x2, y) { + if (!y) return `${CSI}${x2 + 1}G`; + return `${CSI}${y + 1};${x2 + 1}H`; + }, + move(x2, y) { + let ret = ""; + if (x2 < 0) ret += `${CSI}${-x2}D`; + else if (x2 > 0) ret += `${CSI}${x2}C`; + if (y < 0) ret += `${CSI}${-y}A`; + else if (y > 0) ret += `${CSI}${y}B`; + return ret; + }, + up: (count = 1) => `${CSI}${count}A`, + down: (count = 1) => `${CSI}${count}B`, + forward: (count = 1) => `${CSI}${count}C`, + backward: (count = 1) => `${CSI}${count}D`, + nextLine: (count = 1) => `${CSI}E`.repeat(count), + prevLine: (count = 1) => `${CSI}F`.repeat(count), + left: `${CSI}G`, + hide: `${CSI}?25l`, + show: `${CSI}?25h`, + save: `${ESC}7`, + restore: `${ESC}8` + }; + var scroll = { + up: (count = 1) => `${CSI}S`.repeat(count), + down: (count = 1) => `${CSI}T`.repeat(count) + }; + var erase = { + screen: `${CSI}2J`, + up: (count = 1) => `${CSI}1J`.repeat(count), + down: (count = 1) => `${CSI}J`.repeat(count), + line: `${CSI}2K`, + lineEnd: `${CSI}K`, + lineStart: `${CSI}1K`, + lines(count) { + let clear = ""; + for (let i = 0; i < count; i++) + clear += this.line + (i < count - 1 ? cursor.up() : ""); + if (count) + clear += cursor.left; + return clear; + } + }; + module2.exports = { cursor, scroll, erase, beep }; + } +}); + +// src/shared/snapshot-metro.ts +var import_node_fs11 = __toESM(require("fs")); +var import_node_path11 = __toESM(require("path")); + +// ../cache/dist/index.js +var import_node_fs9 = __toESM(require("fs"), 1); + +// ../tools/dist/net.js +var import_node_net = __toESM(require("net"), 1); + +// ../tools/dist/logger.js +var import_node_util = __toESM(require("util"), 1); +var import_picocolors = __toESM(require_picocolors(), 1); +var verbose = !!process.env.HARNESS_DEBUG; +var BASE_TAG = "[harness]"; +var INFO_TAG = import_picocolors.default.isColorSupported ? import_picocolors.default.reset(import_picocolors.default.inverse(import_picocolors.default.bold(import_picocolors.default.magenta(" HARNESS ")))) : "HARNESS"; +var ERROR_TAG = import_picocolors.default.isColorSupported ? import_picocolors.default.reset(import_picocolors.default.inverse(import_picocolors.default.bold(import_picocolors.default.red(" HARNESS ")))) : "HARNESS"; +var getTimestamp = () => (/* @__PURE__ */ new Date()).toISOString(); +var normalizeScope = (scope) => scope.trim().replace(/^\[+|\]+$/g, "").replace(/\]\[/g, "]["); +var formatPrefix = (scopes) => { + const suffix = scopes.map((scope) => `[${normalizeScope(scope)}]`).join(""); + return `${BASE_TAG}${suffix}`; +}; +var mapLines = (text, prefix) => text.split("\n").map((line) => `${prefix} ${line}`).join("\n"); +var writeLog = (level, scopes, messages) => { + if (!verbose && (level === "info" || level === "log" || level === "success")) { + const output2 = import_node_util.default.format(...messages); + const tag = INFO_TAG; + process.stderr.write(`${tag} ${output2} +`); + return; + } + if (!verbose && level === "error") { + const output2 = import_node_util.default.format(...messages); + process.stderr.write(`${ERROR_TAG} ${output2} +`); + return; + } + const method = level === "warn" ? console.warn : console.debug; + const output = import_node_util.default.format(...messages); + const prefix = `${getTimestamp()} ${formatPrefix(scopes)}`; + method(mapLines(output, prefix)); +}; +var setVerbose = (level) => { + verbose = level; +}; +var isVerbose = () => { + return verbose; +}; +var createScopedLogger = (scopes = []) => ({ + debug: (...messages) => { + if (!verbose) { + return; + } + writeLog("debug", scopes, messages); + }, + info: (...messages) => { + writeLog("info", scopes, messages); + }, + warn: (...messages) => { + writeLog("warn", scopes, messages); + }, + error: (...messages) => { + writeLog("error", scopes, messages); + }, + log: (...messages) => { + writeLog("log", scopes, messages); + }, + success: (...messages) => { + writeLog("success", scopes, messages); + }, + child: (scope) => createScopedLogger([...scopes, scope]), + setVerbose, + isVerbose +}); +var logger = createScopedLogger(); + +// ../../node_modules/@clack/core/dist/index.mjs +var import_node_process = require("process"); +var k = __toESM(require("readline"), 1); +var import_node_readline = __toESM(require("readline"), 1); +var import_sisteransi = __toESM(require_src(), 1); +var import_node_tty = require("tty"); +var Ft = { limit: 1 / 0, ellipsis: "" }; +var ft = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 }; +var j = "\x07"; +var Q = "["; +var dt = "]"; +var U = `${dt}8;;`; +var et = new RegExp(`(?:\\${Q}(?\\d+)m|\\${U}(?.*)${j})`, "y"); +var At = ["up", "down", "left", "right", "space", "enter", "cancel"]; +var _ = { actions: new Set(At), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]), messages: { cancel: "Canceled", error: "Something went wrong" }, withGuide: true }; +var bt = globalThis.process.platform.startsWith("win"); + +// ../../node_modules/@clack/prompts/dist/index.mjs +var import_picocolors2 = __toESM(require_picocolors(), 1); +var import_node_process2 = __toESM(require("process"), 1); +var import_node_fs = require("fs"); +var import_node_path = require("path"); +var import_sisteransi2 = __toESM(require_src(), 1); +var import_node_util2 = require("util"); +function ht() { + return import_node_process2.default.platform !== "win32" ? import_node_process2.default.env.TERM !== "linux" : !!import_node_process2.default.env.CI || !!import_node_process2.default.env.WT_SESSION || !!import_node_process2.default.env.TERMINUS_SUBLIME || import_node_process2.default.env.ConEmuTask === "{cmd::Cmder}" || import_node_process2.default.env.TERM_PROGRAM === "Terminus-Sublime" || import_node_process2.default.env.TERM_PROGRAM === "vscode" || import_node_process2.default.env.TERM === "xterm-256color" || import_node_process2.default.env.TERM === "alacritty" || import_node_process2.default.env.TERMINAL_EMULATOR === "JetBrains-JediTerm"; +} +var ee = ht(); +var w = (e, r) => ee ? e : r; +var Me = w("\u25C6", "*"); +var ce = w("\u25A0", "x"); +var de = w("\u25B2", "x"); +var V = w("\u25C7", "o"); +var $e = w("\u250C", "T"); +var h = w("\u2502", "|"); +var x = w("\u2514", "\u2014"); +var Re = w("\u2510", "T"); +var Oe = w("\u2518", "\u2014"); +var Y = w("\u25CF", ">"); +var K = w("\u25CB", " "); +var te = w("\u25FB", "[\u2022]"); +var k2 = w("\u25FC", "[+]"); +var z = w("\u25FB", "[ ]"); +var Pe = w("\u25AA", "\u2022"); +var se = w("\u2500", "-"); +var he = w("\u256E", "+"); +var Ne = w("\u251C", "+"); +var me = w("\u256F", "+"); +var pe = w("\u2570", "+"); +var We = w("\u256D", "+"); +var ge = w("\u25CF", "\u2022"); +var fe = w("\u25C6", "*"); +var Fe = w("\u25B2", "!"); +var ye = w("\u25A0", "x"); +var Ft2 = { limit: 1 / 0, ellipsis: "" }; +var yt2 = { limit: 1 / 0, ellipsis: "", ellipsisWidth: 0 }; +var Ce = "\x07"; +var Ve = "["; +var vt = "]"; +var we = `${vt}8;;`; +var Ge = new RegExp(`(?:\\${Ve}(?\\d+)m|\\${we}(?.*)${Ce})`, "y"); +var Ut = import_picocolors2.default.magenta; +var Ye = { light: w("\u2500", "-"), heavy: w("\u2501", "="), block: w("\u2588", "#") }; +var ze = `${import_picocolors2.default.gray(h)} `; + +// ../tools/dist/spawn.js +var spawnLogger = logger.child("spawn"); + +// ../tools/dist/react-native.js +var import_node_module = require("module"); +var import_node_path2 = __toESM(require("path"), 1); +var import_node_fs2 = __toESM(require("fs"), 1); + +// ../tools/dist/error.js +var HarnessError = class extends Error { +}; + +// ../tools/dist/packages.js +var import_node_path3 = __toESM(require("path"), 1); +var import_node_fs3 = __toESM(require("fs"), 1); + +// ../tools/dist/path.js +var import_node_path4 = __toESM(require("path"), 1); + +// ../tools/dist/crash-artifacts.js +var import_node_fs4 = __toESM(require("fs"), 1); +var import_node_path5 = __toESM(require("path"), 1); +var DEFAULT_ARTIFACT_ROOT = import_node_path5.default.join(process.cwd(), ".harness", "crash-reports"); + +// ../tools/dist/harness-artifacts.js +var import_node_fs5 = __toESM(require("fs"), 1); +var import_node_path6 = __toESM(require("path"), 1); + +// ../tools/dist/diagnostics.js +var noop = () => void 0; +var noopSpan = { + end: noop, + fail: noop +}; +var createNoopDiagnostics = () => { + const diagnostics = { + enabled: false, + start: () => noopSpan, + measure: async (_label, fn) => fn(), + mark: noop, + record: noop, + child: () => diagnostics, + flush: () => [] + }; + return diagnostics; +}; +var noopDiagnostics = createNoopDiagnostics(); + +// ../cache/dist/paths.js +var import_node_path7 = __toESM(require("path"), 1); +var HARNESS_DIRNAME = ".harness"; +var CACHE_DIRNAME = "cache"; +var METRO_TRANSFORM_DIRNAME = "metro"; +var METRO_FILE_MAP_DIRNAME = "metro-file-map"; +var createHarnessCachePaths = (projectRoot) => { + const root = import_node_path7.default.join(projectRoot, HARNESS_DIRNAME, CACHE_DIRNAME); + return { + root, + metroTransform: import_node_path7.default.join(root, METRO_TRANSFORM_DIRNAME), + metroFileMap: import_node_path7.default.join(root, METRO_FILE_MAP_DIRNAME) + }; +}; +var getDomainDirectories = (domain, paths) => { + switch (domain) { + case "metro": + return [paths.metroTransform, paths.metroFileMap]; + default: + return []; + } +}; + +// ../cache/dist/plan.js +var import_node_crypto = require("crypto"); +var import_node_fs6 = __toESM(require("fs"), 1); +var import_node_path8 = __toESM(require("path"), 1); +var cacheLogger = logger.child("cache"); +var KEYS_FILENAME = "keys.json"; +var HASH_LENGTH = 16; +var ALL_DOMAINS = ["metro"]; +var isAccretiveDomain = (domain) => { + switch (domain) { + case "metro": + return true; + default: + return false; + } +}; +var hashSortedEntries = (entries) => (0, import_node_crypto.createHash)("sha256").update(entries.slice().sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, value]) => `${key}=${value}`).join("\n")).digest("hex").slice(0, HASH_LENGTH); +var buildStaticHashKey = (domain, inputs) => { + const hash = hashSortedEntries(Object.entries(inputs.staticInputs)); + return `harness-${domain}-${inputs.os}-${hash}`; +}; +var buildContentHash = (entries) => hashSortedEntries(entries.map((entry) => [entry.path, String(entry.sizeBytes)])); +var walkDirectory = (root, directory, entries) => { + let dirents; + try { + dirents = import_node_fs6.default.readdirSync(directory, { withFileTypes: true }); + } catch { + return; + } + for (const dirent of dirents) { + const fullPath = import_node_path8.default.join(directory, dirent.name); + if (dirent.isDirectory()) { + walkDirectory(root, fullPath, entries); + } else if (dirent.isFile()) { + const stat = import_node_fs6.default.statSync(fullPath); + entries.push({ + path: import_node_path8.default.relative(root, fullPath), + sizeBytes: stat.size, + mtimeMs: stat.mtimeMs + }); + } + } +}; +var planRestore = (paths, inputs) => { + const domains = {}; + for (const domain of ALL_DOMAINS) { + const domainInputs = inputs[domain]; + if (!domainInputs) { + continue; + } + const restoreKey = buildStaticHashKey(domain, domainInputs); + const accretive = isAccretiveDomain(domain); + domains[domain] = { + paths: [...getDomainDirectories(domain, paths)], + restoreKey, + restorePrefixes: accretive ? [`${restoreKey}-`] : [], + accretive + }; + } + return { version: 1, domains }; +}; +var snapshot = (paths) => { + const result = {}; + for (const domain of ALL_DOMAINS) { + try { + const entries = []; + for (const directory of getDomainDirectories(domain, paths)) { + walkDirectory(paths.root, directory, entries); + } + result[domain] = entries; + } catch (error) { + cacheLogger.warn(`Failed to snapshot cache domain "${domain}". Treating as empty.`, error); + result[domain] = []; + } + } + return result; +}; +var diffSnapshots = (before, after) => { + const beforeByPath = new Map(before.map((entry) => [entry.path, entry])); + const afterByPath = new Map(after.map((entry) => [entry.path, entry])); + let addedEntries = 0; + let removedEntries = 0; + let addedBytes = 0; + for (const [entryPath, afterEntry] of afterByPath) { + const beforeEntry = beforeByPath.get(entryPath); + if (!beforeEntry || beforeEntry.sizeBytes !== afterEntry.sizeBytes) { + addedEntries += 1; + addedBytes += afterEntry.sizeBytes; + } + } + for (const entryPath of beforeByPath.keys()) { + if (!afterByPath.has(entryPath)) { + removedEntries += 1; + } + } + return { addedEntries, removedEntries, addedBytes }; +}; +var shouldSaveForPolicy = (policy, delta) => { + if (delta.addedEntries + delta.removedEntries === 0) { + return false; + } + switch (policy.mode) { + case "always": + return true; + case "default-branch": + return policy.isDefaultBranch; + case "never": + return false; + } +}; +var planSave = (paths, before, policy, inputs) => { + const after = snapshot(paths); + const domains = {}; + for (const domain of ALL_DOMAINS) { + const domainInputs = inputs[domain]; + if (!domainInputs) { + continue; + } + const beforeEntries = before[domain] ?? []; + const afterEntries = after[domain] ?? []; + const delta = diffSnapshots(beforeEntries, afterEntries); + const staticHashKey = buildStaticHashKey(domain, domainInputs); + const accretive = isAccretiveDomain(domain); + domains[domain] = { + shouldSave: shouldSaveForPolicy(policy, delta), + saveKey: accretive ? `${staticHashKey}-${buildContentHash(afterEntries)}` : staticHashKey, + delta + }; + } + return { version: 1, domains }; +}; +var writeKeysFile = (paths, plan) => { + const filePath = import_node_path8.default.join(paths.root, KEYS_FILENAME); + try { + import_node_fs6.default.mkdirSync(paths.root, { recursive: true }); + import_node_fs6.default.writeFileSync(filePath, JSON.stringify(plan, null, 2)); + } catch (error) { + cacheLogger.warn(`Failed to write cache keys file "${filePath}".`, error); + } +}; + +// ../cache/dist/warmth.js +var import_node_fs7 = __toESM(require("fs"), 1); +var cacheLogger2 = logger.child("cache"); +var isDomainWarm = (domain, paths) => { + const directory = getWarmthDirectory(domain, paths); + if (!directory) { + return false; + } + try { + const stat = import_node_fs7.default.statSync(directory); + if (!stat.isDirectory()) { + return false; + } + return import_node_fs7.default.readdirSync(directory).length > 0; + } catch (error) { + if (isMissingPathError(error)) { + return false; + } + cacheLogger2.warn(`Failed to check cache warmth for "${domain}" at "${directory}". Treating as cold.`, error); + return false; + } +}; +var getWarmthDirectory = (domain, paths) => { + switch (domain) { + case "metro": + return paths.metroTransform; + default: + return void 0; + } +}; +var isMissingPathError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT"; + +// ../cache/dist/domains/metro.js +var import_node_crypto2 = require("crypto"); +var import_node_fs8 = __toESM(require("fs"), 1); +var import_node_path9 = __toESM(require("path"), 1); +var cacheLogger3 = logger.child("cache"); +var LOCKFILE_NAMES = [ + "bun.lock", + "bun.lockb", + "package-lock.json", + "npm-shrinkwrap.json", + "pnpm-lock.yaml", + "yarn.lock" +]; +var METRO_CONFIG_EXTENSIONS = [ + "js", + "cjs", + "mjs", + "ts", + "cts", + "mts", + "json" +]; +var METRO_CONFIG_NAMES = METRO_CONFIG_EXTENSIONS.map((extension) => `metro.config.${extension}`); +var METRO_CONFIG_DIR_NAMES = METRO_CONFIG_EXTENSIONS.map((extension) => `.config/metro.${extension}`); +var BABEL_CONFIG_NAMES = [ + "babel.config.json", + "babel.config.js", + "babel.config.cjs", + "babel.config.mjs", + "babel.config.cts" +]; +var METRO_KEY_FILENAMES = [ + ...LOCKFILE_NAMES, + ...METRO_CONFIG_NAMES, + ...METRO_CONFIG_DIR_NAMES, + ...BABEL_CONFIG_NAMES +]; + +// ../cache/dist/index.js +var cacheLogger4 = logger.child("cache"); +var createHarnessCache = (options) => { + const paths = createHarnessCachePaths(options.projectRoot); + return { + paths, + isWarm: (domain) => isDomainWarm(domain, paths), + ensureDomainDirectories: (domain) => { + for (const directory of getDomainDirectories(domain, paths)) { + try { + import_node_fs9.default.mkdirSync(directory, { recursive: true }); + } catch (error) { + cacheLogger4.warn(`Failed to create cache directory "${directory}". Continuing with a cold cache.`, error); + } + } + }, + planRestore: (inputs) => planRestore(paths, inputs), + snapshot: () => snapshot(paths), + planSave: (before, policy, inputs) => planSave(paths, before, policy, inputs), + writeKeysFile: (plan) => writeKeysFile(paths, plan) + }; +}; + +// ../../node_modules/zod/dist/esm/v3/external.js +var external_exports = {}; +__export(external_exports, { + BRAND: () => BRAND, + DIRTY: () => DIRTY, + EMPTY_PATH: () => EMPTY_PATH, + INVALID: () => INVALID, + NEVER: () => NEVER, + OK: () => OK, + ParseStatus: () => ParseStatus, + Schema: () => ZodType, + ZodAny: () => ZodAny, + ZodArray: () => ZodArray, + ZodBigInt: () => ZodBigInt, + ZodBoolean: () => ZodBoolean, + ZodBranded: () => ZodBranded, + ZodCatch: () => ZodCatch, + ZodDate: () => ZodDate, + ZodDefault: () => ZodDefault, + ZodDiscriminatedUnion: () => ZodDiscriminatedUnion, + ZodEffects: () => ZodEffects, + ZodEnum: () => ZodEnum, + ZodError: () => ZodError, + ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind, + ZodFunction: () => ZodFunction, + ZodIntersection: () => ZodIntersection, + ZodIssueCode: () => ZodIssueCode, + ZodLazy: () => ZodLazy, + ZodLiteral: () => ZodLiteral, + ZodMap: () => ZodMap, + ZodNaN: () => ZodNaN, + ZodNativeEnum: () => ZodNativeEnum, + ZodNever: () => ZodNever, + ZodNull: () => ZodNull, + ZodNullable: () => ZodNullable, + ZodNumber: () => ZodNumber, + ZodObject: () => ZodObject, + ZodOptional: () => ZodOptional, + ZodParsedType: () => ZodParsedType, + ZodPipeline: () => ZodPipeline, + ZodPromise: () => ZodPromise, + ZodReadonly: () => ZodReadonly, + ZodRecord: () => ZodRecord, + ZodSchema: () => ZodType, + ZodSet: () => ZodSet, + ZodString: () => ZodString, + ZodSymbol: () => ZodSymbol, + ZodTransformer: () => ZodEffects, + ZodTuple: () => ZodTuple, + ZodType: () => ZodType, + ZodUndefined: () => ZodUndefined, + ZodUnion: () => ZodUnion, + ZodUnknown: () => ZodUnknown, + ZodVoid: () => ZodVoid, + addIssueToContext: () => addIssueToContext, + any: () => anyType, + array: () => arrayType, + bigint: () => bigIntType, + boolean: () => booleanType, + coerce: () => coerce, + custom: () => custom, + date: () => dateType, + datetimeRegex: () => datetimeRegex, + defaultErrorMap: () => en_default, + discriminatedUnion: () => discriminatedUnionType, + effect: () => effectsType, + enum: () => enumType, + function: () => functionType, + getErrorMap: () => getErrorMap, + getParsedType: () => getParsedType, + instanceof: () => instanceOfType, + intersection: () => intersectionType, + isAborted: () => isAborted, + isAsync: () => isAsync, + isDirty: () => isDirty, + isValid: () => isValid, + late: () => late, + lazy: () => lazyType, + literal: () => literalType, + makeIssue: () => makeIssue, + map: () => mapType, + nan: () => nanType, + nativeEnum: () => nativeEnumType, + never: () => neverType, + null: () => nullType, + nullable: () => nullableType, + number: () => numberType, + object: () => objectType, + objectUtil: () => objectUtil, + oboolean: () => oboolean, + onumber: () => onumber, + optional: () => optionalType, + ostring: () => ostring, + pipeline: () => pipelineType, + preprocess: () => preprocessType, + promise: () => promiseType, + quotelessJson: () => quotelessJson, + record: () => recordType, + set: () => setType, + setErrorMap: () => setErrorMap, + strictObject: () => strictObjectType, + string: () => stringType, + symbol: () => symbolType, + transformer: () => effectsType, + tuple: () => tupleType, + undefined: () => undefinedType, + union: () => unionType, + unknown: () => unknownType, + util: () => util2, + void: () => voidType +}); + +// ../../node_modules/zod/dist/esm/v3/helpers/util.js +var util2; +(function(util3) { + util3.assertEqual = (_2) => { + }; + function assertIs(_arg) { + } + util3.assertIs = assertIs; + function assertNever(_x) { + throw new Error(); + } + util3.assertNever = assertNever; + util3.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util3.getValidEnumValues = (obj) => { + const validKeys = util3.objectKeys(obj).filter((k3) => typeof obj[obj[k3]] !== "number"); + const filtered = {}; + for (const k3 of validKeys) { + filtered[k3] = obj[k3]; + } + return util3.objectValues(filtered); + }; + util3.objectValues = (obj) => { + return util3.objectKeys(obj).map(function(e) { + return obj[e]; + }); + }; + util3.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object) => { + const keys = []; + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + keys.push(key); + } + } + return keys; + }; + util3.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return void 0; + }; + util3.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val; + function joinValues(array, separator = " | ") { + return array.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator); + } + util3.joinValues = joinValues; + util3.jsonStringifyReplacer = (_2, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util2 || (util2 = {})); +var objectUtil; +(function(objectUtil2) { + objectUtil2.mergeShapes = (first, second) => { + return { + ...first, + ...second + // second overwrites first + }; + }; +})(objectUtil || (objectUtil = {})); +var ZodParsedType = util2.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set" +]); +var getParsedType = (data) => { + const t2 = typeof data; + switch (t2) { + case "undefined": + return ZodParsedType.undefined; + case "string": + return ZodParsedType.string; + case "number": + return Number.isNaN(data) ? ZodParsedType.nan : ZodParsedType.number; + case "boolean": + return ZodParsedType.boolean; + case "function": + return ZodParsedType.function; + case "bigint": + return ZodParsedType.bigint; + case "symbol": + return ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return ZodParsedType.array; + } + if (data === null) { + return ZodParsedType.null; + } + if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") { + return ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return ZodParsedType.date; + } + return ZodParsedType.object; + default: + return ZodParsedType.unknown; + } +}; + +// ../../node_modules/zod/dist/esm/v3/ZodError.js +var ZodIssueCode = util2.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite" +]); +var quotelessJson = (obj) => { + const json = JSON.stringify(obj, null, 2); + return json.replace(/"([^"]+)":/g, "$1:"); +}; +var ZodError = class _ZodError extends Error { + get errors() { + return this.issues; + } + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(this, actualProto); + } else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + format(_mapper) { + const mapper = _mapper || function(issue) { + return issue.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error) => { + for (const issue of error.issues) { + if (issue.code === "invalid_union") { + issue.unionErrors.map(processError); + } else if (issue.code === "invalid_return_type") { + processError(issue.returnTypeError); + } else if (issue.code === "invalid_arguments") { + processError(issue.argumentsError); + } else if (issue.path.length === 0) { + fieldErrors._errors.push(mapper(issue)); + } else { + let curr = fieldErrors; + let i = 0; + while (i < issue.path.length) { + const el = issue.path[i]; + const terminal = i === issue.path.length - 1; + if (!terminal) { + curr[el] = curr[el] || { _errors: [] }; + } else { + curr[el] = curr[el] || { _errors: [] }; + curr[el]._errors.push(mapper(issue)); + } + curr = curr[el]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + static assert(value) { + if (!(value instanceof _ZodError)) { + throw new Error(`Not a ZodError: ${value}`); + } + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util2.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue) => issue.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +}; +ZodError.create = (issues) => { + const error = new ZodError(issues); + return error; +}; + +// ../../node_modules/zod/dist/esm/v3/locales/en.js +var errorMap = (issue, _ctx) => { + let message; + switch (issue.code) { + case ZodIssueCode.invalid_type: + if (issue.received === ZodParsedType.undefined) { + message = "Required"; + } else { + message = `Expected ${issue.expected}, received ${issue.received}`; + } + break; + case ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue.expected, util2.jsonStringifyReplacer)}`; + break; + case ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util2.joinValues(issue.keys, ", ")}`; + break; + case ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util2.joinValues(issue.options)}`; + break; + case ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util2.joinValues(issue.options)}, received '${issue.received}'`; + break; + case ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodIssueCode.invalid_string: + if (typeof issue.validation === "object") { + if ("includes" in issue.validation) { + message = `Invalid input: must include "${issue.validation.includes}"`; + if (typeof issue.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue.validation.position}`; + } + } else if ("startsWith" in issue.validation) { + message = `Invalid input: must start with "${issue.validation.startsWith}"`; + } else if ("endsWith" in issue.validation) { + message = `Invalid input: must end with "${issue.validation.endsWith}"`; + } else { + util2.assertNever(issue.validation); + } + } else if (issue.validation !== "regex") { + message = `Invalid ${issue.validation}`; + } else { + message = "Invalid"; + } + break; + case ZodIssueCode.too_small: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `more than`} ${issue.minimum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? "exactly" : issue.inclusive ? `at least` : `over`} ${issue.minimum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${issue.minimum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact ? `exactly equal to ` : issue.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue.minimum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.too_big: + if (issue.type === "array") + message = `Array must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `less than`} ${issue.maximum} element(s)`; + else if (issue.type === "string") + message = `String must contain ${issue.exact ? `exactly` : issue.inclusive ? `at most` : `under`} ${issue.maximum} character(s)`; + else if (issue.type === "number") + message = `Number must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`; + else if (issue.type === "bigint") + message = `BigInt must be ${issue.exact ? `exactly` : issue.inclusive ? `less than or equal to` : `less than`} ${issue.maximum}`; + else if (issue.type === "date") + message = `Date must be ${issue.exact ? `exactly` : issue.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue.maximum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue.multipleOf}`; + break; + case ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util2.assertNever(issue); + } + return { message }; +}; +var en_default = errorMap; + +// ../../node_modules/zod/dist/esm/v3/errors.js +var overrideErrorMap = en_default; +function setErrorMap(map) { + overrideErrorMap = map; +} +function getErrorMap() { + return overrideErrorMap; +} + +// ../../node_modules/zod/dist/esm/v3/helpers/parseUtil.js +var makeIssue = (params) => { + const { data, path: path11, errorMaps, issueData } = params; + const fullPath = [...path11, ...issueData.path || []]; + const fullIssue = { + ...issueData, + path: fullPath + }; + if (issueData.message !== void 0) { + return { + ...issueData, + path: fullPath, + message: issueData.message + }; + } + let errorMessage = ""; + const maps = errorMaps.filter((m) => !!m).slice().reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: errorMessage + }; +}; +var EMPTY_PATH = []; +function addIssueToContext(ctx, issueData) { + const overrideMap = getErrorMap(); + const issue = makeIssue({ + issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + // contextual error map is first priority + ctx.schemaErrorMap, + // then schema-bound map if available + overrideMap, + // then global override map + overrideMap === en_default ? void 0 : en_default + // then global default map + ].filter((x2) => !!x2) + }); + ctx.common.issues.push(issue); +} +var ParseStatus = class _ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s of results) { + if (s.status === "aborted") + return INVALID; + if (s.status === "dirty") + status.dirty(); + arrayValue.push(s.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value + }); + } + return _ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return INVALID; + if (value.status === "aborted") + return INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +}; +var INVALID = Object.freeze({ + status: "aborted" +}); +var DIRTY = (value) => ({ status: "dirty", value }); +var OK = (value) => ({ status: "valid", value }); +var isAborted = (x2) => x2.status === "aborted"; +var isDirty = (x2) => x2.status === "dirty"; +var isValid = (x2) => x2.status === "valid"; +var isAsync = (x2) => typeof Promise !== "undefined" && x2 instanceof Promise; + +// ../../node_modules/zod/dist/esm/v3/helpers/errorUtil.js +var errorUtil; +(function(errorUtil2) { + errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message; +})(errorUtil || (errorUtil = {})); + +// ../../node_modules/zod/dist/esm/v3/types.js +var ParseInputLazyPath = class { + constructor(parent, value, path11, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path11; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (Array.isArray(this._key)) { + this._cachedPath.push(...this._path, ...this._key); + } else { + this._cachedPath.push(...this._path, this._key); + } + } + return this._cachedPath; + } +}; +var handleResult = (ctx, result) => { + if (isValid(result)) { + return { success: true, data: result.value }; + } else { + if (!ctx.common.issues.length) { + throw new Error("Validation failed but no issues detected."); + } + return { + success: false, + get error() { + if (this._error) + return this._error; + const error = new ZodError(ctx.common.issues); + this._error = error; + return this._error; + } + }; + } +}; +function processCreateParams(params) { + if (!params) + return {}; + const { errorMap: errorMap2, invalid_type_error, required_error, description } = params; + if (errorMap2 && (invalid_type_error || required_error)) { + throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); + } + if (errorMap2) + return { errorMap: errorMap2, description }; + const customMap = (iss, ctx) => { + const { message } = params; + if (iss.code === "invalid_enum_value") { + return { message: message ?? ctx.defaultError }; + } + if (typeof ctx.data === "undefined") { + return { message: message ?? required_error ?? ctx.defaultError }; + } + if (iss.code !== "invalid_type") + return { message: ctx.defaultError }; + return { message: message ?? invalid_type_error ?? ctx.defaultError }; + }; + return { errorMap: customMap, description }; +} +var ZodType = class { + get description() { + return this._def.description; + } + _getType(input) { + return getParsedType(input.data); + } + _getOrReturnCtx(input, ctx) { + return ctx || { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + }; + } + _processInputParams(input) { + return { + status: new ParseStatus(), + ctx: { + common: input.parent.common, + data: input.data, + parsedType: getParsedType(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + } + }; + } + _parseSync(input) { + const result = this._parse(input); + if (isAsync(result)) { + throw new Error("Synchronous parse encountered promise."); + } + return result; + } + _parseAsync(input) { + const result = this._parse(input); + return Promise.resolve(result); + } + parse(data, params) { + const result = this.safeParse(data, params); + if (result.success) + return result.data; + throw result.error; + } + safeParse(data, params) { + const ctx = { + common: { + issues: [], + async: params?.async ?? false, + contextualErrorMap: params?.errorMap + }, + path: params?.path || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + const result = this._parseSync({ data, path: ctx.path, parent: ctx }); + return handleResult(ctx, result); + } + "~validate"(data) { + const ctx = { + common: { + issues: [], + async: !!this["~standard"].async + }, + path: [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + if (!this["~standard"].async) { + try { + const result = this._parseSync({ data, path: [], parent: ctx }); + return isValid(result) ? { + value: result.value + } : { + issues: ctx.common.issues + }; + } catch (err) { + if (err?.message?.toLowerCase()?.includes("encountered")) { + this["~standard"].async = true; + } + ctx.common = { + issues: [], + async: true + }; + } + } + return this._parseAsync({ data, path: [], parent: ctx }).then((result) => isValid(result) ? { + value: result.value + } : { + issues: ctx.common.issues + }); + } + async parseAsync(data, params) { + const result = await this.safeParseAsync(data, params); + if (result.success) + return result.data; + throw result.error; + } + async safeParseAsync(data, params) { + const ctx = { + common: { + issues: [], + contextualErrorMap: params?.errorMap, + async: true + }, + path: params?.path || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType(data) + }; + const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx }); + const result = await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult)); + return handleResult(ctx, result); + } + refine(check, message) { + const getIssueProperties = (val) => { + if (typeof message === "string" || typeof message === "undefined") { + return { message }; + } else if (typeof message === "function") { + return message(val); + } else { + return message; + } + }; + return this._refinement((val, ctx) => { + const result = check(val); + const setError = () => ctx.addIssue({ + code: ZodIssueCode.custom, + ...getIssueProperties(val) + }); + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then((data) => { + if (!data) { + setError(); + return false; + } else { + return true; + } + }); + } + if (!result) { + setError(); + return false; + } else { + return true; + } + }); + } + refinement(check, refinementData) { + return this._refinement((val, ctx) => { + if (!check(val)) { + ctx.addIssue(typeof refinementData === "function" ? refinementData(val, ctx) : refinementData); + return false; + } else { + return true; + } + }); + } + _refinement(refinement) { + return new ZodEffects({ + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "refinement", refinement } + }); + } + superRefine(refinement) { + return this._refinement(refinement); + } + constructor(def) { + this.spa = this.safeParseAsync; + this._def = def; + this.parse = this.parse.bind(this); + this.safeParse = this.safeParse.bind(this); + this.parseAsync = this.parseAsync.bind(this); + this.safeParseAsync = this.safeParseAsync.bind(this); + this.spa = this.spa.bind(this); + this.refine = this.refine.bind(this); + this.refinement = this.refinement.bind(this); + this.superRefine = this.superRefine.bind(this); + this.optional = this.optional.bind(this); + this.nullable = this.nullable.bind(this); + this.nullish = this.nullish.bind(this); + this.array = this.array.bind(this); + this.promise = this.promise.bind(this); + this.or = this.or.bind(this); + this.and = this.and.bind(this); + this.transform = this.transform.bind(this); + this.brand = this.brand.bind(this); + this.default = this.default.bind(this); + this.catch = this.catch.bind(this); + this.describe = this.describe.bind(this); + this.pipe = this.pipe.bind(this); + this.readonly = this.readonly.bind(this); + this.isNullable = this.isNullable.bind(this); + this.isOptional = this.isOptional.bind(this); + this["~standard"] = { + version: 1, + vendor: "zod", + validate: (data) => this["~validate"](data) + }; + } + optional() { + return ZodOptional.create(this, this._def); + } + nullable() { + return ZodNullable.create(this, this._def); + } + nullish() { + return this.nullable().optional(); + } + array() { + return ZodArray.create(this); + } + promise() { + return ZodPromise.create(this, this._def); + } + or(option) { + return ZodUnion.create([this, option], this._def); + } + and(incoming) { + return ZodIntersection.create(this, incoming, this._def); + } + transform(transform) { + return new ZodEffects({ + ...processCreateParams(this._def), + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "transform", transform } + }); + } + default(def) { + const defaultValueFunc = typeof def === "function" ? def : () => def; + return new ZodDefault({ + ...processCreateParams(this._def), + innerType: this, + defaultValue: defaultValueFunc, + typeName: ZodFirstPartyTypeKind.ZodDefault + }); + } + brand() { + return new ZodBranded({ + typeName: ZodFirstPartyTypeKind.ZodBranded, + type: this, + ...processCreateParams(this._def) + }); + } + catch(def) { + const catchValueFunc = typeof def === "function" ? def : () => def; + return new ZodCatch({ + ...processCreateParams(this._def), + innerType: this, + catchValue: catchValueFunc, + typeName: ZodFirstPartyTypeKind.ZodCatch + }); + } + describe(description) { + const This = this.constructor; + return new This({ + ...this._def, + description + }); + } + pipe(target) { + return ZodPipeline.create(this, target); + } + readonly() { + return ZodReadonly.create(this); + } + isOptional() { + return this.safeParse(void 0).success; + } + isNullable() { + return this.safeParse(null).success; + } +}; +var cuidRegex = /^c[^\s-]{8,}$/i; +var cuid2Regex = /^[0-9a-z]+$/; +var ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; +var uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i; +var nanoidRegex = /^[a-z0-9_-]{21}$/i; +var jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/; +var durationRegex = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/; +var emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i; +var _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`; +var emojiRegex; +var ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; +var ipv4CidrRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/; +var ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; +var ipv6CidrRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/; +var base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; +var base64urlRegex = /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/; +var dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`; +var dateRegex = new RegExp(`^${dateRegexSource}$`); +function timeRegexSource(args) { + let secondsRegexSource = `[0-5]\\d`; + if (args.precision) { + secondsRegexSource = `${secondsRegexSource}\\.\\d{${args.precision}}`; + } else if (args.precision == null) { + secondsRegexSource = `${secondsRegexSource}(\\.\\d+)?`; + } + const secondsQuantifier = args.precision ? "+" : "?"; + return `([01]\\d|2[0-3]):[0-5]\\d(:${secondsRegexSource})${secondsQuantifier}`; +} +function timeRegex(args) { + return new RegExp(`^${timeRegexSource(args)}$`); +} +function datetimeRegex(args) { + let regex = `${dateRegexSource}T${timeRegexSource(args)}`; + const opts = []; + opts.push(args.local ? `Z?` : `Z`); + if (args.offset) + opts.push(`([+-]\\d{2}:?\\d{2})`); + regex = `${regex}(${opts.join("|")})`; + return new RegExp(`^${regex}$`); +} +function isValidIP(ip, version) { + if ((version === "v4" || !version) && ipv4Regex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6Regex.test(ip)) { + return true; + } + return false; +} +function isValidJWT(jwt, alg) { + if (!jwtRegex.test(jwt)) + return false; + try { + const [header] = jwt.split("."); + const base64 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "="); + const decoded = JSON.parse(atob(base64)); + if (typeof decoded !== "object" || decoded === null) + return false; + if ("typ" in decoded && decoded?.typ !== "JWT") + return false; + if (!decoded.alg) + return false; + if (alg && decoded.alg !== alg) + return false; + return true; + } catch { + return false; + } +} +function isValidCidr(ip, version) { + if ((version === "v4" || !version) && ipv4CidrRegex.test(ip)) { + return true; + } + if ((version === "v6" || !version) && ipv6CidrRegex.test(ip)) { + return true; + } + return false; +} +var ZodString = class _ZodString extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = String(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.string) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.string, + received: ctx2.parsedType + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.length < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + if (input.data.length > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "length") { + const tooBig = input.data.length > check.value; + const tooSmall = input.data.length < check.value; + if (tooBig || tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + if (tooBig) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message + }); + } else if (tooSmall) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "string", + inclusive: true, + exact: true, + message: check.message + }); + } + status.dirty(); + } + } else if (check.kind === "email") { + if (!emailRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "email", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "emoji") { + if (!emojiRegex) { + emojiRegex = new RegExp(_emojiRegex, "u"); + } + if (!emojiRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "emoji", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "uuid") { + if (!uuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "uuid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "nanoid") { + if (!nanoidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "nanoid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cuid") { + if (!cuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cuid2") { + if (!cuid2Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid2", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "ulid") { + if (!ulidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ulid", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "url") { + try { + new URL(input.data); + } catch { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "url", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "regex") { + check.regex.lastIndex = 0; + const testResult = check.regex.test(input.data); + if (!testResult) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "regex", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "trim") { + input.data = input.data.trim(); + } else if (check.kind === "includes") { + if (!input.data.includes(check.value, check.position)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { includes: check.value, position: check.position }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "toLowerCase") { + input.data = input.data.toLowerCase(); + } else if (check.kind === "toUpperCase") { + input.data = input.data.toUpperCase(); + } else if (check.kind === "startsWith") { + if (!input.data.startsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { startsWith: check.value }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "endsWith") { + if (!input.data.endsWith(check.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { endsWith: check.value }, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "datetime") { + const regex = datetimeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "datetime", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "date") { + const regex = dateRegex; + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "date", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "time") { + const regex = timeRegex(check); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "time", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "duration") { + if (!durationRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "duration", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "ip") { + if (!isValidIP(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ip", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "jwt") { + if (!isValidJWT(input.data, check.alg)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "jwt", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "cidr") { + if (!isValidCidr(input.data, check.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cidr", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "base64") { + if (!base64Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "base64url") { + if (!base64urlRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64url", + code: ZodIssueCode.invalid_string, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _regex(regex, validation, message) { + return this.refinement((data) => regex.test(data), { + validation, + code: ZodIssueCode.invalid_string, + ...errorUtil.errToObj(message) + }); + } + _addCheck(check) { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + email(message) { + return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) }); + } + url(message) { + return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) }); + } + emoji(message) { + return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) }); + } + uuid(message) { + return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) }); + } + nanoid(message) { + return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(message) }); + } + cuid(message) { + return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) }); + } + cuid2(message) { + return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) }); + } + ulid(message) { + return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) }); + } + base64(message) { + return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message) }); + } + base64url(message) { + return this._addCheck({ + kind: "base64url", + ...errorUtil.errToObj(message) + }); + } + jwt(options) { + return this._addCheck({ kind: "jwt", ...errorUtil.errToObj(options) }); + } + ip(options) { + return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) }); + } + cidr(options) { + return this._addCheck({ kind: "cidr", ...errorUtil.errToObj(options) }); + } + datetime(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "datetime", + precision: null, + offset: false, + local: false, + message: options + }); + } + return this._addCheck({ + kind: "datetime", + precision: typeof options?.precision === "undefined" ? null : options?.precision, + offset: options?.offset ?? false, + local: options?.local ?? false, + ...errorUtil.errToObj(options?.message) + }); + } + date(message) { + return this._addCheck({ kind: "date", message }); + } + time(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "time", + precision: null, + message: options + }); + } + return this._addCheck({ + kind: "time", + precision: typeof options?.precision === "undefined" ? null : options?.precision, + ...errorUtil.errToObj(options?.message) + }); + } + duration(message) { + return this._addCheck({ kind: "duration", ...errorUtil.errToObj(message) }); + } + regex(regex, message) { + return this._addCheck({ + kind: "regex", + regex, + ...errorUtil.errToObj(message) + }); + } + includes(value, options) { + return this._addCheck({ + kind: "includes", + value, + position: options?.position, + ...errorUtil.errToObj(options?.message) + }); + } + startsWith(value, message) { + return this._addCheck({ + kind: "startsWith", + value, + ...errorUtil.errToObj(message) + }); + } + endsWith(value, message) { + return this._addCheck({ + kind: "endsWith", + value, + ...errorUtil.errToObj(message) + }); + } + min(minLength, message) { + return this._addCheck({ + kind: "min", + value: minLength, + ...errorUtil.errToObj(message) + }); + } + max(maxLength, message) { + return this._addCheck({ + kind: "max", + value: maxLength, + ...errorUtil.errToObj(message) + }); + } + length(len, message) { + return this._addCheck({ + kind: "length", + value: len, + ...errorUtil.errToObj(message) + }); + } + /** + * Equivalent to `.min(1)` + */ + nonempty(message) { + return this.min(1, errorUtil.errToObj(message)); + } + trim() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "trim" }] + }); + } + toLowerCase() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toLowerCase" }] + }); + } + toUpperCase() { + return new _ZodString({ + ...this._def, + checks: [...this._def.checks, { kind: "toUpperCase" }] + }); + } + get isDatetime() { + return !!this._def.checks.find((ch) => ch.kind === "datetime"); + } + get isDate() { + return !!this._def.checks.find((ch) => ch.kind === "date"); + } + get isTime() { + return !!this._def.checks.find((ch) => ch.kind === "time"); + } + get isDuration() { + return !!this._def.checks.find((ch) => ch.kind === "duration"); + } + get isEmail() { + return !!this._def.checks.find((ch) => ch.kind === "email"); + } + get isURL() { + return !!this._def.checks.find((ch) => ch.kind === "url"); + } + get isEmoji() { + return !!this._def.checks.find((ch) => ch.kind === "emoji"); + } + get isUUID() { + return !!this._def.checks.find((ch) => ch.kind === "uuid"); + } + get isNANOID() { + return !!this._def.checks.find((ch) => ch.kind === "nanoid"); + } + get isCUID() { + return !!this._def.checks.find((ch) => ch.kind === "cuid"); + } + get isCUID2() { + return !!this._def.checks.find((ch) => ch.kind === "cuid2"); + } + get isULID() { + return !!this._def.checks.find((ch) => ch.kind === "ulid"); + } + get isIP() { + return !!this._def.checks.find((ch) => ch.kind === "ip"); + } + get isCIDR() { + return !!this._def.checks.find((ch) => ch.kind === "cidr"); + } + get isBase64() { + return !!this._def.checks.find((ch) => ch.kind === "base64"); + } + get isBase64url() { + return !!this._def.checks.find((ch) => ch.kind === "base64url"); + } + get minLength() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxLength() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodString.create = (params) => { + return new ZodString({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodString, + coerce: params?.coerce ?? false, + ...processCreateParams(params) + }); +}; +function floatSafeRemainder(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = Number.parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", "")); + return valInt % stepInt / 10 ** decCount; +} +var ZodNumber = class _ZodNumber extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + this.step = this.multipleOf; + } + _parse(input) { + if (this._def.coerce) { + input.data = Number(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.number) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.number, + received: ctx2.parsedType + }); + return INVALID; + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "int") { + if (!util2.isInteger(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: "integer", + received: "float", + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "min") { + const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check.value, + type: "number", + inclusive: check.inclusive, + exact: false, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "multipleOf") { + if (floatSafeRemainder(input.data, check.value) !== 0) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "finite") { + if (!Number.isFinite(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_finite, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodNumber({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check) { + return new _ZodNumber({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + int(message) { + return this._addCheck({ + kind: "int", + message: errorUtil.toString(message) + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + finite(message) { + return this._addCheck({ + kind: "finite", + message: errorUtil.toString(message) + }); + } + safe(message) { + return this._addCheck({ + kind: "min", + inclusive: true, + value: Number.MIN_SAFE_INTEGER, + message: errorUtil.toString(message) + })._addCheck({ + kind: "max", + inclusive: true, + value: Number.MAX_SAFE_INTEGER, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } + get isInt() { + return !!this._def.checks.find((ch) => ch.kind === "int" || ch.kind === "multipleOf" && util2.isInteger(ch.value)); + } + get isFinite() { + let max = null; + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "finite" || ch.kind === "int" || ch.kind === "multipleOf") { + return true; + } else if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } else if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return Number.isFinite(min) && Number.isFinite(max); + } +}; +ZodNumber.create = (params) => { + return new ZodNumber({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodNumber, + coerce: params?.coerce || false, + ...processCreateParams(params) + }); +}; +var ZodBigInt = class _ZodBigInt extends ZodType { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + } + _parse(input) { + if (this._def.coerce) { + try { + input.data = BigInt(input.data); + } catch { + return this._getInvalidInput(input); + } + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.bigint) { + return this._getInvalidInput(input); + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check of this._def.checks) { + if (check.kind === "min") { + const tooSmall = check.inclusive ? input.data < check.value : input.data <= check.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + type: "bigint", + minimum: check.value, + inclusive: check.inclusive, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "max") { + const tooBig = check.inclusive ? input.data > check.value : input.data >= check.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + type: "bigint", + maximum: check.value, + inclusive: check.inclusive, + message: check.message + }); + status.dirty(); + } + } else if (check.kind === "multipleOf") { + if (input.data % check.value !== BigInt(0)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check.value, + message: check.message + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { status: status.value, value: input.data }; + } + _getInvalidInput(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.bigint, + received: ctx.parsedType + }); + return INVALID; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodBigInt({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check) { + return new _ZodBigInt({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodBigInt.create = (params) => { + return new ZodBigInt({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodBigInt, + coerce: params?.coerce ?? false, + ...processCreateParams(params) + }); +}; +var ZodBoolean = class extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = Boolean(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.boolean) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.boolean, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodBoolean.create = (params) => { + return new ZodBoolean({ + typeName: ZodFirstPartyTypeKind.ZodBoolean, + coerce: params?.coerce || false, + ...processCreateParams(params) + }); +}; +var ZodDate = class _ZodDate extends ZodType { + _parse(input) { + if (this._def.coerce) { + input.data = new Date(input.data); + } + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.date) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.date, + received: ctx2.parsedType + }); + return INVALID; + } + if (Number.isNaN(input.data.getTime())) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_date + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check of this._def.checks) { + if (check.kind === "min") { + if (input.data.getTime() < check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + message: check.message, + inclusive: true, + exact: false, + minimum: check.value, + type: "date" + }); + status.dirty(); + } + } else if (check.kind === "max") { + if (input.data.getTime() > check.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + message: check.message, + inclusive: true, + exact: false, + maximum: check.value, + type: "date" + }); + status.dirty(); + } + } else { + util2.assertNever(check); + } + } + return { + status: status.value, + value: new Date(input.data.getTime()) + }; + } + _addCheck(check) { + return new _ZodDate({ + ...this._def, + checks: [...this._def.checks, check] + }); + } + min(minDate, message) { + return this._addCheck({ + kind: "min", + value: minDate.getTime(), + message: errorUtil.toString(message) + }); + } + max(maxDate, message) { + return this._addCheck({ + kind: "max", + value: maxDate.getTime(), + message: errorUtil.toString(message) + }); + } + get minDate() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min != null ? new Date(min) : null; + } + get maxDate() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max != null ? new Date(max) : null; + } +}; +ZodDate.create = (params) => { + return new ZodDate({ + checks: [], + coerce: params?.coerce || false, + typeName: ZodFirstPartyTypeKind.ZodDate, + ...processCreateParams(params) + }); +}; +var ZodSymbol = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodSymbol.create = (params) => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params) + }); +}; +var ZodUndefined = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.undefined, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodUndefined.create = (params) => { + return new ZodUndefined({ + typeName: ZodFirstPartyTypeKind.ZodUndefined, + ...processCreateParams(params) + }); +}; +var ZodNull = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.null) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.null, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodNull.create = (params) => { + return new ZodNull({ + typeName: ZodFirstPartyTypeKind.ZodNull, + ...processCreateParams(params) + }); +}; +var ZodAny = class extends ZodType { + constructor() { + super(...arguments); + this._any = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodAny.create = (params) => { + return new ZodAny({ + typeName: ZodFirstPartyTypeKind.ZodAny, + ...processCreateParams(params) + }); +}; +var ZodUnknown = class extends ZodType { + constructor() { + super(...arguments); + this._unknown = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodUnknown.create = (params) => { + return new ZodUnknown({ + typeName: ZodFirstPartyTypeKind.ZodUnknown, + ...processCreateParams(params) + }); +}; +var ZodNever = class extends ZodType { + _parse(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.never, + received: ctx.parsedType + }); + return INVALID; + } +}; +ZodNever.create = (params) => { + return new ZodNever({ + typeName: ZodFirstPartyTypeKind.ZodNever, + ...processCreateParams(params) + }); +}; +var ZodVoid = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.void, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodVoid.create = (params) => { + return new ZodVoid({ + typeName: ZodFirstPartyTypeKind.ZodVoid, + ...processCreateParams(params) + }); +}; +var ZodArray = class _ZodArray extends ZodType { + _parse(input) { + const { ctx, status } = this._processInputParams(input); + const def = this._def; + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (def.exactLength !== null) { + const tooBig = ctx.data.length > def.exactLength.value; + const tooSmall = ctx.data.length < def.exactLength.value; + if (tooBig || tooSmall) { + addIssueToContext(ctx, { + code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small, + minimum: tooSmall ? def.exactLength.value : void 0, + maximum: tooBig ? def.exactLength.value : void 0, + type: "array", + inclusive: true, + exact: true, + message: def.exactLength.message + }); + status.dirty(); + } + } + if (def.minLength !== null) { + if (ctx.data.length < def.minLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.minLength.message + }); + status.dirty(); + } + } + if (def.maxLength !== null) { + if (ctx.data.length > def.maxLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.maxLength.message + }); + status.dirty(); + } + } + if (ctx.common.async) { + return Promise.all([...ctx.data].map((item, i) => { + return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + })).then((result2) => { + return ParseStatus.mergeArray(status, result2); + }); + } + const result = [...ctx.data].map((item, i) => { + return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + }); + return ParseStatus.mergeArray(status, result); + } + get element() { + return this._def.type; + } + min(minLength, message) { + return new _ZodArray({ + ...this._def, + minLength: { value: minLength, message: errorUtil.toString(message) } + }); + } + max(maxLength, message) { + return new _ZodArray({ + ...this._def, + maxLength: { value: maxLength, message: errorUtil.toString(message) } + }); + } + length(len, message) { + return new _ZodArray({ + ...this._def, + exactLength: { value: len, message: errorUtil.toString(message) } + }); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodArray.create = (schema, params) => { + return new ZodArray({ + type: schema, + minLength: null, + maxLength: null, + exactLength: null, + typeName: ZodFirstPartyTypeKind.ZodArray, + ...processCreateParams(params) + }); +}; +function deepPartialify(schema) { + if (schema instanceof ZodObject) { + const newShape = {}; + for (const key in schema.shape) { + const fieldSchema = schema.shape[key]; + newShape[key] = ZodOptional.create(deepPartialify(fieldSchema)); + } + return new ZodObject({ + ...schema._def, + shape: () => newShape + }); + } else if (schema instanceof ZodArray) { + return new ZodArray({ + ...schema._def, + type: deepPartialify(schema.element) + }); + } else if (schema instanceof ZodOptional) { + return ZodOptional.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodNullable) { + return ZodNullable.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodTuple) { + return ZodTuple.create(schema.items.map((item) => deepPartialify(item))); + } else { + return schema; + } +} +var ZodObject = class _ZodObject extends ZodType { + constructor() { + super(...arguments); + this._cached = null; + this.nonstrict = this.passthrough; + this.augment = this.extend; + } + _getCached() { + if (this._cached !== null) + return this._cached; + const shape = this._def.shape(); + const keys = util2.objectKeys(shape); + this._cached = { shape, keys }; + return this._cached; + } + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.object) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx2.parsedType + }); + return INVALID; + } + const { status, ctx } = this._processInputParams(input); + const { shape, keys: shapeKeys } = this._getCached(); + const extraKeys = []; + if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) { + for (const key in ctx.data) { + if (!shapeKeys.includes(key)) { + extraKeys.push(key); + } + } + } + const pairs = []; + for (const key of shapeKeys) { + const keyValidator = shape[key]; + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (this._def.catchall instanceof ZodNever) { + const unknownKeys = this._def.unknownKeys; + if (unknownKeys === "passthrough") { + for (const key of extraKeys) { + pairs.push({ + key: { status: "valid", value: key }, + value: { status: "valid", value: ctx.data[key] } + }); + } + } else if (unknownKeys === "strict") { + if (extraKeys.length > 0) { + addIssueToContext(ctx, { + code: ZodIssueCode.unrecognized_keys, + keys: extraKeys + }); + status.dirty(); + } + } else if (unknownKeys === "strip") { + } else { + throw new Error(`Internal ZodObject error: invalid unknownKeys value.`); + } + } else { + const catchall = this._def.catchall; + for (const key of extraKeys) { + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: catchall._parse( + new ParseInputLazyPath(ctx, value, ctx.path, key) + //, ctx.child(key), value, getParsedType(value) + ), + alwaysSet: key in ctx.data + }); + } + } + if (ctx.common.async) { + return Promise.resolve().then(async () => { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value, + alwaysSet: pair.alwaysSet + }); + } + return syncPairs; + }).then((syncPairs) => { + return ParseStatus.mergeObjectSync(status, syncPairs); + }); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get shape() { + return this._def.shape(); + } + strict(message) { + errorUtil.errToObj; + return new _ZodObject({ + ...this._def, + unknownKeys: "strict", + ...message !== void 0 ? { + errorMap: (issue, ctx) => { + const defaultError = this._def.errorMap?.(issue, ctx).message ?? ctx.defaultError; + if (issue.code === "unrecognized_keys") + return { + message: errorUtil.errToObj(message).message ?? defaultError + }; + return { + message: defaultError + }; + } + } : {} + }); + } + strip() { + return new _ZodObject({ + ...this._def, + unknownKeys: "strip" + }); + } + passthrough() { + return new _ZodObject({ + ...this._def, + unknownKeys: "passthrough" + }); + } + // const AugmentFactory = + // (def: Def) => + // ( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, Augmentation>, + // Def["unknownKeys"], + // Def["catchall"] + // > => { + // return new ZodObject({ + // ...def, + // shape: () => ({ + // ...def.shape(), + // ...augmentation, + // }), + // }) as any; + // }; + extend(augmentation) { + return new _ZodObject({ + ...this._def, + shape: () => ({ + ...this._def.shape(), + ...augmentation + }) + }); + } + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging) { + const merged = new _ZodObject({ + unknownKeys: merging._def.unknownKeys, + catchall: merging._def.catchall, + shape: () => ({ + ...this._def.shape(), + ...merging._def.shape() + }), + typeName: ZodFirstPartyTypeKind.ZodObject + }); + return merged; + } + // merge< + // Incoming extends AnyZodObject, + // Augmentation extends Incoming["shape"], + // NewOutput extends { + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }, + // NewInput extends { + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // } + // >( + // merging: Incoming + // ): ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"], + // NewOutput, + // NewInput + // > { + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + setKey(key, schema) { + return this.augment({ [key]: schema }); + } + // merge( + // merging: Incoming + // ): //ZodObject = (merging) => { + // ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"] + // > { + // // const mergedShape = objectUtil.mergeShapes( + // // this._def.shape(), + // // merging._def.shape() + // // ); + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + catchall(index) { + return new _ZodObject({ + ...this._def, + catchall: index + }); + } + pick(mask) { + const shape = {}; + for (const key of util2.objectKeys(mask)) { + if (mask[key] && this.shape[key]) { + shape[key] = this.shape[key]; + } + } + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + omit(mask) { + const shape = {}; + for (const key of util2.objectKeys(this.shape)) { + if (!mask[key]) { + shape[key] = this.shape[key]; + } + } + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + /** + * @deprecated + */ + deepPartial() { + return deepPartialify(this); + } + partial(mask) { + const newShape = {}; + for (const key of util2.objectKeys(this.shape)) { + const fieldSchema = this.shape[key]; + if (mask && !mask[key]) { + newShape[key] = fieldSchema; + } else { + newShape[key] = fieldSchema.optional(); + } + } + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + required(mask) { + const newShape = {}; + for (const key of util2.objectKeys(this.shape)) { + if (mask && !mask[key]) { + newShape[key] = this.shape[key]; + } else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional) { + newField = newField._def.innerType; + } + newShape[key] = newField; + } + } + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + keyof() { + return createZodEnum(util2.objectKeys(this.shape)); + } +}; +ZodObject.create = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject.strictCreate = (shape, params) => { + return new ZodObject({ + shape: () => shape, + unknownKeys: "strict", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject.lazycreate = (shape, params) => { + return new ZodObject({ + shape, + unknownKeys: "strip", + catchall: ZodNever.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +var ZodUnion = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const options = this._def.options; + function handleResults(results) { + for (const result of results) { + if (result.result.status === "valid") { + return result.result; + } + } + for (const result of results) { + if (result.result.status === "dirty") { + ctx.common.issues.push(...result.ctx.common.issues); + return result.result; + } + } + const unionErrors = results.map((result) => new ZodError(result.ctx.common.issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + if (ctx.common.async) { + return Promise.all(options.map(async (option) => { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + return { + result: await option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }), + ctx: childCtx + }; + })).then(handleResults); + } else { + let dirty = void 0; + const issues = []; + for (const option of options) { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }); + if (result.status === "valid") { + return result; + } else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + if (childCtx.common.issues.length) { + issues.push(childCtx.common.issues); + } + } + if (dirty) { + ctx.common.issues.push(...dirty.ctx.common.issues); + return dirty.result; + } + const unionErrors = issues.map((issues2) => new ZodError(issues2)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + } + get options() { + return this._def.options; + } +}; +ZodUnion.create = (types, params) => { + return new ZodUnion({ + options: types, + typeName: ZodFirstPartyTypeKind.ZodUnion, + ...processCreateParams(params) + }); +}; +var getDiscriminator = (type) => { + if (type instanceof ZodLazy) { + return getDiscriminator(type.schema); + } else if (type instanceof ZodEffects) { + return getDiscriminator(type.innerType()); + } else if (type instanceof ZodLiteral) { + return [type.value]; + } else if (type instanceof ZodEnum) { + return type.options; + } else if (type instanceof ZodNativeEnum) { + return util2.objectValues(type.enum); + } else if (type instanceof ZodDefault) { + return getDiscriminator(type._def.innerType); + } else if (type instanceof ZodUndefined) { + return [void 0]; + } else if (type instanceof ZodNull) { + return [null]; + } else if (type instanceof ZodOptional) { + return [void 0, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodNullable) { + return [null, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodBranded) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodReadonly) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodCatch) { + return getDiscriminator(type._def.innerType); + } else { + return []; + } +}; +var ZodDiscriminatedUnion = class _ZodDiscriminatedUnion extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const discriminator = this.discriminator; + const discriminatorValue = ctx.data[discriminator]; + const option = this.optionsMap.get(discriminatorValue); + if (!option) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union_discriminator, + options: Array.from(this.optionsMap.keys()), + path: [discriminator] + }); + return INVALID; + } + if (ctx.common.async) { + return option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } else { + return option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } + } + get discriminator() { + return this._def.discriminator; + } + get options() { + return this._def.options; + } + get optionsMap() { + return this._def.optionsMap; + } + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create(discriminator, options, params) { + const optionsMap = /* @__PURE__ */ new Map(); + for (const type of options) { + const discriminatorValues = getDiscriminator(type.shape[discriminator]); + if (!discriminatorValues.length) { + throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`); + } + for (const value of discriminatorValues) { + if (optionsMap.has(value)) { + throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`); + } + optionsMap.set(value, type); + } + } + return new _ZodDiscriminatedUnion({ + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + discriminator, + options, + optionsMap, + ...processCreateParams(params) + }); + } +}; +function mergeValues(a, b) { + const aType = getParsedType(a); + const bType = getParsedType(b); + if (a === b) { + return { valid: true, data: a }; + } else if (aType === ZodParsedType.object && bType === ZodParsedType.object) { + const bKeys = util2.objectKeys(b); + const sharedKeys = util2.objectKeys(a).filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a, ...b }; + for (const key of sharedKeys) { + const sharedValue = mergeValues(a[key], b[key]); + if (!sharedValue.valid) { + return { valid: false }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } else if (aType === ZodParsedType.array && bType === ZodParsedType.array) { + if (a.length !== b.length) { + return { valid: false }; + } + const newArray = []; + for (let index = 0; index < a.length; index++) { + const itemA = a[index]; + const itemB = b[index]; + const sharedValue = mergeValues(itemA, itemB); + if (!sharedValue.valid) { + return { valid: false }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } else if (aType === ZodParsedType.date && bType === ZodParsedType.date && +a === +b) { + return { valid: true, data: a }; + } else { + return { valid: false }; + } +} +var ZodIntersection = class extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const handleParsed = (parsedLeft, parsedRight) => { + if (isAborted(parsedLeft) || isAborted(parsedRight)) { + return INVALID; + } + const merged = mergeValues(parsedLeft.value, parsedRight.value); + if (!merged.valid) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_intersection_types + }); + return INVALID; + } + if (isDirty(parsedLeft) || isDirty(parsedRight)) { + status.dirty(); + } + return { status: status.value, value: merged.data }; + }; + if (ctx.common.async) { + return Promise.all([ + this._def.left._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), + this._def.right._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }) + ]).then(([left, right]) => handleParsed(left, right)); + } else { + return handleParsed(this._def.left._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), this._def.right._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + })); + } + } +}; +ZodIntersection.create = (left, right, params) => { + return new ZodIntersection({ + left, + right, + typeName: ZodFirstPartyTypeKind.ZodIntersection, + ...processCreateParams(params) + }); +}; +var ZodTuple = class _ZodTuple extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (ctx.data.length < this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + return INVALID; + } + const rest = this._def.rest; + if (!rest && ctx.data.length > this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + status.dirty(); + } + const items = [...ctx.data].map((item, itemIndex) => { + const schema = this._def.items[itemIndex] || this._def.rest; + if (!schema) + return null; + return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex)); + }).filter((x2) => !!x2); + if (ctx.common.async) { + return Promise.all(items).then((results) => { + return ParseStatus.mergeArray(status, results); + }); + } else { + return ParseStatus.mergeArray(status, items); + } + } + get items() { + return this._def.items; + } + rest(rest) { + return new _ZodTuple({ + ...this._def, + rest + }); + } +}; +ZodTuple.create = (schemas, params) => { + if (!Array.isArray(schemas)) { + throw new Error("You must pass an array of schemas to z.tuple([ ... ])"); + } + return new ZodTuple({ + items: schemas, + typeName: ZodFirstPartyTypeKind.ZodTuple, + rest: null, + ...processCreateParams(params) + }); +}; +var ZodRecord = class _ZodRecord extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const pairs = []; + const keyType = this._def.keyType; + const valueType = this._def.valueType; + for (const key in ctx.data) { + pairs.push({ + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)), + value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (ctx.common.async) { + return ParseStatus.mergeObjectAsync(status, pairs); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get element() { + return this._def.valueType; + } + static create(first, second, third) { + if (second instanceof ZodType) { + return new _ZodRecord({ + keyType: first, + valueType: second, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(third) + }); + } + return new _ZodRecord({ + keyType: ZodString.create(), + valueType: first, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(second) + }); + } +}; +var ZodMap = class extends ZodType { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.map) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.map, + received: ctx.parsedType + }); + return INVALID; + } + const keyType = this._def.keyType; + const valueType = this._def.valueType; + const pairs = [...ctx.data.entries()].map(([key, value], index) => { + return { + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])), + value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])) + }; + }); + if (ctx.common.async) { + const finalMap = /* @__PURE__ */ new Map(); + return Promise.resolve().then(async () => { + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + }); + } else { + const finalMap = /* @__PURE__ */ new Map(); + for (const pair of pairs) { + const key = pair.key; + const value = pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + } + } +}; +ZodMap.create = (keyType, valueType, params) => { + return new ZodMap({ + valueType, + keyType, + typeName: ZodFirstPartyTypeKind.ZodMap, + ...processCreateParams(params) + }); +}; +var ZodSet = class _ZodSet extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.set) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.set, + received: ctx.parsedType + }); + return INVALID; + } + const def = this._def; + if (def.minSize !== null) { + if (ctx.data.size < def.minSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.minSize.message + }); + status.dirty(); + } + } + if (def.maxSize !== null) { + if (ctx.data.size > def.maxSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.maxSize.message + }); + status.dirty(); + } + } + const valueType = this._def.valueType; + function finalizeSet(elements2) { + const parsedSet = /* @__PURE__ */ new Set(); + for (const element of elements2) { + if (element.status === "aborted") + return INVALID; + if (element.status === "dirty") + status.dirty(); + parsedSet.add(element.value); + } + return { status: status.value, value: parsedSet }; + } + const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i))); + if (ctx.common.async) { + return Promise.all(elements).then((elements2) => finalizeSet(elements2)); + } else { + return finalizeSet(elements); + } + } + min(minSize, message) { + return new _ZodSet({ + ...this._def, + minSize: { value: minSize, message: errorUtil.toString(message) } + }); + } + max(maxSize, message) { + return new _ZodSet({ + ...this._def, + maxSize: { value: maxSize, message: errorUtil.toString(message) } + }); + } + size(size, message) { + return this.min(size, message).max(size, message); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodSet.create = (valueType, params) => { + return new ZodSet({ + valueType, + minSize: null, + maxSize: null, + typeName: ZodFirstPartyTypeKind.ZodSet, + ...processCreateParams(params) + }); +}; +var ZodFunction = class _ZodFunction extends ZodType { + constructor() { + super(...arguments); + this.validate = this.implement; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.function) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.function, + received: ctx.parsedType + }); + return INVALID; + } + function makeArgsIssue(args, error) { + return makeIssue({ + data: args, + path: ctx.path, + errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default].filter((x2) => !!x2), + issueData: { + code: ZodIssueCode.invalid_arguments, + argumentsError: error + } + }); + } + function makeReturnsIssue(returns, error) { + return makeIssue({ + data: returns, + path: ctx.path, + errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default].filter((x2) => !!x2), + issueData: { + code: ZodIssueCode.invalid_return_type, + returnTypeError: error + } + }); + } + const params = { errorMap: ctx.common.contextualErrorMap }; + const fn = ctx.data; + if (this._def.returns instanceof ZodPromise) { + const me2 = this; + return OK(async function(...args) { + const error = new ZodError([]); + const parsedArgs = await me2._def.args.parseAsync(args, params).catch((e) => { + error.addIssue(makeArgsIssue(args, e)); + throw error; + }); + const result = await Reflect.apply(fn, this, parsedArgs); + const parsedReturns = await me2._def.returns._def.type.parseAsync(result, params).catch((e) => { + error.addIssue(makeReturnsIssue(result, e)); + throw error; + }); + return parsedReturns; + }); + } else { + const me2 = this; + return OK(function(...args) { + const parsedArgs = me2._def.args.safeParse(args, params); + if (!parsedArgs.success) { + throw new ZodError([makeArgsIssue(args, parsedArgs.error)]); + } + const result = Reflect.apply(fn, this, parsedArgs.data); + const parsedReturns = me2._def.returns.safeParse(result, params); + if (!parsedReturns.success) { + throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]); + } + return parsedReturns.data; + }); + } + } + parameters() { + return this._def.args; + } + returnType() { + return this._def.returns; + } + args(...items) { + return new _ZodFunction({ + ...this._def, + args: ZodTuple.create(items).rest(ZodUnknown.create()) + }); + } + returns(returnType) { + return new _ZodFunction({ + ...this._def, + returns: returnType + }); + } + implement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + strictImplement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + static create(args, returns, params) { + return new _ZodFunction({ + args: args ? args : ZodTuple.create([]).rest(ZodUnknown.create()), + returns: returns || ZodUnknown.create(), + typeName: ZodFirstPartyTypeKind.ZodFunction, + ...processCreateParams(params) + }); + } +}; +var ZodLazy = class extends ZodType { + get schema() { + return this._def.getter(); + } + _parse(input) { + const { ctx } = this._processInputParams(input); + const lazySchema = this._def.getter(); + return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx }); + } +}; +ZodLazy.create = (getter, params) => { + return new ZodLazy({ + getter, + typeName: ZodFirstPartyTypeKind.ZodLazy, + ...processCreateParams(params) + }); +}; +var ZodLiteral = class extends ZodType { + _parse(input) { + if (input.data !== this._def.value) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_literal, + expected: this._def.value + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } + get value() { + return this._def.value; + } +}; +ZodLiteral.create = (value, params) => { + return new ZodLiteral({ + value, + typeName: ZodFirstPartyTypeKind.ZodLiteral, + ...processCreateParams(params) + }); +}; +function createZodEnum(values, params) { + return new ZodEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodEnum, + ...processCreateParams(params) + }); +} +var ZodEnum = class _ZodEnum extends ZodType { + _parse(input) { + if (typeof input.data !== "string") { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + expected: util2.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!this._cache) { + this._cache = new Set(this._def.values); + } + if (!this._cache.has(input.data)) { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get options() { + return this._def.values; + } + get enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Values() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + extract(values, newDef = this._def) { + return _ZodEnum.create(values, { + ...this._def, + ...newDef + }); + } + exclude(values, newDef = this._def) { + return _ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), { + ...this._def, + ...newDef + }); + } +}; +ZodEnum.create = createZodEnum; +var ZodNativeEnum = class extends ZodType { + _parse(input) { + const nativeEnumValues = util2.getValidEnumValues(this._def.values); + const ctx = this._getOrReturnCtx(input); + if (ctx.parsedType !== ZodParsedType.string && ctx.parsedType !== ZodParsedType.number) { + const expectedValues = util2.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + expected: util2.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!this._cache) { + this._cache = new Set(util2.getValidEnumValues(this._def.values)); + } + if (!this._cache.has(input.data)) { + const expectedValues = util2.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get enum() { + return this._def.values; + } +}; +ZodNativeEnum.create = (values, params) => { + return new ZodNativeEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodNativeEnum, + ...processCreateParams(params) + }); +}; +var ZodPromise = class extends ZodType { + unwrap() { + return this._def.type; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.promise && ctx.common.async === false) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.promise, + received: ctx.parsedType + }); + return INVALID; + } + const promisified = ctx.parsedType === ZodParsedType.promise ? ctx.data : Promise.resolve(ctx.data); + return OK(promisified.then((data) => { + return this._def.type.parseAsync(data, { + path: ctx.path, + errorMap: ctx.common.contextualErrorMap + }); + })); + } +}; +ZodPromise.create = (schema, params) => { + return new ZodPromise({ + type: schema, + typeName: ZodFirstPartyTypeKind.ZodPromise, + ...processCreateParams(params) + }); +}; +var ZodEffects = class extends ZodType { + innerType() { + return this._def.schema; + } + sourceType() { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const effect = this._def.effect || null; + const checkCtx = { + addIssue: (arg) => { + addIssueToContext(ctx, arg); + if (arg.fatal) { + status.abort(); + } else { + status.dirty(); + } + }, + get path() { + return ctx.path; + } + }; + checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx); + if (effect.type === "preprocess") { + const processed = effect.transform(ctx.data, checkCtx); + if (ctx.common.async) { + return Promise.resolve(processed).then(async (processed2) => { + if (status.value === "aborted") + return INVALID; + const result = await this._def.schema._parseAsync({ + data: processed2, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + }); + } else { + if (status.value === "aborted") + return INVALID; + const result = this._def.schema._parseSync({ + data: processed, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + } + } + if (effect.type === "refinement") { + const executeRefinement = (acc) => { + const result = effect.refinement(acc, checkCtx); + if (ctx.common.async) { + return Promise.resolve(result); + } + if (result instanceof Promise) { + throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead."); + } + return acc; + }; + if (ctx.common.async === false) { + const inner = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + executeRefinement(inner.value); + return { status: status.value, value: inner.value }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => { + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + return executeRefinement(inner.value).then(() => { + return { status: status.value, value: inner.value }; + }); + }); + } + } + if (effect.type === "transform") { + if (ctx.common.async === false) { + const base = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (!isValid(base)) + return INVALID; + const result = effect.transform(base.value, checkCtx); + if (result instanceof Promise) { + throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`); + } + return { status: status.value, value: result }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => { + if (!isValid(base)) + return INVALID; + return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ + status: status.value, + value: result + })); + }); + } + } + util2.assertNever(effect); + } +}; +ZodEffects.create = (schema, effect, params) => { + return new ZodEffects({ + schema, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect, + ...processCreateParams(params) + }); +}; +ZodEffects.createWithPreprocess = (preprocess, schema, params) => { + return new ZodEffects({ + schema, + effect: { type: "preprocess", transform: preprocess }, + typeName: ZodFirstPartyTypeKind.ZodEffects, + ...processCreateParams(params) + }); +}; +var ZodOptional = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.undefined) { + return OK(void 0); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodOptional.create = (type, params) => { + return new ZodOptional({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodOptional, + ...processCreateParams(params) + }); +}; +var ZodNullable = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType === ZodParsedType.null) { + return OK(null); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodNullable.create = (type, params) => { + return new ZodNullable({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodNullable, + ...processCreateParams(params) + }); +}; +var ZodDefault = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + let data = ctx.data; + if (ctx.parsedType === ZodParsedType.undefined) { + data = this._def.defaultValue(); + } + return this._def.innerType._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + removeDefault() { + return this._def.innerType; + } +}; +ZodDefault.create = (type, params) => { + return new ZodDefault({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodDefault, + defaultValue: typeof params.default === "function" ? params.default : () => params.default, + ...processCreateParams(params) + }); +}; +var ZodCatch = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const newCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + } + }; + const result = this._def.innerType._parse({ + data: newCtx.data, + path: newCtx.path, + parent: { + ...newCtx + } + }); + if (isAsync(result)) { + return result.then((result2) => { + return { + status: "valid", + value: result2.status === "valid" ? result2.value : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + }); + } else { + return { + status: "valid", + value: result.status === "valid" ? result.value : this._def.catchValue({ + get error() { + return new ZodError(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + } + } + removeCatch() { + return this._def.innerType; + } +}; +ZodCatch.create = (type, params) => { + return new ZodCatch({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodCatch, + catchValue: typeof params.catch === "function" ? params.catch : () => params.catch, + ...processCreateParams(params) + }); +}; +var ZodNaN = class extends ZodType { + _parse(input) { + const parsedType = this._getType(input); + if (parsedType !== ZodParsedType.nan) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.nan, + received: ctx.parsedType + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } +}; +ZodNaN.create = (params) => { + return new ZodNaN({ + typeName: ZodFirstPartyTypeKind.ZodNaN, + ...processCreateParams(params) + }); +}; +var BRAND = /* @__PURE__ */ Symbol("zod_brand"); +var ZodBranded = class extends ZodType { + _parse(input) { + const { ctx } = this._processInputParams(input); + const data = ctx.data; + return this._def.type._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + unwrap() { + return this._def.type; + } +}; +var ZodPipeline = class _ZodPipeline extends ZodType { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.common.async) { + const handleAsync = async () => { + const inResult = await this._def.in._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return DIRTY(inResult.value); + } else { + return this._def.out._parseAsync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + }; + return handleAsync(); + } else { + const inResult = this._def.in._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return { + status: "dirty", + value: inResult.value + }; + } else { + return this._def.out._parseSync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + } + } + static create(a, b) { + return new _ZodPipeline({ + in: a, + out: b, + typeName: ZodFirstPartyTypeKind.ZodPipeline + }); + } +}; +var ZodReadonly = class extends ZodType { + _parse(input) { + const result = this._def.innerType._parse(input); + const freeze = (data) => { + if (isValid(data)) { + data.value = Object.freeze(data.value); + } + return data; + }; + return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result); + } + unwrap() { + return this._def.innerType; + } +}; +ZodReadonly.create = (type, params) => { + return new ZodReadonly({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodReadonly, + ...processCreateParams(params) + }); +}; +function cleanParams(params, data) { + const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params; + const p2 = typeof p === "string" ? { message: p } : p; + return p2; +} +function custom(check, _params = {}, fatal) { + if (check) + return ZodAny.create().superRefine((data, ctx) => { + const r = check(data); + if (r instanceof Promise) { + return r.then((r2) => { + if (!r2) { + const params = cleanParams(_params, data); + const _fatal = params.fatal ?? fatal ?? true; + ctx.addIssue({ code: "custom", ...params, fatal: _fatal }); + } + }); + } + if (!r) { + const params = cleanParams(_params, data); + const _fatal = params.fatal ?? fatal ?? true; + ctx.addIssue({ code: "custom", ...params, fatal: _fatal }); + } + return; + }); + return ZodAny.create(); +} +var late = { + object: ZodObject.lazycreate +}; +var ZodFirstPartyTypeKind; +(function(ZodFirstPartyTypeKind2) { + ZodFirstPartyTypeKind2["ZodString"] = "ZodString"; + ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber"; + ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN"; + ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt"; + ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean"; + ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate"; + ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol"; + ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined"; + ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull"; + ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny"; + ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown"; + ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever"; + ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid"; + ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray"; + ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject"; + ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion"; + ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion"; + ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection"; + ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple"; + ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord"; + ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap"; + ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet"; + ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction"; + ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy"; + ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral"; + ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum"; + ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects"; + ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum"; + ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional"; + ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable"; + ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault"; + ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch"; + ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise"; + ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded"; + ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline"; + ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly"; +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); +var instanceOfType = (cls, params = { + message: `Input not instance of ${cls.name}` +}) => custom((data) => data instanceof cls, params); +var stringType = ZodString.create; +var numberType = ZodNumber.create; +var nanType = ZodNaN.create; +var bigIntType = ZodBigInt.create; +var booleanType = ZodBoolean.create; +var dateType = ZodDate.create; +var symbolType = ZodSymbol.create; +var undefinedType = ZodUndefined.create; +var nullType = ZodNull.create; +var anyType = ZodAny.create; +var unknownType = ZodUnknown.create; +var neverType = ZodNever.create; +var voidType = ZodVoid.create; +var arrayType = ZodArray.create; +var objectType = ZodObject.create; +var strictObjectType = ZodObject.strictCreate; +var unionType = ZodUnion.create; +var discriminatedUnionType = ZodDiscriminatedUnion.create; +var intersectionType = ZodIntersection.create; +var tupleType = ZodTuple.create; +var recordType = ZodRecord.create; +var mapType = ZodMap.create; +var setType = ZodSet.create; +var functionType = ZodFunction.create; +var lazyType = ZodLazy.create; +var literalType = ZodLiteral.create; +var enumType = ZodEnum.create; +var nativeEnumType = ZodNativeEnum.create; +var promiseType = ZodPromise.create; +var effectsType = ZodEffects.create; +var optionalType = ZodOptional.create; +var nullableType = ZodNullable.create; +var preprocessType = ZodEffects.createWithPreprocess; +var pipelineType = ZodPipeline.create; +var ostring = () => stringType().optional(); +var onumber = () => numberType().optional(); +var oboolean = () => booleanType().optional(); +var coerce = { + string: ((arg) => ZodString.create({ ...arg, coerce: true })), + number: ((arg) => ZodNumber.create({ ...arg, coerce: true })), + boolean: ((arg) => ZodBoolean.create({ + ...arg, + coerce: true + })), + bigint: ((arg) => ZodBigInt.create({ ...arg, coerce: true })), + date: ((arg) => ZodDate.create({ ...arg, coerce: true })) +}; +var NEVER = INVALID; + +// ../plugins/dist/utils.js +var isHookTree = (value) => { + if (value == null || typeof value !== "object" || Array.isArray(value)) { + return false; + } + for (const child of Object.values(value)) { + if (child === void 0) { + continue; + } + if (typeof child === "function") { + continue; + } + if (child == null || typeof child !== "object" || Array.isArray(child) || !isHookTree(child)) { + return false; + } + } + return true; +}; + +// ../plugins/dist/plugin.js +var isHarnessPlugin = (value) => { + if (value == null || typeof value !== "object" || Array.isArray(value)) { + return false; + } + const candidate = value; + if (typeof candidate.name !== "string" || candidate.name.length === 0) { + return false; + } + if (candidate.createState != null && typeof candidate.createState !== "function") { + return false; + } + if (candidate.hooks != null && !isHookTree(candidate.hooks)) { + return false; + } + return true; +}; + +// ../plugins/dist/manager.js +var pluginsLogger = logger.child("plugins"); + +// ../config/dist/types.js +var DEFAULT_METRO_PORT = 8081; +var configLogger = logger.child("config"); +var RunnerSchema = external_exports.object({ + name: external_exports.string().min(1, "Runner name is required").regex(/^[a-zA-Z0-9._-]+$/, "Runner name can only contain alphanumeric characters, dots, underscores, and hyphens"), + config: external_exports.record(external_exports.any()), + runner: external_exports.string(), + cli: external_exports.string().optional(), + platformId: external_exports.string() +}); +var PluginSchema = external_exports.custom((value) => isHarnessPlugin(value), "Invalid Harness plugin"); +var ConfigSchema = external_exports.object({ + entryPoint: external_exports.string().min(1, "Entry point is required"), + appRegistryComponentName: external_exports.string().min(1, "App registry component name is required"), + runners: external_exports.array(RunnerSchema).min(1, "At least one runner is required"), + plugins: external_exports.array(PluginSchema).optional().default([]), + defaultRunner: external_exports.string().optional(), + host: external_exports.string().min(1, "Host is required").optional(), + metroPort: external_exports.number().int("Metro port must be an integer").min(1, "Metro port must be at least 1").max(65535, "Metro port must be at most 65535").optional().default(DEFAULT_METRO_PORT), + webSocketPort: external_exports.number().optional().describe("Deprecated. Bridge traffic now uses metroPort and this value is ignored."), + bridgeTimeout: external_exports.number().min(1e3, "Bridge timeout must be at least 1 second").default(6e4), + testTimeout: external_exports.number().min(1e3, "Test timeout must be at least 1 second").default(5e3), + platformReadyTimeout: external_exports.number().min(1e3, "Platform ready timeout must be at least 1 second").default(3e5), + bundleStartTimeout: external_exports.number().min(1e3, "Bundle start timeout must be at least 1 second").default(6e4), + maxAppRestarts: external_exports.number().min(0, "Max app restarts must be at least 0").default(2), + eagerPrewarm: external_exports.boolean().optional().default(true).describe("Start building the Metro bundle while the platform (emulator, simulator, or browser) is still booting, so the first bundle is ready sooner. Disable to defer the first bundle build until app startup."), + resetEnvironmentBetweenTestFiles: external_exports.union([external_exports.boolean(), external_exports.enum(["process", "runtime"])]).optional().default(true).describe("Controls how the environment is reset between test files. `true` (default) and `'process'` kill and cold-restart the app process. `'runtime'` reloads the JS runtime in place (DevSettings.reload() / window.location.reload()), which is cheaper but escalates to a process restart if the reload fails or the app does not reconnect in time. `false` disables resetting the environment between test files entirely."), + skipAlreadyIncludedModules: external_exports.boolean().optional().describe('Skip re-sending modules already served in the main app bundle when Metro serves a per-test-file bundle. Defaults to true; set to false as an escape hatch if it causes issues. Left undefined (rather than defaulted here) so `resolveSkipAlreadyIncludedModules` can tell "not set" apart from "explicitly set" when reconciling with the deprecated `unstable__skipAlreadyIncludedModules` alias.'), + // Deprecated alias for `skipAlreadyIncludedModules` -- see + // `resolveSkipAlreadyIncludedModules`. Left without a `.default()` for the + // same reason: a default value would be indistinguishable from the user + // never having set it, which would break alias-vs-explicit-flag precedence. + unstable__skipAlreadyIncludedModules: external_exports.boolean().optional().describe("Deprecated. Use `skipAlreadyIncludedModules` instead."), + cache: external_exports.object({ + metro: external_exports.boolean().optional().describe('Enable persistent Metro transform and file-map caching under the .harness cache directory. Defaults to true; set to false to always start with a cold Metro cache. Left undefined (rather than defaulted here) so `resolveMetroCacheEnabled` can tell "not set" apart from "explicitly set" when reconciling with the deprecated `unstable__enableMetroCache` alias.'), + version: external_exports.string().optional().describe("User-controlled salt folded into the Metro cacheVersion. Bump it to force-invalidate previously cached transforms.") + }).optional(), + // Deprecated alias for `cache.metro` -- see `resolveMetroCacheEnabled`. + // Left without a `.default()` for the same reason as + // `unstable__skipAlreadyIncludedModules`: a default value would be + // indistinguishable from the user never having set it, which would break + // alias-vs-explicit-flag precedence. + unstable__enableMetroCache: external_exports.boolean().optional().describe("Deprecated. Use `cache.metro` instead."), + permissions: external_exports.boolean().optional().default(false).describe("Enable platform-specific permission prompt automation. When false, Harness does not start permission-handling helpers such as the iOS XCTest agent."), + detectNativeCrashes: external_exports.boolean().optional().default(true), + crashDetectionInterval: external_exports.number().min(100, "Crash detection interval must be at least 100ms").default(500), + disableViewFlattening: external_exports.boolean().optional().default(false).describe("Disable view flattening in React Native. This will set collapsable={true} for all View components to ensure they are not flattened by the native layout engine."), + coverage: external_exports.object({ + root: external_exports.string().optional().describe(`Root directory for coverage instrumentation in monorepo setups. Specifies the directory from which coverage data should be collected. Use ".." for create-react-native-library projects where tests run from example/ but source files are in parent directory. Passed to babel-plugin-istanbul's cwd option.`), + native: external_exports.object({ + ios: external_exports.object({ + pods: external_exports.array(external_exports.string()).min(1, "At least one pod name is required").describe("Pod names to instrument for native code coverage. Coverage flags are injected at pod install time via a CocoaPods hook. After tests, profraw data is collected and converted to lcov format.") + }).optional() + }).optional().describe("Native code coverage configuration.") + }).optional(), + forwardClientLogs: external_exports.boolean().optional().default(false).describe("Enable forwarding of console.log, console.warn, console.error, and other console method calls from the React Native app during the active test run. When enabled, app console output is attached to the active test result's console output."), + diagnostics: external_exports.boolean().optional().default(false).describe("Enable diagnostics tracing for the harness session. Records spans for session setup, Metro bundling, bridge/client events, and per-file test runs, then writes a Chrome Trace Event JSON file and prints a summary after each run. Can also be enabled via the RN_HARNESS_DIAGNOSTICS environment variable."), + // Deprecated property - used for migration detection + include: external_exports.array(external_exports.string()).optional() +}).refine((config) => { + if (config.defaultRunner) { + return config.runners.some((runner) => runner.name === config.defaultRunner); + } + return true; +}, { + message: "Default runner must match one of the configured runner names", + path: ["defaultRunner"] +}); + +// ../config/dist/errors.js +var ConfigValidationError = class extends HarnessError { + filePath; + validationErrors; + constructor(filePath, validationErrors) { + const lines = [ + `Configuration validation failed in ${filePath}`, + "", + "The following issues were found:", + "" + ]; + validationErrors.forEach((error, index) => { + lines.push(` ${index + 1}. ${error}`); + }); + lines.push("", "Please fix these issues and try again.", "For more information, visit: https://react-native-harness.dev/docs/configuration"); + super(lines.join("\n")); + this.filePath = filePath; + this.validationErrors = validationErrors; + this.name = "ConfigValidationError"; + } +}; +var ConfigNotFoundError = class extends HarnessError { + searchPath; + constructor(searchPath) { + const lines = [ + "Configuration file not found", + "", + `Searched for configuration files in: ${searchPath}`, + "and all parent directories.", + "", + "React Native Harness looks for one of these files:", + " \u2022 rn-harness.config.js", + " \u2022 rn-harness.config.mjs", + " \u2022 rn-harness.config.cjs", + " \u2022 rn-harness.config.json", + "", + "For more information, visit: https://www.react-native-harness.dev/docs/getting-started/configuration" + ]; + super(lines.join("\n")); + this.searchPath = searchPath; + this.name = "ConfigNotFoundError"; + } +}; +var ConfigLoadError = class extends HarnessError { + filePath; + cause; + constructor(filePath, cause) { + const lines = [ + "Failed to load configuration file", + "", + `File: ${filePath}`, + "" + ]; + if (cause) { + lines.push("Error details:"); + lines.push(` ${cause.message}`); + lines.push(""); + } + lines.push("This could be due to:", " \u2022 Syntax errors in your configuration file", " \u2022 Missing dependencies or modules", " \u2022 Invalid file format or encoding", " \u2022 File permissions issues", "", "Troubleshooting steps:", " 1. Check the file syntax and format", " 2. Ensure all required dependencies are installed", " 3. Verify file permissions", " 4. Try creating a new configuration file", "", "For more help, visit: https://www.react-native-harness.dev/docs/getting-started/configuration"); + super(lines.join("\n")); + this.filePath = filePath; + this.name = "ConfigLoadError"; + this.cause = cause; + } +}; + +// ../config/dist/reader.js +var import_node_path10 = __toESM(require("path"), 1); +var import_node_fs10 = __toESM(require("fs"), 1); +var import_node_module2 = require("module"); +var import_meta = {}; +var extensions = [".js", ".mjs", ".cjs", ".json"]; +var importUp = async (dir, name) => { + const filePath = import_node_path10.default.join(dir, name); + for (const ext of extensions) { + const filePathWithExt = `${filePath}${ext}`; + if (import_node_fs10.default.existsSync(filePathWithExt)) { + let rawConfig; + try { + if (ext === ".mjs") { + rawConfig = await import(filePathWithExt).then((module2) => module2.default); + } else { + const require2 = (0, import_node_module2.createRequire)(import_meta.url); + rawConfig = require2(filePathWithExt); + } + } catch (error) { + throw new ConfigLoadError(filePathWithExt, error instanceof Error ? error : void 0); + } + try { + const config = ConfigSchema.parse(rawConfig); + return { config, filePathWithExt, configDir: dir }; + } catch (error) { + if (error instanceof ZodError) { + const validationErrors = error.errors.map((err) => { + const path11 = err.path.length > 0 ? ` at "${err.path.join(".")}"` : ""; + return `${err.message}${path11}`; + }); + throw new ConfigValidationError(filePathWithExt, validationErrors); + } + throw error; + } + } + } + const parentDir = import_node_path10.default.dirname(dir); + if (parentDir === dir) { + throw new ConfigNotFoundError(dir); + } + return importUp(parentDir, name); +}; +var getConfig = async (dir) => { + const { config, configDir } = await importUp(dir, "rn-harness.config"); + return { + config, + projectRoot: configDir + }; +}; + +// src/shared/format-bytes.ts +var formatMegabytes = (bytes) => `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + +// src/shared/snapshot-metro.ts +var run = async () => { + try { + const projectRootInput = process.env.INPUT_PROJECTROOT; + const projectRoot = projectRootInput ? import_node_path11.default.resolve(projectRootInput) : process.cwd(); + console.info(`Snapshotting Metro cache for: ${projectRoot}`); + const { projectRoot: resolvedProjectRoot } = await getConfig(projectRoot); + const githubOutput = process.env.GITHUB_OUTPUT; + if (!githubOutput) { + throw new Error("GITHUB_OUTPUT environment variable is not set"); + } + const cache = createHarnessCache({ projectRoot: resolvedProjectRoot }); + const snapshotAfterRestore = cache.snapshot(); + const restoredKey = process.env.METRO_RESTORED_KEY; + if (restoredKey) { + const metroEntries = snapshotAfterRestore.metro ?? []; + const totalBytes = metroEntries.reduce( + (sum, entry) => sum + entry.sizeBytes, + 0 + ); + console.info( + `Metro cache: restored from key "${restoredKey}" (${metroEntries.length} files, ${formatMegabytes(totalBytes)}).` + ); + } else { + console.info( + "Metro cache: no existing cache entry matched \u2014 starting cold." + ); + } + const output = `metroSnapshot=${JSON.stringify(snapshotAfterRestore)} +`; + import_node_fs11.default.appendFileSync(githubOutput, output); + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } else { + console.error("Failed to snapshot the Metro cache"); + } + process.exit(1); + } +}; +run(); diff --git a/apps/playground/rn-harness.config.mjs b/apps/playground/rn-harness.config.mjs index 1053d2f9..bb4badfe 100644 --- a/apps/playground/rn-harness.config.mjs +++ b/apps/playground/rn-harness.config.mjs @@ -116,6 +116,7 @@ export default { defaultRunner: 'android', platformReadyTimeout: 300000, bridgeTimeout: 120000, + testTimeout: 10000, permissions: true, detectNativeCrashes: true, diff --git a/packages/cache/src/__tests__/domains/metro.test.ts b/packages/cache/src/__tests__/domains/metro.test.ts new file mode 100644 index 00000000..96f3f308 --- /dev/null +++ b/packages/cache/src/__tests__/domains/metro.test.ts @@ -0,0 +1,298 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { computeMetroStaticInputs } from '../../domains/metro.js'; + +describe('computeMetroStaticInputs', () => { + let repoRoot: string; + let projectRoot: string; + + beforeEach(() => { + repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-cache-metro-')); + projectRoot = path.join(repoRoot, 'apps', 'mobile'); + fs.mkdirSync(projectRoot, { recursive: true }); + }); + + afterEach(() => { + fs.rmSync(repoRoot, { recursive: true, force: true }); + }); + + it('includes a lockfile at an ancestor directory between projectRoot and repoRoot', () => { + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-a'); + + const before = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-b'); + + const after = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(before.lockfileHash).not.toBe(after.lockfileHash); + }); + + it('ignores a config file in a sibling directory not on the ancestor chain', () => { + const siblingDir = path.join(repoRoot, 'apps', 'other'); + fs.mkdirSync(siblingDir, { recursive: true }); + fs.writeFileSync(path.join(siblingDir, 'metro.config.js'), 'config-a'); + + const before = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(siblingDir, 'metro.config.js'), 'config-b'); + + const after = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(before.lockfileHash).toBe(after.lockfileHash); + }); + + it('excludes a vendored lockfile under node_modules (regression pin)', () => { + // Pin for the old bug where a downward `hashFiles('**/pnpm-lock.yaml', + // ...)`-style glob would match a dependency's own lockfile under + // node_modules. Trivially true now that node_modules is never on the + // upward ancestor chain, but kept as an explicit regression test. + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-a'); + const vendoredDir = path.join(projectRoot, 'node_modules', 'some-pkg'); + fs.mkdirSync(vendoredDir, { recursive: true }); + fs.writeFileSync( + path.join(vendoredDir, 'package-lock.json'), + 'vendored-lockfile' + ); + + const withVendored = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.rmSync(path.join(projectRoot, 'node_modules'), { + recursive: true, + force: true, + }); + + const withoutVendored = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(withVendored.lockfileHash).toBe(withoutVendored.lockfileHash); + }); + + it('is deterministic across independent calls given the same fixture tree', () => { + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-a'); + fs.writeFileSync(path.join(projectRoot, 'metro.config.js'), 'config-a'); + + const first = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + const second = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(first.lockfileHash).toBe(second.lockfileHash); + }); + + it('changes the hash when a matched file changes content', () => { + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-a'); + + const before = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-b'); + + const after = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(before.lockfileHash).not.toBe(after.lockfileHash); + }); + + it('does not change the hash when an unrelated file changes content', () => { + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-a'); + fs.writeFileSync(path.join(projectRoot, 'notes.txt'), 'v1'); + + const before = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(projectRoot, 'notes.txt'), 'v2'); + + const after = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(before.lockfileHash).toBe(after.lockfileHash); + }); + + it.each([ + ['metro.config.mts', 'metro-mts-a', 'metro-mts-b'], + ['metro.config.cts', 'metro-cts-a', 'metro-cts-b'], + ['metro.config.json', 'metro-json-a', 'metro-json-b'], + ['babel.config.cts', 'babel-cts-a', 'babel-cts-b'], + ])('picks up %s as a candidate at projectRoot', (filename, before, after) => { + fs.writeFileSync(path.join(projectRoot, filename), before); + + const beforeInputs = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(projectRoot, filename), after); + + const afterInputs = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(beforeInputs.lockfileHash).not.toBe(afterInputs.lockfileHash); + }); + + it('picks up .config/metro.js as a candidate at projectRoot', () => { + const configDir = path.join(projectRoot, '.config'); + fs.mkdirSync(configDir, { recursive: true }); + fs.writeFileSync(path.join(configDir, 'metro.js'), 'config-a'); + + const before = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(configDir, 'metro.js'), 'config-b'); + + const after = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(before.lockfileHash).not.toBe(after.lockfileHash); + }); + + it('no longer treats babel.config.ts as a candidate', () => { + const before = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(projectRoot, 'babel.config.ts'), 'babel-ts-a'); + + const afterCreate = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(projectRoot, 'babel.config.ts'), 'babel-ts-b'); + + const afterChange = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(afterCreate.lockfileHash).toBe(before.lockfileHash); + expect(afterChange.lockfileHash).toBe(before.lockfileHash); + }); + + it('passes bundlerMetroVersion and salt through into the returned object', () => { + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-a'); + + const withoutSalt = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '2.3.4', + }); + expect(withoutSalt.bundlerMetroVersion).toBe('2.3.4'); + expect(withoutSalt.salt).toBeUndefined(); + + const withSalt = computeMetroStaticInputs({ + projectRoot, + repoRoot, + bundlerMetroVersion: '2.3.4', + salt: 'v2', + }); + expect(withSalt.salt).toBe('v2'); + }); + + it('returns a stable fallback instead of throwing when projectRoot does not exist', () => { + const missingProjectRoot = path.join(repoRoot, 'does-not-exist'); + + const result = computeMetroStaticInputs({ + projectRoot: missingProjectRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(result).toEqual({ + lockfileHash: 'unavailable', + bundlerMetroVersion: '1.0.0', + }); + }); + + it('returns a stable fallback instead of throwing when repoRoot does not exist', () => { + const missingRepoRoot = path.join(repoRoot, 'does-not-exist'); + + const result = computeMetroStaticInputs({ + projectRoot, + repoRoot: missingRepoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(result).toEqual({ + lockfileHash: 'unavailable', + bundlerMetroVersion: '1.0.0', + }); + }); + + it('works when projectRoot === repoRoot (single directory examined)', () => { + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-a'); + + const before = computeMetroStaticInputs({ + projectRoot: repoRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + fs.writeFileSync(path.join(repoRoot, 'pnpm-lock.yaml'), 'lockfile-b'); + + const after = computeMetroStaticInputs({ + projectRoot: repoRoot, + repoRoot, + bundlerMetroVersion: '1.0.0', + }); + + expect(before.lockfileHash).not.toBe(after.lockfileHash); + }); +}); diff --git a/packages/cache/src/__tests__/plan.test.ts b/packages/cache/src/__tests__/plan.test.ts new file mode 100644 index 00000000..ea9bbd88 --- /dev/null +++ b/packages/cache/src/__tests__/plan.test.ts @@ -0,0 +1,330 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { createHarnessCachePaths } from '../paths.js'; +import { + planRestore, + planSave, + snapshot, + writeKeysFile, + type CacheKeyInputs, + type CacheSnapshot, +} from '../plan.js'; + +describe('planRestore', () => { + let projectRoot: string; + + beforeEach(() => { + projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-cache-')); + }); + + afterEach(() => { + fs.rmSync(projectRoot, { recursive: true, force: true }); + }); + + const inputs = ( + staticInputs: Record + ): Record<'metro', CacheKeyInputs> => ({ + metro: { os: 'linux', staticInputs }, + }); + + it('produces the same restoreKey across independent calls given the same staticInputs', () => { + const paths = createHarnessCachePaths(projectRoot); + + const first = planRestore(paths, inputs({ a: '1', b: '2' })); + const second = planRestore(paths, inputs({ a: '1', b: '2' })); + + expect(first.domains.metro?.restoreKey).toBe( + second.domains.metro?.restoreKey + ); + }); + + it('produces the same restoreKey regardless of staticInputs key order', () => { + const paths = createHarnessCachePaths(projectRoot); + + const first = planRestore(paths, inputs({ a: '1', b: '2' })); + const second = planRestore(paths, inputs({ b: '2', a: '1' })); + + expect(first.domains.metro?.restoreKey).toBe( + second.domains.metro?.restoreKey + ); + }); + + it('produces a different restoreKey for different staticInputs', () => { + const paths = createHarnessCachePaths(projectRoot); + + const first = planRestore(paths, inputs({ a: '1' })); + const second = planRestore(paths, inputs({ a: '2' })); + + expect(first.domains.metro?.restoreKey).not.toBe( + second.domains.metro?.restoreKey + ); + }); + + it('shapes restorePrefixes for the accretive metro domain as a single trailing-dash prefix', () => { + const paths = createHarnessCachePaths(projectRoot); + const plan = planRestore(paths, inputs({ a: '1' })); + const domain = plan.domains.metro; + + expect(domain?.accretive).toBe(true); + expect(domain?.restorePrefixes).toEqual([`${domain?.restoreKey}-`]); + }); + + it('only includes domains present in the inputs', () => { + const paths = createHarnessCachePaths(projectRoot); + const plan = planRestore(paths, {}); + + expect(plan.domains).toEqual({}); + }); + + it('reports the domain cache directories as paths', () => { + const paths = createHarnessCachePaths(projectRoot); + const plan = planRestore(paths, inputs({ a: '1' })); + + expect(plan.domains.metro?.paths).toEqual([ + paths.metroTransform, + paths.metroFileMap, + ]); + }); +}); + +describe('snapshot', () => { + let projectRoot: string; + + beforeEach(() => { + projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-cache-')); + }); + + afterEach(() => { + fs.rmSync(projectRoot, { recursive: true, force: true }); + }); + + it('is empty for a domain whose directories do not exist', () => { + const paths = createHarnessCachePaths(projectRoot); + + expect(() => snapshot(paths)).not.toThrow(); + expect(snapshot(paths).metro).toEqual([]); + }); + + it('collects entries for files under the domain directories', () => { + const paths = createHarnessCachePaths(projectRoot); + fs.mkdirSync(path.join(paths.metroTransform, 'nested'), { + recursive: true, + }); + fs.writeFileSync(path.join(paths.metroTransform, 'a.txt'), 'aaa'); + fs.writeFileSync( + path.join(paths.metroTransform, 'nested', 'b.txt'), + 'bb' + ); + + const result = snapshot(paths); + const entries = result.metro ?? []; + + expect(entries).toHaveLength(2); + const aEntry = entries.find((entry) => + entry.path.endsWith(path.join('metro', 'a.txt')) + ); + const bEntry = entries.find((entry) => + entry.path.endsWith(path.join('metro', 'nested', 'b.txt')) + ); + expect(aEntry?.sizeBytes).toBe(3); + expect(bEntry?.sizeBytes).toBe(2); + expect(typeof aEntry?.mtimeMs).toBe('number'); + }); +}); + +describe('planSave', () => { + let projectRoot: string; + + beforeEach(() => { + projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-cache-')); + }); + + afterEach(() => { + fs.rmSync(projectRoot, { recursive: true, force: true }); + }); + + const inputs = ( + staticInputs: Record = { a: '1' } + ): Record<'metro', CacheKeyInputs> => ({ + metro: { os: 'linux', staticInputs }, + }); + + it('does not save when there is no change, regardless of policy', () => { + const paths = createHarnessCachePaths(projectRoot); + fs.mkdirSync(paths.metroTransform, { recursive: true }); + fs.writeFileSync(path.join(paths.metroTransform, 'a.txt'), 'aaa'); + + const before = snapshot(paths); + const plan = planSave( + paths, + before, + { mode: 'always', isDefaultBranch: true }, + inputs() + ); + + expect(plan.domains.metro?.shouldSave).toBe(false); + expect(plan.domains.metro?.delta).toEqual({ + addedEntries: 0, + removedEntries: 0, + addedBytes: 0, + }); + }); + + it('saves an added file only when policy allows it', () => { + const paths = createHarnessCachePaths(projectRoot); + fs.mkdirSync(paths.metroTransform, { recursive: true }); + const before = snapshot(paths); + + fs.writeFileSync(path.join(paths.metroTransform, 'a.txt'), 'aaa'); + + const disallowed = planSave( + paths, + before, + { mode: 'default-branch', isDefaultBranch: false }, + inputs() + ); + expect(disallowed.domains.metro?.shouldSave).toBe(false); + expect(disallowed.domains.metro?.delta.addedEntries).toBe(1); + expect(disallowed.domains.metro?.delta.addedBytes).toBe(3); + + const allowed = planSave( + paths, + before, + { mode: 'default-branch', isDefaultBranch: true }, + inputs() + ); + expect(allowed.domains.metro?.shouldSave).toBe(true); + }); + + it('never saves when policy mode is "never"', () => { + const paths = createHarnessCachePaths(projectRoot); + fs.mkdirSync(paths.metroTransform, { recursive: true }); + const before = snapshot(paths); + fs.writeFileSync(path.join(paths.metroTransform, 'a.txt'), 'aaa'); + + const plan = planSave( + paths, + before, + { mode: 'never', isDefaultBranch: true }, + inputs() + ); + + expect(plan.domains.metro?.shouldSave).toBe(false); + }); + + it('counts a removed file in the delta', () => { + const paths = createHarnessCachePaths(projectRoot); + fs.mkdirSync(paths.metroTransform, { recursive: true }); + fs.writeFileSync(path.join(paths.metroTransform, 'a.txt'), 'aaa'); + const before = snapshot(paths); + + fs.rmSync(path.join(paths.metroTransform, 'a.txt')); + + const plan = planSave( + paths, + before, + { mode: 'always', isDefaultBranch: true }, + inputs() + ); + + expect(plan.domains.metro?.delta.removedEntries).toBe(1); + expect(plan.domains.metro?.shouldSave).toBe(true); + }); + + it('does not treat an mtime-only change as an added entry or change the content hash', () => { + const paths = createHarnessCachePaths(projectRoot); + fs.mkdirSync(paths.metroTransform, { recursive: true }); + const filePath = path.join(paths.metroTransform, 'a.txt'); + fs.writeFileSync(filePath, 'aaa'); + const before = snapshot(paths); + + const beforeSave = planSave( + paths, + before, + { mode: 'always', isDefaultBranch: true }, + inputs() + ); + + // Touch the file's mtime without changing its size/content. + const future = new Date(Date.now() + 10_000); + fs.utimesSync(filePath, future, future); + + const afterSave = planSave( + paths, + before, + { mode: 'always', isDefaultBranch: true }, + inputs() + ); + + expect(afterSave.domains.metro?.delta.addedEntries).toBe(0); + expect(afterSave.domains.metro?.shouldSave).toBe(false); + expect(afterSave.domains.metro?.saveKey).toBe( + beforeSave.domains.metro?.saveKey + ); + }); + + it('produces a deterministic saveKey across independent planSave calls given the same on-disk state', () => { + const paths = createHarnessCachePaths(projectRoot); + fs.mkdirSync(paths.metroTransform, { recursive: true }); + fs.writeFileSync(path.join(paths.metroTransform, 'a.txt'), 'aaa'); + const before: CacheSnapshot = {}; + + const first = planSave( + paths, + before, + { mode: 'always', isDefaultBranch: true }, + inputs() + ); + const second = planSave( + paths, + before, + { mode: 'always', isDefaultBranch: true }, + inputs() + ); + + expect(first.domains.metro?.saveKey).toBe(second.domains.metro?.saveKey); + }); +}); + +describe('writeKeysFile', () => { + let projectRoot: string; + + beforeEach(() => { + projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-cache-')); + }); + + afterEach(() => { + fs.rmSync(projectRoot, { recursive: true, force: true }); + }); + + it('writes keys.json under the cache root, outside domain subdirectories', () => { + const paths = createHarnessCachePaths(projectRoot); + const plan = planRestore(paths, { + metro: { os: 'linux', staticInputs: { a: '1' } }, + }); + + writeKeysFile(paths, plan); + + const filePath = path.join(paths.root, 'keys.json'); + expect(fs.existsSync(filePath)).toBe(true); + expect(filePath.startsWith(paths.metroTransform)).toBe(false); + expect(filePath.startsWith(paths.metroFileMap)).toBe(false); + + const written = JSON.parse(fs.readFileSync(filePath, 'utf8')); + expect(written.version).toBe(1); + }); + + it('swallows errors instead of throwing when the cache root cannot be created', () => { + const blockedRoot = path.join(projectRoot, 'blocked-file', 'nested'); + fs.writeFileSync(path.join(projectRoot, 'blocked-file'), 'not a directory'); + const paths = createHarnessCachePaths(blockedRoot); + const plan = planRestore(paths, { + metro: { os: 'linux', staticInputs: { a: '1' } }, + }); + + expect(() => writeKeysFile(paths, plan)).not.toThrow(); + expect(fs.existsSync(path.join(paths.root, 'keys.json'))).toBe(false); + }); +}); diff --git a/packages/cache/src/domains/metro.ts b/packages/cache/src/domains/metro.ts new file mode 100644 index 00000000..02dcb8ac --- /dev/null +++ b/packages/cache/src/domains/metro.ts @@ -0,0 +1,202 @@ +import { createHash } from 'node:crypto'; +import fs from 'node:fs'; +import path from 'node:path'; +import { logger } from '@react-native-harness/tools'; + +const cacheLogger = logger.child('cache'); + +// Metro resolves its own config by walking UP from the project root (see +// Metro's loadConfig docs), never by scanning the tree downward. Lockfiles in +// a monorepo live at the repo root, i.e. somewhere on projectRoot's ancestor +// chain. So instead of a downward `hashFiles('**/…')`-style walk (which also +// has to dodge node_modules/.git/dist/build to avoid vendored copies), we +// walk upward from projectRoot to repoRoot (inclusive) and hash every +// candidate key file that exists along that chain. Sibling workspace +// packages are never on this chain, so their configs can't spuriously +// invalidate this project's cache key. +const LOCKFILE_NAMES: readonly string[] = [ + 'bun.lock', + 'bun.lockb', + 'package-lock.json', + 'npm-shrinkwrap.json', + 'pnpm-lock.yaml', + 'yarn.lock', +]; + +// Metro config candidates: `metro.config.*` at the directory root, plus the +// `.config/metro.*` directory convention Metro also resolves. +const METRO_CONFIG_EXTENSIONS: readonly string[] = [ + 'js', + 'cjs', + 'mjs', + 'ts', + 'cts', + 'mts', + 'json', +]; +const METRO_CONFIG_NAMES: readonly string[] = METRO_CONFIG_EXTENSIONS.map( + (extension) => `metro.config.${extension}` +); +const METRO_CONFIG_DIR_NAMES: readonly string[] = METRO_CONFIG_EXTENSIONS.map( + (extension) => `.config/metro.${extension}` +); + +// Babel only resolves root configs named `babel.config.*` (note: no `.ts` — +// Babel does not resolve `babel.config.ts`). File-relative `.babelrc` and the +// `package.json` `babel`/`metro` fields are intentionally out of scope. +const BABEL_CONFIG_NAMES: readonly string[] = [ + 'babel.config.json', + 'babel.config.js', + 'babel.config.cjs', + 'babel.config.mjs', + 'babel.config.cts', +]; + +// Relative-to-examined-directory paths checked at every directory on the +// ancestor chain. All candidates that exist are hashed (rather than +// emulating Metro/Babel's own first-match-wins resolution), which is +// deterministic and strictly more conservative. +export const METRO_KEY_FILENAMES: readonly string[] = [ + ...LOCKFILE_NAMES, + ...METRO_CONFIG_NAMES, + ...METRO_CONFIG_DIR_NAMES, + ...BABEL_CONFIG_NAMES, +]; + +interface KeyFileMatch { + relativePath: string; + content: Buffer; +} + +/** + * Directories to examine: projectRoot and every ancestor directory up to and + * including repoRoot. Defensive: if projectRoot isn't inside repoRoot (or + * repoRoot can't be related to it), fall back to examining projectRoot alone + * rather than guessing. + */ +const collectAncestorDirectories = ( + projectRoot: string, + repoRoot: string +): string[] => { + const resolvedProjectRoot = path.resolve(projectRoot); + const resolvedRepoRoot = path.resolve(repoRoot); + + if (resolvedProjectRoot === resolvedRepoRoot) { + return [resolvedProjectRoot]; + } + + const relative = path.relative(resolvedRepoRoot, resolvedProjectRoot); + const isProjectRootInsideRepoRoot = + relative !== '' && + !relative.startsWith(`..${path.sep}`) && + relative !== '..' && + !path.isAbsolute(relative); + + if (!isProjectRootInsideRepoRoot) { + return [resolvedProjectRoot]; + } + + const directories: string[] = []; + let current = resolvedProjectRoot; + for (;;) { + directories.push(current); + if (current === resolvedRepoRoot) { + break; + } + const parent = path.dirname(current); + if (parent === current) { + // Reached the filesystem root without hitting repoRoot; defensive + // guard against an infinite loop. + break; + } + current = parent; + } + + return directories; +}; + +const collectKeyFiles = ( + directories: readonly string[], + repoRoot: string +): KeyFileMatch[] => { + const matches: KeyFileMatch[] = []; + + for (const directory of directories) { + for (const candidate of METRO_KEY_FILENAMES) { + const fullPath = path.join(directory, candidate); + + if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) { + continue; + } + + matches.push({ + relativePath: path + .relative(repoRoot, fullPath) + .split(path.sep) + .join('/'), + content: fs.readFileSync(fullPath), + }); + } + } + + return matches; +}; + +const hashKeyFiles = (matches: KeyFileMatch[]): string => { + const hash = createHash('sha256'); + + for (const match of matches + .slice() + .sort((a, b) => (a.relativePath < b.relativePath ? -1 : 1))) { + hash.update(match.relativePath); + hash.update('\0'); + hash.update(match.content); + hash.update('\0'); + } + + return hash.digest('hex'); +}; + +export const computeMetroStaticInputs = (options: { + projectRoot: string; + repoRoot: string; + bundlerMetroVersion: string; + salt?: string; +}): Record => { + try { + const resolvedProjectRoot = path.resolve(options.projectRoot); + const resolvedRepoRoot = path.resolve(options.repoRoot); + + // Validate both roots exist so a missing directory degrades to the + // always-miss fallback below instead of silently hashing zero files. + if (!fs.statSync(resolvedProjectRoot).isDirectory()) { + throw new Error( + `projectRoot "${resolvedProjectRoot}" is not a directory` + ); + } + if (!fs.statSync(resolvedRepoRoot).isDirectory()) { + throw new Error(`repoRoot "${resolvedRepoRoot}" is not a directory`); + } + + const directories = collectAncestorDirectories( + resolvedProjectRoot, + resolvedRepoRoot + ); + const matches = collectKeyFiles(directories, resolvedRepoRoot); + + return { + lockfileHash: hashKeyFiles(matches), + bundlerMetroVersion: options.bundlerMetroVersion, + ...(options.salt ? { salt: options.salt } : {}), + }; + } catch (error) { + cacheLogger.warn( + `Failed to compute Metro cache key inputs for "${options.projectRoot}". Falling back to an always-miss key.`, + error + ); + return { + lockfileHash: 'unavailable', + bundlerMetroVersion: options.bundlerMetroVersion, + }; + } +}; diff --git a/packages/cache/src/index.ts b/packages/cache/src/index.ts index e511d23f..6e9cff23 100644 --- a/packages/cache/src/index.ts +++ b/packages/cache/src/index.ts @@ -6,14 +6,51 @@ import { type CacheDomainId, type HarnessCachePaths, } from './paths.js'; +import { + planRestore, + planSave, + snapshot, + writeKeysFile, + type CacheKeyInputs, + type CacheSnapshot, + type CacheSnapshotEntry, + type DomainRestorePlan, + type DomainSavePlan, + type RestorePlan, + type SavePlan, + type SavePolicy, +} from './plan.js'; import { isDomainWarm } from './warmth.js'; +import { computeMetroStaticInputs, METRO_KEY_FILENAMES } from './domains/metro.js'; + +export { computeMetroStaticInputs, METRO_KEY_FILENAMES }; export type { CacheDomainId, HarnessCachePaths }; +export type { + CacheKeyInputs, + CacheSnapshot, + CacheSnapshotEntry, + DomainRestorePlan, + DomainSavePlan, + RestorePlan, + SavePlan, + SavePolicy, +}; export interface HarnessCache { readonly paths: HarnessCachePaths; isWarm(domain: CacheDomainId): boolean; ensureDomainDirectories(domain: CacheDomainId): void; + planRestore( + inputs: Partial> + ): RestorePlan; + snapshot(): CacheSnapshot; + planSave( + before: CacheSnapshot, + policy: SavePolicy, + inputs: Partial> + ): SavePlan; + writeKeysFile(plan: RestorePlan | SavePlan): void; } const cacheLogger = logger.child('cache'); @@ -38,5 +75,10 @@ export const createHarnessCache = (options: { } } }, + planRestore: (inputs) => planRestore(paths, inputs), + snapshot: () => snapshot(paths), + planSave: (before, policy, inputs) => + planSave(paths, before, policy, inputs), + writeKeysFile: (plan) => writeKeysFile(paths, plan), }; }; diff --git a/packages/cache/src/plan.ts b/packages/cache/src/plan.ts new file mode 100644 index 00000000..c1eda032 --- /dev/null +++ b/packages/cache/src/plan.ts @@ -0,0 +1,285 @@ +import { createHash } from 'node:crypto'; +import fs from 'node:fs'; +import path from 'node:path'; +import { logger } from '@react-native-harness/tools'; +import { getDomainDirectories } from './paths.js'; +import type { CacheDomainId, HarnessCachePaths } from './paths.js'; + +const cacheLogger = logger.child('cache'); + +const KEYS_FILENAME = 'keys.json'; +const HASH_LENGTH = 16; + +export interface CacheKeyInputs { + os: string; + staticInputs: Record; +} + +export interface DomainRestorePlan { + paths: string[]; + restoreKey: string; + restorePrefixes: string[]; + accretive: boolean; +} + +export interface RestorePlan { + version: 1; + domains: Partial>; +} + +export interface CacheSnapshotEntry { + path: string; + sizeBytes: number; + mtimeMs: number; +} + +export type CacheSnapshot = Partial>; + +export interface SavePolicy { + mode: 'default-branch' | 'always' | 'never'; + isDefaultBranch: boolean; +} + +export interface DomainSavePlan { + shouldSave: boolean; + saveKey: string; + delta: { + addedEntries: number; + removedEntries: number; + addedBytes: number; + }; +} + +export interface SavePlan { + version: 1; + domains: Partial>; +} + +const ALL_DOMAINS: readonly CacheDomainId[] = ['metro']; + +/** + * Accretive domains only ever grow (transform/file-map caches keyed by + * content hash) so a save is always safe to key off the static hash plus a + * content hash, and prior saves can be found via a prefix match on the + * static hash. Non-accretive domains would need an exact key match instead. + * A domain not yet handled here defaults to non-accretive (exact-match), + * matching the safe-fallback style of `getDomainDirectories`/ + * `getWarmthDirectory`: prefix-matching into an unknown domain's saves would + * not be a neutral no-op, so the conservative default is to require an exact + * key match rather than silently opt a future domain into accretive lookup. + */ +const isAccretiveDomain = (domain: CacheDomainId): boolean => { + switch (domain) { + case 'metro': + return true; + default: + return false; + } +}; + +const hashSortedEntries = (entries: Array<[string, string]>): string => + createHash('sha256') + .update( + entries + .slice() + .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)) + .map(([key, value]) => `${key}=${value}`) + .join('\n') + ) + .digest('hex') + .slice(0, HASH_LENGTH); + +const buildStaticHashKey = ( + domain: CacheDomainId, + inputs: CacheKeyInputs +): string => { + const hash = hashSortedEntries(Object.entries(inputs.staticInputs)); + return `harness-${domain}-${inputs.os}-${hash}`; +}; + +const buildContentHash = (entries: CacheSnapshotEntry[]): string => + hashSortedEntries( + entries.map((entry) => [entry.path, String(entry.sizeBytes)]) + ); + +const walkDirectory = ( + root: string, + directory: string, + entries: CacheSnapshotEntry[] +): void => { + let dirents: fs.Dirent[]; + + try { + dirents = fs.readdirSync(directory, { withFileTypes: true }); + } catch { + // Missing/unreadable directory is treated as empty rather than an error. + return; + } + + for (const dirent of dirents) { + const fullPath = path.join(directory, dirent.name); + + if (dirent.isDirectory()) { + walkDirectory(root, fullPath, entries); + } else if (dirent.isFile()) { + const stat = fs.statSync(fullPath); + entries.push({ + path: path.relative(root, fullPath), + sizeBytes: stat.size, + mtimeMs: stat.mtimeMs, + }); + } + } +}; + +export const planRestore = ( + paths: HarnessCachePaths, + inputs: Partial> +): RestorePlan => { + const domains: RestorePlan['domains'] = {}; + + for (const domain of ALL_DOMAINS) { + const domainInputs = inputs[domain]; + if (!domainInputs) { + continue; + } + + const restoreKey = buildStaticHashKey(domain, domainInputs); + const accretive = isAccretiveDomain(domain); + + domains[domain] = { + paths: [...getDomainDirectories(domain, paths)], + restoreKey, + restorePrefixes: accretive ? [`${restoreKey}-`] : [], + accretive, + }; + } + + return { version: 1, domains }; +}; + +/** + * Synchronous snapshot of on-disk cache contents, keyed by domain. Never + * throws: a domain whose directories cannot be read is reported as empty. + */ +export const snapshot = (paths: HarnessCachePaths): CacheSnapshot => { + const result: CacheSnapshot = {}; + + for (const domain of ALL_DOMAINS) { + try { + const entries: CacheSnapshotEntry[] = []; + for (const directory of getDomainDirectories(domain, paths)) { + walkDirectory(paths.root, directory, entries); + } + result[domain] = entries; + } catch (error) { + cacheLogger.warn( + `Failed to snapshot cache domain "${domain}". Treating as empty.`, + error + ); + result[domain] = []; + } + } + + return result; +}; + +const diffSnapshots = ( + before: CacheSnapshotEntry[], + after: CacheSnapshotEntry[] +): DomainSavePlan['delta'] => { + const beforeByPath = new Map(before.map((entry) => [entry.path, entry])); + const afterByPath = new Map(after.map((entry) => [entry.path, entry])); + + let addedEntries = 0; + let removedEntries = 0; + let addedBytes = 0; + + for (const [entryPath, afterEntry] of afterByPath) { + const beforeEntry = beforeByPath.get(entryPath); + if (!beforeEntry || beforeEntry.sizeBytes !== afterEntry.sizeBytes) { + addedEntries += 1; + addedBytes += afterEntry.sizeBytes; + } + } + + for (const entryPath of beforeByPath.keys()) { + if (!afterByPath.has(entryPath)) { + removedEntries += 1; + } + } + + return { addedEntries, removedEntries, addedBytes }; +}; + +const shouldSaveForPolicy = ( + policy: SavePolicy, + delta: DomainSavePlan['delta'] +): boolean => { + if (delta.addedEntries + delta.removedEntries === 0) { + return false; + } + + switch (policy.mode) { + case 'always': + return true; + case 'default-branch': + return policy.isDefaultBranch; + case 'never': + return false; + } +}; + +export const planSave = ( + paths: HarnessCachePaths, + before: CacheSnapshot, + policy: SavePolicy, + inputs: Partial> +): SavePlan => { + const after = snapshot(paths); + const domains: SavePlan['domains'] = {}; + + for (const domain of ALL_DOMAINS) { + const domainInputs = inputs[domain]; + if (!domainInputs) { + continue; + } + + const beforeEntries = before[domain] ?? []; + const afterEntries = after[domain] ?? []; + const delta = diffSnapshots(beforeEntries, afterEntries); + const staticHashKey = buildStaticHashKey(domain, domainInputs); + const accretive = isAccretiveDomain(domain); + + domains[domain] = { + shouldSave: shouldSaveForPolicy(policy, delta), + saveKey: accretive + ? `${staticHashKey}-${buildContentHash(afterEntries)}` + : staticHashKey, + delta, + }; + } + + return { version: 1, domains }; +}; + +/** + * Written to `/keys.json`, outside any domain's cached + * subdirectories, so it is never itself part of what gets restored/saved. + * It is a secondary/debug artifact; the actual data flow between CI steps + * goes through GITHUB_OUTPUT, not by re-reading this file. Cache I/O must + * never fail a run, so write failures are logged and swallowed. + */ +export const writeKeysFile = ( + paths: HarnessCachePaths, + plan: RestorePlan | SavePlan +): void => { + const filePath = path.join(paths.root, KEYS_FILENAME); + + try { + fs.mkdirSync(paths.root, { recursive: true }); + fs.writeFileSync(filePath, JSON.stringify(plan, null, 2)); + } catch (error) { + cacheLogger.warn(`Failed to write cache keys file "${filePath}".`, error); + } +}; diff --git a/packages/github-action/README.md b/packages/github-action/README.md index b10b9f14..8c4ff783 100644 --- a/packages/github-action/README.md +++ b/packages/github-action/README.md @@ -32,8 +32,9 @@ The action reads your `rn-harness.config.mjs` file, resolves the `runner` you pa - `cacheAvd` (optional, Android only): Whether to cache the Android Virtual Device snapshot. Defaults to `true` - `preRunHook` (optional): Inline shell script run in `bash` immediately before Harness starts - `afterRunHook` (optional): Inline shell script run in `bash` immediately after Harness finishes and before artifact upload +- `cacheSavePolicy` (optional): When to save a new Metro cache entry -- `default-branch` (default), `always`, or `never` - Crash artifacts persisted to `.harness/crash-reports/` are uploaded automatically when present -- Metro cache persisted to `.harness/cache/metro/` and `.harness/cache/metro-file-map/` is restored and saved automatically when present +- Metro cache persisted to `.harness/cache/metro/` and `.harness/cache/metro-file-map/` is restored and saved automatically when present. The cache key is computed by `@react-native-harness/cache` from your lockfile(s), Metro/Babel config, the installed `@react-native-harness/bundler-metro` version, and your Harness config's `cache.version` salt -- a new entry is only saved when the run's cache content actually changed and `cacheSavePolicy` allows it ## Behavior diff --git a/packages/github-action/eslint.config.mjs b/packages/github-action/eslint.config.mjs index dc4ddc31..fabd4248 100644 --- a/packages/github-action/eslint.config.mjs +++ b/packages/github-action/eslint.config.mjs @@ -12,6 +12,18 @@ export default [ '{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}', '{projectRoot}/tsup.config.{js,cjs,mjs,ts,cts,mts}', ], + ignoredDependencies: [ + 'vite', + 'vitest', + // Referenced only as a require.resolve() target to read the + // consuming project's own installed version at runtime, not + // imported/bundled by this package. + '@react-native-harness/bundler-metro', + // Referenced only as a require.resolve() fallback root to reach + // bundler-metro when it's nested under jest's own node_modules, + // not imported/bundled by this package. + '@react-native-harness/jest', + ], }, ], }, diff --git a/packages/github-action/package.json b/packages/github-action/package.json index 4d2b87c2..85b6bc0e 100644 --- a/packages/github-action/package.json +++ b/packages/github-action/package.json @@ -11,6 +11,7 @@ "build": "tsup" }, "dependencies": { + "@react-native-harness/cache": "workspace:*", "@react-native-harness/config": "workspace:*", "@react-native-harness/platform-android": "workspace:*" }, diff --git a/packages/github-action/src/action.yml b/packages/github-action/src/action.yml index 09cf8899..0a6fe34b 100644 --- a/packages/github-action/src/action.yml +++ b/packages/github-action/src/action.yml @@ -43,6 +43,11 @@ inputs: required: false type: string default: '' + cacheSavePolicy: + description: When to save a new Metro cache entry ('default-branch', 'always', or 'never') + required: false + type: string + default: 'default-branch' runs: using: 'composite' steps: @@ -64,21 +69,45 @@ runs: echo "Please provide the path to the built app (.apk for Android, .app for iOS)" exit 1 fi - - name: Metro bundler cache (.harness/cache/metro) - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + - name: Plan Metro cache restore + id: plan-metro-restore + shell: bash + env: + INPUT_PROJECTROOT: ${{ steps.load-config.outputs.projectRoot }} + run: | + node ${{ github.action_path }}/actions/shared/plan-restore.cjs + - name: Restore Metro cache (.harness/cache/metro) + id: restore-metro + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/metro ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/metro-file-map - key: ${{ runner.os }}-metro-cache-v2-${{ hashFiles('**/bun.lock', '**/bun.lockb', '**/package-lock.json', '**/npm-shrinkwrap.json', '**/pnpm-lock.yaml', '**/yarn.lock', '**/metro.config.js', '**/metro.config.cjs', '**/metro.config.mjs', '**/metro.config.ts', '**/babel.config.js', '**/babel.config.cjs', '**/babel.config.mjs', '**/babel.config.ts', '**/babel.config.json') }} - restore-keys: | - ${{ runner.os }}-metro-cache-v2- + key: ${{ steps.plan-metro-restore.outputs.metroRestoreKey }} + # The '\n' here is a literal newline embedded via YAML double-quote + # escaping (not a GitHub Actions expression escape -- expression + # string literals don't interpret backslash sequences), so join() + # actually separates entries onto their own lines, as actions/cache + # requires for restore-keys. + restore-keys: "${{ join(fromJson(steps.plan-metro-restore.outputs.metroRestorePrefixes), '\n') }}" + # Must run after "Restore Metro cache" so this reflects what was actually + # restored (empty on a cache miss), not the always-empty pre-restore state. + - name: Snapshot Metro cache + id: snapshot-metro-restore + shell: bash + env: + INPUT_PROJECTROOT: ${{ steps.load-config.outputs.projectRoot }} + # Empty on a cache miss; snapshot-metro.cjs uses it to report whether + # the Metro cache was actually restored and from which key. + METRO_RESTORED_KEY: ${{ steps.restore-metro.outputs.cache-matched-key }} + run: | + node ${{ github.action_path }}/actions/shared/snapshot-metro.cjs - name: Restore Harness cache id: cache-harness-restore if: fromJson(steps.load-config.outputs.config).platformId == 'ios' uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: - path: ${{ steps.load-config.outputs.projectRoot }}/.harness/cache + path: ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/xctest-agent-simulator-* key: harness-ios-${{ runner.os }}-${{ hashFiles(format('{0}/.harness/cache/**/cache.json', steps.load-config.outputs.projectRoot)) }} restore-keys: | harness-ios-${{ runner.os }}- @@ -242,8 +271,27 @@ runs: if: ${{ always() && fromJson(steps.load-config.outputs.config).platformId == 'ios' && steps.cache-harness-restore.outputs.cache-hit != 'true' }} uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: - path: ${{ steps.load-config.outputs.projectRoot }}/.harness/cache + path: ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/xctest-agent-simulator-* key: ${{ steps.cache-harness-restore.outputs.cache-primary-key }} + - name: Plan Metro cache save + id: plan-metro-save + if: always() + shell: bash + env: + INPUT_PROJECTROOT: ${{ steps.load-config.outputs.projectRoot }} + INPUT_METRO_SNAPSHOT: ${{ steps.snapshot-metro-restore.outputs.metroSnapshot }} + INPUT_CACHESAVEPOLICY: ${{ inputs.cacheSavePolicy }} + IS_DEFAULT_BRANCH: ${{ github.ref_name == github.event.repository.default_branch }} + run: | + node ${{ github.action_path }}/actions/shared/plan-save.cjs + - name: Save Metro cache + if: always() && steps.plan-metro-save.outputs.metroShouldSave == 'true' + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: | + ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/metro + ${{ steps.load-config.outputs.projectRoot }}/.harness/cache/metro-file-map + key: ${{ steps.plan-metro-save.outputs.metroSaveKey }} - name: Upload crash report artifacts if: always() uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 diff --git a/packages/github-action/src/shared/__tests__/metro-cache-inputs.test.ts b/packages/github-action/src/shared/__tests__/metro-cache-inputs.test.ts new file mode 100644 index 00000000..5838a36d --- /dev/null +++ b/packages/github-action/src/shared/__tests__/metro-cache-inputs.test.ts @@ -0,0 +1,194 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { + resolveBundlerMetroVersion, + resolveMetroStaticInputs, + resolveRepoRoot, +} from '../metro-cache-inputs.js'; + +const mocks = vi.hoisted(() => ({ + computeMetroStaticInputs: vi.fn(), +})); + +// This package's job is wiring, not re-testing @react-native-harness/cache's +// own logic, so its exports are mocked rather than exercised for real. +vi.mock('@react-native-harness/cache', () => ({ + computeMetroStaticInputs: mocks.computeMetroStaticInputs, +})); + +describe('resolveRepoRoot', () => { + let root: string; + + beforeEach(() => { + root = fs.mkdtempSync(path.join(os.tmpdir(), 'gha-repo-root-')); + }); + + afterEach(() => { + fs.rmSync(root, { recursive: true, force: true }); + }); + + it('walks up to the nearest ancestor containing a .git directory', () => { + fs.mkdirSync(path.join(root, '.git')); + const projectRoot = path.join(root, 'apps', 'mobile'); + fs.mkdirSync(projectRoot, { recursive: true }); + + expect(resolveRepoRoot(projectRoot)).toBe(root); + }); + + it('falls back to projectRoot when no .git directory is found', () => { + const projectRoot = path.join(root, 'no-git-here'); + fs.mkdirSync(projectRoot, { recursive: true }); + + expect(resolveRepoRoot(projectRoot)).toBe(projectRoot); + }); +}); + +describe('resolveBundlerMetroVersion', () => { + let projectRoot: string; + + beforeEach(() => { + projectRoot = fs.mkdtempSync( + path.join(os.tmpdir(), 'gha-bundler-metro-version-') + ); + vi.spyOn(console, 'warn').mockImplementation(() => undefined); + }); + + afterEach(() => { + fs.rmSync(projectRoot, { recursive: true, force: true }); + vi.restoreAllMocks(); + }); + + it('resolves the version from the consuming project\'s installed package', () => { + const packageDir = path.join( + projectRoot, + 'node_modules', + '@react-native-harness', + 'bundler-metro' + ); + fs.mkdirSync(packageDir, { recursive: true }); + fs.writeFileSync( + path.join(packageDir, 'package.json'), + JSON.stringify({ + name: '@react-native-harness/bundler-metro', + version: '9.9.9', + main: 'index.js', + }) + ); + fs.writeFileSync(path.join(packageDir, 'index.js'), 'module.exports = {};'); + + expect(resolveBundlerMetroVersion(projectRoot)).toBe('9.9.9'); + }); + + it('resolves the version through @react-native-harness/jest when bundler-metro is only a nested dependency', () => { + const jestPackageDir = path.join( + projectRoot, + 'node_modules', + '@react-native-harness', + 'jest' + ); + fs.mkdirSync(jestPackageDir, { recursive: true }); + fs.writeFileSync( + path.join(jestPackageDir, 'package.json'), + JSON.stringify({ + name: '@react-native-harness/jest', + version: '1.0.0', + main: 'index.js', + }) + ); + fs.writeFileSync(path.join(jestPackageDir, 'index.js'), 'module.exports = {};'); + + const nestedBundlerMetroDir = path.join( + jestPackageDir, + 'node_modules', + '@react-native-harness', + 'bundler-metro' + ); + fs.mkdirSync(nestedBundlerMetroDir, { recursive: true }); + fs.writeFileSync( + path.join(nestedBundlerMetroDir, 'package.json'), + JSON.stringify({ + name: '@react-native-harness/bundler-metro', + version: '2.2.2', + main: 'index.js', + }) + ); + fs.writeFileSync( + path.join(nestedBundlerMetroDir, 'index.js'), + 'module.exports = {};' + ); + + expect(resolveBundlerMetroVersion(projectRoot)).toBe('2.2.2'); + }); + + it('falls back to "unknown" and warns when the package cannot be resolved', () => { + expect(resolveBundlerMetroVersion(projectRoot)).toBe('unknown'); + expect(console.warn).toHaveBeenCalled(); + }); +}); + +describe('resolveMetroStaticInputs', () => { + let repoRoot: string; + let projectRoot: string; + + beforeEach(() => { + vi.clearAllMocks(); + + repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gha-static-inputs-')); + fs.mkdirSync(path.join(repoRoot, '.git')); + projectRoot = path.join(repoRoot, 'apps', 'mobile'); + + const packageDir = path.join( + projectRoot, + 'node_modules', + '@react-native-harness', + 'bundler-metro' + ); + fs.mkdirSync(packageDir, { recursive: true }); + fs.writeFileSync( + path.join(packageDir, 'package.json'), + JSON.stringify({ + name: '@react-native-harness/bundler-metro', + version: '9.9.9', + main: 'index.js', + }) + ); + fs.writeFileSync(path.join(packageDir, 'index.js'), 'module.exports = {};'); + }); + + afterEach(() => { + fs.rmSync(repoRoot, { recursive: true, force: true }); + vi.restoreAllMocks(); + }); + + it('forwards projectRoot, the resolved repoRoot, bundlerMetroVersion, and salt', () => { + mocks.computeMetroStaticInputs.mockReturnValue({ lockfileHash: 'abc' }); + + const result = resolveMetroStaticInputs({ + projectRoot, + cacheVersionSalt: 'v2', + }); + + expect(mocks.computeMetroStaticInputs).toHaveBeenCalledWith({ + projectRoot, + repoRoot, + bundlerMetroVersion: '9.9.9', + salt: 'v2', + }); + expect(result).toEqual({ lockfileHash: 'abc' }); + }); + + it('omits the salt when no cacheVersionSalt is provided', () => { + mocks.computeMetroStaticInputs.mockReturnValue({ lockfileHash: 'abc' }); + + resolveMetroStaticInputs({ projectRoot }); + + expect(mocks.computeMetroStaticInputs).toHaveBeenCalledWith({ + projectRoot, + repoRoot, + bundlerMetroVersion: '9.9.9', + salt: undefined, + }); + }); +}); diff --git a/packages/github-action/src/shared/__tests__/plan-restore.test.ts b/packages/github-action/src/shared/__tests__/plan-restore.test.ts new file mode 100644 index 00000000..e2c89d7f --- /dev/null +++ b/packages/github-action/src/shared/__tests__/plan-restore.test.ts @@ -0,0 +1,123 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + getConfig: vi.fn(), + createHarnessCache: vi.fn(), + computeMetroStaticInputs: vi.fn(), +})); + +vi.mock('@react-native-harness/config', () => ({ + getConfig: mocks.getConfig, +})); + +// This package's job is wiring, not re-testing @react-native-harness/cache's +// own logic, so its exports are mocked rather than exercised for real. +vi.mock('@react-native-harness/cache', () => ({ + createHarnessCache: mocks.createHarnessCache, + computeMetroStaticInputs: mocks.computeMetroStaticInputs, +})); + +describe('plan-restore', () => { + let projectRoot: string; + let githubOutputPath: string; + let planRestoreMock: ReturnType; + let writeKeysFileMock: ReturnType; + + beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + + projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gha-plan-restore-')); + githubOutputPath = path.join(projectRoot, 'github-output.txt'); + fs.writeFileSync(githubOutputPath, ''); + + process.env.INPUT_PROJECTROOT = projectRoot; + process.env.GITHUB_OUTPUT = githubOutputPath; + process.env.RUNNER_OS = 'Linux'; + + mocks.getConfig.mockResolvedValue({ + config: { cache: { version: 'salt-1' } }, + projectRoot, + }); + mocks.computeMetroStaticInputs.mockReturnValue({ lockfileHash: 'abc' }); + + planRestoreMock = vi.fn().mockReturnValue({ + version: 1, + domains: { + metro: { + paths: ['/tmp/metro', '/tmp/metro-file-map'], + restoreKey: 'harness-metro-linux-abc', + restorePrefixes: ['harness-metro-linux-abc-'], + accretive: true, + }, + }, + }); + writeKeysFileMock = vi.fn(); + + mocks.createHarnessCache.mockReturnValue({ + planRestore: planRestoreMock, + writeKeysFile: writeKeysFileMock, + }); + + vi.spyOn(process, 'exit').mockImplementation(() => undefined as never); + vi.spyOn(console, 'error').mockImplementation(() => undefined); + vi.spyOn(console, 'warn').mockImplementation(() => undefined); + vi.spyOn(console, 'info').mockImplementation(() => undefined); + }); + + afterEach(() => { + fs.rmSync(projectRoot, { recursive: true, force: true }); + delete process.env.INPUT_PROJECTROOT; + delete process.env.GITHUB_OUTPUT; + delete process.env.RUNNER_OS; + vi.restoreAllMocks(); + }); + + it('writes metroRestoreKey and metroRestorePrefixes (as JSON) to GITHUB_OUTPUT', async () => { + await import('../plan-restore.js'); + + await vi.waitFor(() => { + expect(writeKeysFileMock).toHaveBeenCalled(); + }); + + const output = fs.readFileSync(githubOutputPath, 'utf8'); + expect(output).toContain('metroRestoreKey=harness-metro-linux-abc\n'); + expect(output).toContain( + 'metroRestorePrefixes=["harness-metro-linux-abc-"]\n' + ); + // The pre-run snapshot is taken by the separate snapshot-metro step, + // which runs after the actual cache restore -- not here. + expect(output).not.toContain('metroSnapshot'); + }); + + it('calls planRestore with the os and staticInputs assembled from the Metro key recipe', async () => { + await import('../plan-restore.js'); + + await vi.waitFor(() => { + expect(planRestoreMock).toHaveBeenCalled(); + }); + + expect(planRestoreMock).toHaveBeenCalledWith({ + metro: { os: 'Linux', staticInputs: { lockfileHash: 'abc' } }, + }); + expect(mocks.computeMetroStaticInputs).toHaveBeenCalledWith( + expect.objectContaining({ salt: 'salt-1' }) + ); + }); + + it('fails loudly (process.exit(1)) when GITHUB_OUTPUT is not set', async () => { + delete process.env.GITHUB_OUTPUT; + + await import('../plan-restore.js'); + + await vi.waitFor(() => { + expect(process.exit).toHaveBeenCalledWith(1); + }); + expect(console.error).toHaveBeenCalledWith( + 'GITHUB_OUTPUT environment variable is not set' + ); + }); +}); diff --git a/packages/github-action/src/shared/__tests__/plan-save.test.ts b/packages/github-action/src/shared/__tests__/plan-save.test.ts new file mode 100644 index 00000000..70dc4910 --- /dev/null +++ b/packages/github-action/src/shared/__tests__/plan-save.test.ts @@ -0,0 +1,250 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + getConfig: vi.fn(), + createHarnessCache: vi.fn(), + computeMetroStaticInputs: vi.fn(), +})); + +vi.mock('@react-native-harness/config', () => ({ + getConfig: mocks.getConfig, +})); + +vi.mock('@react-native-harness/cache', () => ({ + createHarnessCache: mocks.createHarnessCache, + computeMetroStaticInputs: mocks.computeMetroStaticInputs, +})); + +describe('plan-save', () => { + let projectRoot: string; + let githubOutputPath: string; + let planSaveMock: ReturnType; + let writeKeysFileMock: ReturnType; + + beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + + projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gha-plan-save-')); + githubOutputPath = path.join(projectRoot, 'github-output.txt'); + fs.writeFileSync(githubOutputPath, ''); + + process.env.INPUT_PROJECTROOT = projectRoot; + process.env.GITHUB_OUTPUT = githubOutputPath; + process.env.RUNNER_OS = 'Linux'; + process.env.INPUT_METRO_SNAPSHOT = JSON.stringify({ metro: [] }); + process.env.IS_DEFAULT_BRANCH = 'true'; + process.env.INPUT_CACHESAVEPOLICY = 'default-branch'; + + mocks.getConfig.mockResolvedValue({ + config: { cache: { version: 'salt-1' } }, + projectRoot, + }); + mocks.computeMetroStaticInputs.mockReturnValue({ lockfileHash: 'abc' }); + + planSaveMock = vi.fn().mockReturnValue({ + version: 1, + domains: { + metro: { + shouldSave: true, + saveKey: 'harness-metro-linux-abc-contenthash', + delta: { addedEntries: 1, removedEntries: 0, addedBytes: 10 }, + }, + }, + }); + writeKeysFileMock = vi.fn(); + + mocks.createHarnessCache.mockReturnValue({ + planSave: planSaveMock, + writeKeysFile: writeKeysFileMock, + }); + + vi.spyOn(process, 'exit').mockImplementation(() => undefined as never); + vi.spyOn(console, 'error').mockImplementation(() => undefined); + vi.spyOn(console, 'warn').mockImplementation(() => undefined); + vi.spyOn(console, 'info').mockImplementation(() => undefined); + }); + + afterEach(() => { + fs.rmSync(projectRoot, { recursive: true, force: true }); + delete process.env.INPUT_PROJECTROOT; + delete process.env.GITHUB_OUTPUT; + delete process.env.RUNNER_OS; + delete process.env.INPUT_METRO_SNAPSHOT; + delete process.env.IS_DEFAULT_BRANCH; + delete process.env.INPUT_CACHESAVEPOLICY; + vi.restoreAllMocks(); + }); + + it('writes metroShouldSave and metroSaveKey to GITHUB_OUTPUT', async () => { + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(writeKeysFileMock).toHaveBeenCalled(); + }); + + const output = fs.readFileSync(githubOutputPath, 'utf8'); + expect(output).toContain('metroShouldSave=true\n'); + expect(output).toContain( + 'metroSaveKey=harness-metro-linux-abc-contenthash\n' + ); + }); + + it('parses the metroSnapshot input and builds the SavePolicy from env/input', async () => { + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(planSaveMock).toHaveBeenCalled(); + }); + + expect(planSaveMock).toHaveBeenCalledWith( + { metro: [] }, + { mode: 'default-branch', isDefaultBranch: true }, + { metro: { os: 'Linux', staticInputs: { lockfileHash: 'abc' } } } + ); + }); + + it('treats an unparseable metroSnapshot input as an empty snapshot instead of throwing', async () => { + process.env.INPUT_METRO_SNAPSHOT = 'not json'; + + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(planSaveMock).toHaveBeenCalled(); + }); + + expect(planSaveMock).toHaveBeenCalledWith( + {}, + expect.anything(), + expect.anything() + ); + expect(console.warn).toHaveBeenCalled(); + }); + + it('defaults an unrecognized cacheSavePolicy input to "default-branch"', async () => { + process.env.INPUT_CACHESAVEPOLICY = 'bogus'; + + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(planSaveMock).toHaveBeenCalled(); + }); + + expect(planSaveMock).toHaveBeenCalledWith( + expect.anything(), + { mode: 'default-branch', isDefaultBranch: true }, + expect.anything() + ); + }); + + it('reports the delta and save key when the cache changed and will be saved', async () => { + planSaveMock.mockReturnValue({ + version: 1, + domains: { + metro: { + shouldSave: true, + saveKey: 'harness-metro-linux-abc-contenthash', + delta: { + addedEntries: 3, + removedEntries: 1, + addedBytes: 2 * 1024 * 1024, + }, + }, + }, + }); + + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(writeKeysFileMock).toHaveBeenCalled(); + }); + + expect(console.info).toHaveBeenCalledWith( + 'Metro cache: changed (+3 files / 2.0 MB added, 1 removed) — saving new entry with key "harness-metro-linux-abc-contenthash".' + ); + }); + + it('reports an unchanged cache as the reason for skipping the save', async () => { + planSaveMock.mockReturnValue({ + version: 1, + domains: { + metro: { + shouldSave: false, + saveKey: 'harness-metro-linux-abc-contenthash', + delta: { addedEntries: 0, removedEntries: 0, addedBytes: 0 }, + }, + }, + }); + + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(writeKeysFileMock).toHaveBeenCalled(); + }); + + expect(console.info).toHaveBeenCalledWith( + 'Metro cache: unchanged — skipping save.' + ); + }); + + it('reports the "default-branch" policy as the reason when a change is not saved off the default branch', async () => { + process.env.IS_DEFAULT_BRANCH = 'false'; + planSaveMock.mockReturnValue({ + version: 1, + domains: { + metro: { + shouldSave: false, + saveKey: 'harness-metro-linux-abc-contenthash', + delta: { addedEntries: 2, removedEntries: 0, addedBytes: 1024 }, + }, + }, + }); + + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(writeKeysFileMock).toHaveBeenCalled(); + }); + + expect(console.info).toHaveBeenCalledWith( + 'Metro cache: changed (+2 files / 0.0 MB added, 0 removed) but save skipped by policy "default-branch" (current branch is not the default branch).' + ); + }); + + it('reports the "never" policy as the reason when a change is not saved', async () => { + process.env.INPUT_CACHESAVEPOLICY = 'never'; + planSaveMock.mockReturnValue({ + version: 1, + domains: { + metro: { + shouldSave: false, + saveKey: 'harness-metro-linux-abc-contenthash', + delta: { addedEntries: 2, removedEntries: 0, addedBytes: 1024 }, + }, + }, + }); + + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(writeKeysFileMock).toHaveBeenCalled(); + }); + + expect(console.info).toHaveBeenCalledWith( + 'Metro cache: changed (+2 files / 0.0 MB added, 0 removed) but save skipped by policy "never".' + ); + }); + + it('fails loudly (process.exit(1)) when GITHUB_OUTPUT is not set', async () => { + delete process.env.GITHUB_OUTPUT; + + await import('../plan-save.js'); + + await vi.waitFor(() => { + expect(process.exit).toHaveBeenCalledWith(1); + }); + }); +}); diff --git a/packages/github-action/src/shared/__tests__/snapshot-metro.test.ts b/packages/github-action/src/shared/__tests__/snapshot-metro.test.ts new file mode 100644 index 00000000..69b9b11f --- /dev/null +++ b/packages/github-action/src/shared/__tests__/snapshot-metro.test.ts @@ -0,0 +1,117 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +const mocks = vi.hoisted(() => ({ + getConfig: vi.fn(), + createHarnessCache: vi.fn(), +})); + +vi.mock('@react-native-harness/config', () => ({ + getConfig: mocks.getConfig, +})); + +vi.mock('@react-native-harness/cache', () => ({ + createHarnessCache: mocks.createHarnessCache, +})); + +describe('snapshot-metro', () => { + let projectRoot: string; + let githubOutputPath: string; + let snapshotMock: ReturnType; + + beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + + projectRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gha-snapshot-metro-')); + githubOutputPath = path.join(projectRoot, 'github-output.txt'); + fs.writeFileSync(githubOutputPath, ''); + + process.env.INPUT_PROJECTROOT = projectRoot; + process.env.GITHUB_OUTPUT = githubOutputPath; + + mocks.getConfig.mockResolvedValue({ + config: {}, + projectRoot, + }); + + snapshotMock = vi.fn().mockReturnValue({ + metro: [{ path: 'metro/a', sizeBytes: 3, mtimeMs: 1 }], + }); + + mocks.createHarnessCache.mockReturnValue({ + snapshot: snapshotMock, + }); + + vi.spyOn(process, 'exit').mockImplementation(() => undefined as never); + vi.spyOn(console, 'error').mockImplementation(() => undefined); + vi.spyOn(console, 'info').mockImplementation(() => undefined); + }); + + afterEach(() => { + fs.rmSync(projectRoot, { recursive: true, force: true }); + delete process.env.INPUT_PROJECTROOT; + delete process.env.GITHUB_OUTPUT; + delete process.env.METRO_RESTORED_KEY; + vi.restoreAllMocks(); + }); + + it('writes the current cache snapshot to GITHUB_OUTPUT as metroSnapshot', async () => { + await import('../snapshot-metro.js'); + + await vi.waitFor(() => { + expect(snapshotMock).toHaveBeenCalled(); + }); + + const output = fs.readFileSync(githubOutputPath, 'utf8'); + expect(output).toContain( + 'metroSnapshot={"metro":[{"path":"metro/a","sizeBytes":3,"mtimeMs":1}]}\n' + ); + }); + + it('reports the restored key with file count and size when a cache entry matched', async () => { + process.env.METRO_RESTORED_KEY = 'harness-metro-linux-abc-contenthash'; + snapshotMock.mockReturnValue({ + metro: [ + { path: 'metro/a', sizeBytes: 1024 * 1024, mtimeMs: 1 }, + { path: 'metro/b', sizeBytes: 512 * 1024, mtimeMs: 1 }, + ], + }); + + await import('../snapshot-metro.js'); + + await vi.waitFor(() => { + expect(snapshotMock).toHaveBeenCalled(); + }); + + expect(console.info).toHaveBeenCalledWith( + 'Metro cache: restored from key "harness-metro-linux-abc-contenthash" (2 files, 1.5 MB).' + ); + }); + + it('reports a cold start when no cache entry matched', async () => { + delete process.env.METRO_RESTORED_KEY; + + await import('../snapshot-metro.js'); + + await vi.waitFor(() => { + expect(snapshotMock).toHaveBeenCalled(); + }); + + expect(console.info).toHaveBeenCalledWith( + 'Metro cache: no existing cache entry matched — starting cold.' + ); + }); + + it('fails loudly (process.exit(1)) when GITHUB_OUTPUT is not set', async () => { + delete process.env.GITHUB_OUTPUT; + + await import('../snapshot-metro.js'); + + await vi.waitFor(() => { + expect(process.exit).toHaveBeenCalledWith(1); + }); + }); +}); diff --git a/packages/github-action/src/shared/format-bytes.ts b/packages/github-action/src/shared/format-bytes.ts new file mode 100644 index 00000000..143de54b --- /dev/null +++ b/packages/github-action/src/shared/format-bytes.ts @@ -0,0 +1,6 @@ +/** + * Human-readable size for single-line step-log messages, e.g. "12.3 MB". + * Always megabytes so the unit is stable and the lines stay grep-friendly. + */ +export const formatMegabytes = (bytes: number): string => + `${(bytes / (1024 * 1024)).toFixed(1)} MB`; diff --git a/packages/github-action/src/shared/metro-cache-inputs.ts b/packages/github-action/src/shared/metro-cache-inputs.ts new file mode 100644 index 00000000..e03f04f4 --- /dev/null +++ b/packages/github-action/src/shared/metro-cache-inputs.ts @@ -0,0 +1,100 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { computeMetroStaticInputs } from '@react-native-harness/cache'; + +const FALLBACK_BUNDLER_METRO_VERSION = 'unknown'; + +/** + * Lockfiles in a monorepo often live above the Harness projectRoot, so the + * Metro key recipe walks the ancestor chain upward from projectRoot. The git + * repository root is the upper boundary of that walk when discoverable, + * ensuring monorepo-root lockfiles above projectRoot are included. Falls + * back to projectRoot itself for shallow/sparse checkouts with no `.git` + * (limiting the walk to projectRoot alone). + */ +export const resolveRepoRoot = (projectRoot: string): string => { + let dir = projectRoot; + + while (true) { + if (fs.existsSync(path.join(dir, '.git'))) { + return dir; + } + + const parent = path.dirname(dir); + if (parent === dir) { + return projectRoot; + } + + dir = parent; + } +}; + +/** + * `@react-native-harness/bundler-metro` is rarely a direct dependency of the + * consuming project — it's pulled in transitively through + * `@react-native-harness/jest` (the jest preset every Harness project + * installs directly). Package managers that don't hoist nested/transitive + * workspace deps to the project root (e.g. pnpm) can only resolve it by + * walking through `@react-native-harness/jest`'s own node_modules, so that's + * tried as a fallback resolution root. + */ +const resolveBundlerMetroPackageJson = (projectRoot: string): string => { + try { + return require.resolve( + '@react-native-harness/bundler-metro/package.json', + { paths: [projectRoot] } + ); + } catch { + const jestPackageJsonPath = require.resolve( + '@react-native-harness/jest/package.json', + { paths: [projectRoot] } + ); + return require.resolve('@react-native-harness/bundler-metro/package.json', { + paths: [path.dirname(jestPackageJsonPath)], + }); + } +}; + +/** + * Resolved from the consuming project's own installed copy of + * `@react-native-harness/bundler-metro`, since that's the actual Metro + * integration whose behavior determines cache validity for that project. + * Never throws: a broken/missing resolution degrades to a stable + * placeholder rather than aborting the CI step. + */ +export const resolveBundlerMetroVersion = (projectRoot: string): string => { + try { + const packageJsonPath = resolveBundlerMetroPackageJson(projectRoot); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + if (typeof packageJson.version !== 'string') { + throw new Error('"version" field is missing or not a string'); + } + + return packageJson.version; + } catch (error) { + console.warn( + `Failed to resolve @react-native-harness/bundler-metro's version from "${projectRoot}". ` + + `Falling back to "${FALLBACK_BUNDLER_METRO_VERSION}".`, + error + ); + return FALLBACK_BUNDLER_METRO_VERSION; + } +}; + +/** + * Shared by plan-restore and plan-save so both steps derive the exact same + * staticInputs from the same recipe. computeMetroStaticInputs is + * deterministic given the same inputs, so recomputing it in the later + * plan-save step (rather than threading it through GITHUB_OUTPUT) is safe. + */ +export const resolveMetroStaticInputs = (options: { + projectRoot: string; + cacheVersionSalt?: string; +}): Record => + computeMetroStaticInputs({ + projectRoot: options.projectRoot, + repoRoot: resolveRepoRoot(options.projectRoot), + bundlerMetroVersion: resolveBundlerMetroVersion(options.projectRoot), + salt: options.cacheVersionSalt, + }); diff --git a/packages/github-action/src/shared/plan-restore.ts b/packages/github-action/src/shared/plan-restore.ts new file mode 100644 index 00000000..67691bef --- /dev/null +++ b/packages/github-action/src/shared/plan-restore.ts @@ -0,0 +1,69 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { createHarnessCache } from '@react-native-harness/cache'; +import { getConfig } from '@react-native-harness/config'; +import { resolveMetroStaticInputs } from './metro-cache-inputs.js'; + +const run = async (): Promise => { + try { + const projectRootInput = process.env.INPUT_PROJECTROOT; + + const projectRoot = projectRootInput + ? path.resolve(projectRootInput) + : process.cwd(); + + console.info(`Planning Metro cache restore for: ${projectRoot}`); + + const { config, projectRoot: resolvedProjectRoot } = await getConfig( + projectRoot + ); + + const githubOutput = process.env.GITHUB_OUTPUT; + if (!githubOutput) { + throw new Error('GITHUB_OUTPUT environment variable is not set'); + } + + const staticInputs = resolveMetroStaticInputs({ + projectRoot: resolvedProjectRoot, + cacheVersionSalt: config.cache?.version, + }); + + const cache = createHarnessCache({ projectRoot: resolvedProjectRoot }); + const os = process.env.RUNNER_OS ?? process.platform; + + const restorePlan = cache.planRestore({ + metro: { os, staticInputs }, + }); + cache.writeKeysFile(restorePlan); + + const metroPlan = restorePlan.domains.metro; + + // Structured values are passed through GITHUB_OUTPUT as single-line JSON + // strings, matching the `config=${JSON.stringify(...)}` convention + // already used by shared/index.ts (parsed on the YAML side via + // `fromJson(...)`). + // + // The pre-run cache snapshot is deliberately NOT taken here: this step + // runs before the actual `actions/cache/restore` step, so the cache + // directories are still empty at this point. It's captured by a + // separate snapshot-metro step that runs after the restore action + // instead (see snapshot-metro.ts). + const output = + `metroRestoreKey=${metroPlan?.restoreKey ?? ''}\n` + + `metroRestorePrefixes=${JSON.stringify( + metroPlan?.restorePrefixes ?? [] + )}\n`; + + fs.appendFileSync(githubOutput, output); + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } else { + console.error('Failed to plan Metro cache restore'); + } + + process.exit(1); + } +}; + +run(); diff --git a/packages/github-action/src/shared/plan-save.ts b/packages/github-action/src/shared/plan-save.ts new file mode 100644 index 00000000..8c071987 --- /dev/null +++ b/packages/github-action/src/shared/plan-save.ts @@ -0,0 +1,137 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { + createHarnessCache, + type CacheSnapshot, + type DomainSavePlan, + type SavePolicy, +} from '@react-native-harness/cache'; +import { getConfig } from '@react-native-harness/config'; +import { formatMegabytes } from './format-bytes.js'; +import { resolveMetroStaticInputs } from './metro-cache-inputs.js'; + +const parseSnapshot = (raw: string | undefined): CacheSnapshot => { + if (!raw) { + return {}; + } + + try { + return JSON.parse(raw) as CacheSnapshot; + } catch (error) { + console.warn( + 'Failed to parse the metroSnapshot input. Treating the pre-run cache state as empty.', + error + ); + return {}; + } +}; + +const parseSavePolicyMode = ( + raw: string | undefined +): SavePolicy['mode'] => { + if (raw === 'always' || raw === 'never' || raw === 'default-branch') { + return raw; + } + + return 'default-branch'; +}; + +/** + * Single-line, grep-friendly job-log report of the save decision and its + * reason, so a skipped "Save Metro cache" step is never a mystery: it + * distinguishes "nothing changed" from "changed but blocked by policy". + */ +const logMetroSaveDecision = ( + plan: DomainSavePlan | undefined, + policy: SavePolicy +): void => { + if (!plan) { + return; + } + + const { delta, shouldSave, saveKey } = plan; + + if (delta.addedEntries + delta.removedEntries === 0) { + console.info('Metro cache: unchanged — skipping save.'); + return; + } + + const change = `+${delta.addedEntries} files / ${formatMegabytes(delta.addedBytes)} added, ${delta.removedEntries} removed`; + + if (shouldSave) { + console.info( + `Metro cache: changed (${change}) — saving new entry with key "${saveKey}".` + ); + return; + } + + const reason = + policy.mode === 'default-branch' + ? 'policy "default-branch" (current branch is not the default branch)' + : `policy "${policy.mode}"`; + console.info( + `Metro cache: changed (${change}) but save skipped by ${reason}.` + ); +}; + +const run = async (): Promise => { + try { + const projectRootInput = process.env.INPUT_PROJECTROOT; + + const projectRoot = projectRootInput + ? path.resolve(projectRootInput) + : process.cwd(); + + console.info(`Planning Metro cache save for: ${projectRoot}`); + + const { config, projectRoot: resolvedProjectRoot } = await getConfig( + projectRoot + ); + + const githubOutput = process.env.GITHUB_OUTPUT; + if (!githubOutput) { + throw new Error('GITHUB_OUTPUT environment variable is not set'); + } + + // Recomputed rather than threaded through GITHUB_OUTPUT from + // plan-restore: computeMetroStaticInputs is deterministic given the same + // repoRoot/bundlerMetroVersion/salt, so this step can run independently. + const staticInputs = resolveMetroStaticInputs({ + projectRoot: resolvedProjectRoot, + cacheVersionSalt: config.cache?.version, + }); + + const before = parseSnapshot(process.env.INPUT_METRO_SNAPSHOT); + const policy: SavePolicy = { + mode: parseSavePolicyMode(process.env.INPUT_CACHESAVEPOLICY), + isDefaultBranch: process.env.IS_DEFAULT_BRANCH === 'true', + }; + + const cache = createHarnessCache({ projectRoot: resolvedProjectRoot }); + const os = process.env.RUNNER_OS ?? process.platform; + + const savePlan = cache.planSave(before, policy, { + metro: { os, staticInputs }, + }); + cache.writeKeysFile(savePlan); + + const metroPlan = savePlan.domains.metro; + logMetroSaveDecision(metroPlan, policy); + + const output = + `metroShouldSave=${metroPlan?.shouldSave ? 'true' : 'false'}\n` + + `metroSaveKey=${metroPlan?.saveKey ?? ''}\n`; + + fs.appendFileSync(githubOutput, output); + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } else { + console.error('Failed to plan Metro cache save'); + } + + process.exit(1); + } +}; + +run(); diff --git a/packages/github-action/src/shared/snapshot-metro.ts b/packages/github-action/src/shared/snapshot-metro.ts new file mode 100644 index 00000000..281ad55c --- /dev/null +++ b/packages/github-action/src/shared/snapshot-metro.ts @@ -0,0 +1,69 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { createHarnessCache } from '@react-native-harness/cache'; +import { getConfig } from '@react-native-harness/config'; +import { formatMegabytes } from './format-bytes.js'; + +/** + * Must run AFTER the "Restore Metro cache" step and BEFORE the test run, so + * this snapshot reflects exactly what was restored from a prior save (empty + * on a cache miss) -- not the pre-restore, always-empty state of a fresh + * checkout. plan-save.ts diffs its own post-run snapshot against this one, so + * capturing it too early would make every run look like it added the whole + * restored cache, defeating the "only save when content actually changed" + * policy. + */ +const run = async (): Promise => { + try { + const projectRootInput = process.env.INPUT_PROJECTROOT; + + const projectRoot = projectRootInput + ? path.resolve(projectRootInput) + : process.cwd(); + + console.info(`Snapshotting Metro cache for: ${projectRoot}`); + + const { projectRoot: resolvedProjectRoot } = await getConfig(projectRoot); + + const githubOutput = process.env.GITHUB_OUTPUT; + if (!githubOutput) { + throw new Error('GITHUB_OUTPUT environment variable is not set'); + } + + const cache = createHarnessCache({ projectRoot: resolvedProjectRoot }); + const snapshotAfterRestore = cache.snapshot(); + + // actions/cache/restore's cache-matched-key output, threaded in via the + // step env; empty on a cache miss. This makes the job log state plainly + // whether the Metro cache was actually restored. + const restoredKey = process.env.METRO_RESTORED_KEY; + if (restoredKey) { + const metroEntries = snapshotAfterRestore.metro ?? []; + const totalBytes = metroEntries.reduce( + (sum, entry) => sum + entry.sizeBytes, + 0 + ); + console.info( + `Metro cache: restored from key "${restoredKey}" (${metroEntries.length} files, ${formatMegabytes(totalBytes)}).` + ); + } else { + console.info( + 'Metro cache: no existing cache entry matched — starting cold.' + ); + } + + const output = `metroSnapshot=${JSON.stringify(snapshotAfterRestore)}\n`; + + fs.appendFileSync(githubOutput, output); + } catch (error) { + if (error instanceof Error) { + console.error(error.message); + } else { + console.error('Failed to snapshot the Metro cache'); + } + + process.exit(1); + } +}; + +run(); diff --git a/packages/github-action/tsconfig.json b/packages/github-action/tsconfig.json index 399ec7ae..0fe47aad 100644 --- a/packages/github-action/tsconfig.json +++ b/packages/github-action/tsconfig.json @@ -4,11 +4,20 @@ "include": [], "references": [ { - "path": "../config" + "path": "../jest" + }, + { + "path": "../bundler-metro" }, { "path": "../platform-android" }, + { + "path": "../config" + }, + { + "path": "../cache" + }, { "path": "./tsconfig.lib.json" } diff --git a/packages/github-action/tsconfig.lib.json b/packages/github-action/tsconfig.lib.json index 8459915f..c497d568 100644 --- a/packages/github-action/tsconfig.lib.json +++ b/packages/github-action/tsconfig.lib.json @@ -12,10 +12,19 @@ "include": ["src/**/*.ts"], "references": [ { - "path": "../config/tsconfig.lib.json" + "path": "../jest/tsconfig.lib.json" + }, + { + "path": "../bundler-metro/tsconfig.lib.json" }, { "path": "../platform-android/tsconfig.lib.json" + }, + { + "path": "../config/tsconfig.lib.json" + }, + { + "path": "../cache/tsconfig.lib.json" } ] } diff --git a/packages/github-action/tsup.config.mts b/packages/github-action/tsup.config.mts index db836be2..47cc3626 100644 --- a/packages/github-action/tsup.config.mts +++ b/packages/github-action/tsup.config.mts @@ -15,6 +15,9 @@ export default defineConfig({ TARGETS.map((target) => [`${target}/index`, `src/${target}/index.ts`]) ), 'shared/index': 'src/shared/index.ts', + 'shared/plan-restore': 'src/shared/plan-restore.ts', + 'shared/snapshot-metro': 'src/shared/snapshot-metro.ts', + 'shared/plan-save': 'src/shared/plan-save.ts', }, outDir: OUT_DIR, format: 'cjs', diff --git a/packages/github-action/vite.config.ts b/packages/github-action/vite.config.ts new file mode 100644 index 00000000..9db8823c --- /dev/null +++ b/packages/github-action/vite.config.ts @@ -0,0 +1,18 @@ +/// +import { defineConfig } from 'vite'; + +export default defineConfig(() => ({ + root: __dirname, + cacheDir: '../../node_modules/.vite/packages/github-action', + test: { + watch: false, + globals: true, + environment: 'node', + include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + reporters: ['default'], + coverage: { + reportsDirectory: './test-output/vitest/coverage', + provider: 'v8' as const, + }, + }, +})); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 239dda3c..f060f58e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -371,6 +371,9 @@ importers: packages/github-action: dependencies: + '@react-native-harness/cache': + specifier: workspace:* + version: link:../cache '@react-native-harness/config': specifier: workspace:* version: link:../config diff --git a/website/src/docs/guides/ci-cd.md b/website/src/docs/guides/ci-cd.md index ae17381f..982637e4 100644 --- a/website/src/docs/guides/ci-cd.md +++ b/website/src/docs/guides/ci-cd.md @@ -45,6 +45,7 @@ The action accepts the following inputs: - `cacheAvd` (optional, Android only): Whether to cache the Android Virtual Device snapshot. Defaults to `true`. This is most useful when your Android runner defines AVD details in `rn-harness.config.mjs`. - `preRunHook` (optional): Inline shell script run in `bash` immediately before Harness starts - `afterRunHook` (optional): Inline shell script run in `bash` immediately after Harness finishes and before artifacts are uploaded +- `cacheSavePolicy` (optional): When to save a new Metro cache entry -- `default-branch` (default), `always`, or `never`. See [Metro cache](#metro-cache) below ## Crash Artifacts @@ -249,7 +250,17 @@ If your workflow does not define AVD details, the action can still run the tests React Native Harness persists Metro's transform cache under `.harness/cache/metro` and its file-map (haste) cache under `.harness/cache/metro-file-map`, both in your project root. This is on by default (`cache.metro: true`); set `cache.metro: false` in config to disable it. The deprecated `unstable__enableMetroCache` option is still honored as an alias for one release. -When you use the `callstackincubator/react-native-harness` GitHub Action, Metro cache restoration and saving is handled automatically for the resolved `projectRoot`. You do not need to add a separate `actions/cache` step for `.harness/cache/metro` or `.harness/cache/metro-file-map`. +When you use the `callstackincubator/react-native-harness` GitHub Action, Metro cache restoration and saving is handled automatically for the resolved `projectRoot`. Do not add your own `actions/cache` step for `.harness/cache/metro` or `.harness/cache/metro-file-map` -- the action's cache key scheme assumes it's the only thing writing and reading those entries, and a manually-added step would fight it for ownership of the same paths. + +The cache key is computed by Harness itself, not a static file list: it hashes your lockfile(s) and Metro/Babel config together with the resolved `@react-native-harness/bundler-metro` version and your `cache.version` salt. This means the cache invalidates automatically whenever you upgrade `@react-native-harness/bundler-metro`, not only when a lockfile or config file changes. Key files are discovered in `projectRoot` and its ancestor directories up to the repository root -- so in a monorepo the root lockfile is included, while sibling packages' Metro/Babel configs never affect your project's cache key. + +Use the `cacheSavePolicy` input to control when a new cache entry is saved: + +- `default-branch` (default): only save from your default branch, so pull request runs restore from the shared cache but never write to it. This keeps PR runs cache-neutral and avoids cache churn from short-lived branches. +- `always`: save a new entry from every run that changed the cache contents, regardless of branch. Useful if you want branches to build on each other's cache, at the cost of more cache writes and storage churn. +- `never`: never save, only restore. Useful if you manage the Metro cache some other way and only want the action to consume it. + +A new entry is only saved when the run actually changed the cache's contents -- an unchanged, fully cache-hit run does not create a redundant save. ## Web in CI