import { a as langFromFile, c as resolvePath, d as withoutQuery, i as isFilePath, l as stripCacheQuery, n as displayPath, o as normalizeSlashes, r as hasScheme, s as relativeToCwd, t as dirname, u as toPath } from "./path-BUhlDN52.mjs"; import { Buffer } from "node:buffer"; import { existsSync, readFileSync, realpathSync, statSync } from "node:fs"; import { readFile } from "node:fs/promises"; import process from "node:process"; import { parseRawStackTrace } from "errx"; import { fnv1a64Base36 } from "fnv1a-64"; //#region src/loaders/decode.ts /** * Sources of an index map's sections are named with the section's * `sourceRoot`, which the top-level resolution never sees. */ function sectionRoot(map, parent) { return map.sourceRoot ? `${parent}${map.sourceRoot.replace(/\/?$/, "/")}` : parent; } const BASE64 = (/* @__PURE__ */ new Int8Array(128)).fill(-1); for (const [i, c] of [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"].entries()) BASE64[c.charCodeAt(0)] = i; function decodeMappings(mappings, raw, root) { const entries = []; const state = [ 0, 0, 0, 0, 0 ]; let line = 0; let i = 0; while (i < mappings.length) { const char = mappings[i]; if (char === ";") { line++; state[0] = 0; i++; continue; } if (char === ",") { i++; continue; } let field = 0; const fields = []; while (i < mappings.length && mappings[i] !== "," && mappings[i] !== ";") { let value = 0; let shift = 0; let digit; do { digit = BASE64[mappings.charCodeAt(i++)] ?? -1; if (digit < 0) throw new SyntaxError("Invalid source map mappings"); value += (digit & 31) << shift; shift += 5; } while (digit & 32); const next = (state[field] ?? 0) + (value & 1 ? -(value >>> 1) : value >>> 1); state[field] = next; fields.push(next); field++; } const entry = { generatedLine: line, generatedColumn: fields[0] }; if (fields.length >= 4) { const source = raw.sources?.[fields[1]]; entry.originalSource = typeof source === "string" ? root ? `${root}${source}` : source : void 0; entry.originalLine = fields[2]; entry.originalColumn = fields[3]; } entries.push(entry); } return entries; } function decodeEntries(raw, root = "") { if (!raw.sections) return decodeMappings(raw.mappings ?? "", raw, root); return raw.sections.flatMap(({ offset, map }) => decodeEntries(map, sectionRoot(map, root)).map((entry) => ({ ...entry, generatedLine: entry.generatedLine + offset.line, generatedColumn: entry.generatedColumn + (entry.generatedLine === 0 ? offset.column : 0) }))); } function collectSources(raw, root, into) { if (raw.sections) { for (const { map } of raw.sections) collectSources(map, sectionRoot(map, root), into); return into; } const ignoreList = raw.ignoreList ?? raw.x_google_ignoreList; for (const [index, source] of (raw.sources ?? []).entries()) { if (typeof source !== "string") continue; const name = `${root}${source}`; const content = raw.sourcesContent?.[index]; if (typeof content === "string" && content && !into.contents.has(name)) into.contents.set(name, content); if (ignoreList?.includes(index)) into.ignored.add(name); } return into; } function decodeSourceMap(raw) { const entries = decodeEntries(raw).sort((a, b) => a.generatedLine - b.generatedLine || a.generatedColumn - b.generatedColumn); let sources; const collected = () => sources ??= collectSources(raw, "", { contents: /* @__PURE__ */ new Map(), ignored: /* @__PURE__ */ new Set() }); return { sourceContent: (source) => collected().contents.get(source), ignoredSource: (source) => collected().ignored.has(source), findEntry(line, column) { let lo = 0; let hi = entries.length - 1; let found; while (lo <= hi) { const mid = lo + hi >>> 1; const entry = entries[mid]; if (entry.generatedLine < line || entry.generatedLine === line && entry.generatedColumn <= column) { found = entry; lo = mid + 1; } else hi = mid - 1; } return found ?? {}; } }; } //#endregion //#region src/loaders/embedded.ts /** Original source contents that a loader found embedded in the map it used for a frame, with the file they belong to. */ const embedded = /* @__PURE__ */ new WeakMap(); function withEmbeddedSource(frame, contents) { if (contents) embedded.set(frame, { file: frame.file, contents }); return frame; } /** Only while the frame still names the file the contents were recorded for, in case a later loader rewrote it in place. */ function embeddedSource(frame) { const entry = embedded.get(frame); return entry && entry.file === frame.file ? entry.contents : void 0; } //#endregion //#region src/loaders/sourcemap.ts /** * Look up a 1-based generated position in a map. Returns `undefined` unless the * map has a segment on that exact generated line: `findEntry` falls * back to the nearest preceding segment, which would map an already-original * position a second time. */ function findOriginal(map, line, column) { const entry = map.findEntry(line - 1, column === void 0 ? 0 : Math.max(0, column - 1)); if (!("originalSource" in entry) || entry.originalSource === void 0 || entry.originalLine === void 0 || entry.generatedLine !== line - 1) return; return { source: entry.originalSource, line: entry.originalLine + 1, column: (entry.originalColumn ?? 0) + 1 }; } /** Resolve a 1-based generated position through a map to an original file position. */ function mapPosition(map, raw, base, line, column) { return resolveOriginal(map, raw, base, line, column)?.position; } function resolveOriginal(map, raw, base, line, column) { const original = findOriginal(map, line, column); if (!original) return; const root = raw.sourceRoot ? raw.sourceRoot.replace(/\/?$/, "/") : ""; const ignoreList = raw.ignoreList ?? raw.x_google_ignoreList; const index = raw.sources?.indexOf(original.source) ?? -1; const ignored = index >= 0 ? ignoreList?.includes(index) : map.ignoredSource?.(original.source); return { position: { file: resolvePath(base, toPath(`${root}${original.source}`)), line: original.line, column: original.column, ...ignored && { ignored: true } }, source: original.source, index }; } /** The contents a map embeds for one of its sources, ignoring empty placeholders. */ function embeddedContent(map, raw, source, index) { const content = map.sourceContent ? map.sourceContent(source) : index >= 0 ? raw.sourcesContent?.[index] : void 0; return typeof content === "string" && content ? content : void 0; } /** Decoded maps by raw map object, shared by every loader instance since integrations commonly create loaders per request. */ const parsed$1 = /* @__PURE__ */ new WeakMap(); function parseMap(raw) { const existing = parsed$1.get(raw); if (existing !== void 0) return existing ?? void 0; try { const map = decodeSourceMap(raw); parsed$1.set(raw, map); return map; } catch { parsed$1.set(raw, null); } } /** * Maps frames with sourcemaps supplied by the caller. Snippets come from the * map's `sourcesContent` where it has them, otherwise from disk. */ function sourceMapLoader(options) { const embed = options.sourcesContent ?? true; const cache = /* @__PURE__ */ new Map(); async function load(file) { const raw = await options.getSourceMap(file); if (!raw || ("sections" in raw ? !Array.isArray(raw.sections) || !raw.sections.length : !raw.mappings)) return; const map = parseMap(raw); if (!map) return; return { map, raw: "sections" in raw ? {} : raw, base: options.base?.(file) ?? dirname(file) }; } async function loaded(file) { if (!options.getVersion) return load(file); const version = await options.getVersion(file); const entry = cache.get(file); if (entry && entry.version === version) return entry.value; const value = load(file); cache.set(file, { version, value }); return value; } return { name: "sourcemap", async map(frame) { if (!frame.file || frame.line === void 0) return; const result = await loaded(withoutQuery(frame.file)); if (!result) return; const resolved = resolveOriginal(result.map, result.raw, result.base, frame.line, frame.column); if (!resolved) return; const { ignored, ...position } = resolved.position; const mapped = { ...frame, ...position, ...ignored && { type: "vendor" }, compiled: frame.compiled ?? { file: frame.file, line: frame.line, column: frame.column } }; return (typeof embed === "function" ? embed(position.file) : embed) ? withEmbeddedSource(mapped, embeddedContent(result.map, result.raw, resolved.source, resolved.index)) : mapped; }, read(file) { const path = withoutQuery(file); if (options.fs === false || !isFilePath(path)) return; return readFile(path, "utf8").catch(() => void 0); }, async readCompiled(file) { return await options.getCode?.(withoutQuery(file)) ?? void 0; } }; } //#endregion //#region src/loaders/fs.ts const SOURCE_MAPPING_URL_MARKER = "sourceMappingURL="; const SOURCE_MAPPING_URL_PREFIX_RE = /\/\/[#@][^\S\n]*$/; /** Decode a `sourceMappingURL=data:` payload. */ function parseDataUrl(url) { if (!url.startsWith("data:")) return; const comma = url.indexOf(","); const meta = url.slice(5, comma); const data = url.slice(comma + 1); return meta.includes(";base64") ? Buffer.from(data, "base64").toString("utf8") : decodeURIComponent(data); } function sourceMappingURLIn(tail) { const at = tail.lastIndexOf(SOURCE_MAPPING_URL_MARKER); if (at < 0 || !SOURCE_MAPPING_URL_PREFIX_RE.test(tail.slice(0, at))) return; const url = tail.slice(at + 17).trimEnd(); return url && !/\s/.test(url) ? url : void 0; } /** * The comment is normally the final line, which for an inline map can be far * longer than any fixed tail window, so that line is checked first. */ function lastSourceMappingURL(code) { let end = code.length; while (end > 0 && (code.charCodeAt(end - 1) === 10 || code.charCodeAt(end - 1) === 13)) end--; const start = code.lastIndexOf("\n", end - 1) + 1; return sourceMappingURLIn(code.slice(start, end)) ?? sourceMappingURLIn(code.slice(-8192)); } /** * Decode the `sourceMappingURL=data:` comment of a module's transformed code, * for module runners that expose the code but not the parsed map. */ function parseInlineSourceMap(code) { const url = lastSourceMappingURL(code); const data = url ? parseDataUrl(url) : void 0; return data === void 0 ? void 0 : parse(data); } /** * Contents keyed by size and mtime, shared by every loader instance: integrations * commonly create loaders per request, and validation against `stat` keeps a * shared cache correct after a rebuild. */ const files = /* @__PURE__ */ new Map(); const MAX_FILES = 64; /** Lookups made in the same event-loop turn (the frames of one stack) share a single `stat`. */ let turn; function read(path) { if (!turn) { turn = /* @__PURE__ */ new Map(); setImmediate(() => { turn = void 0; }); } let pending = turn.get(path); if (!pending) { pending = readFresh(path); turn.set(path, pending); } return pending; } async function readFresh(path) { const info = statOrUndefined(path); if (!info?.isFile()) { files.delete(path); return; } const cached = files.get(path); if (cached && cached.size === info.size && cached.mtimeMs === info.mtimeMs) return cached; const contents = await readFile(path, "utf8").catch(() => void 0); if (contents === void 0) { files.delete(path); return; } const entry = { size: info.size, mtimeMs: info.mtimeMs, contents }; files.delete(path); if (files.size >= MAX_FILES) files.delete(files.keys().next().value); files.set(path, entry); return entry; } /** * Maps frames through `.map` sidecars or `sourceMappingURL` comments and reads * sources from disk. Node-only. */ function fsLoader(options = {}) { const { sidecar = true, inline = true, roots, canRead } = options; const linked = /* @__PURE__ */ new Map(); const bounds = roots?.map((root) => canonicalPath(root).replace(/\/$/, "")); /** The canonical path to read, or `undefined` when it is out of bounds. */ function allowed(path) { let target = path; if (bounds) { target = canonicalPath(path); if (!bounds.some((root) => target === root || target.startsWith(`${root}/`))) return; } return canRead && !canRead(path) ? void 0 : target; } const readAllowed = (path) => { const target = allowed(path); return target === void 0 ? Promise.resolve(void 0) : read(target); }; async function getSourceMap(file) { linked.delete(file); if (allowed(file) === void 0) return; const sidecarFile = sidecar ? await readAllowed(`${file}.map`) : void 0; if (sidecarFile) return parsed(sidecarFile); if (!inline) return; const source = await readAllowed(file); if (!source) return; source.url ??= lastSourceMappingURL(source.contents) ?? null; const url = source.url; if (!url) return; if (url.startsWith("data:")) return parsed(source, url); const mapPath = resolvePath(dirname(file), url); const linkedFile = await readAllowed(mapPath); linked.set(file, mapPath); if (linkedFile) return parsed(linkedFile); } const loader = sourceMapLoader({ getSourceMap, base: (file) => dirname(linked.get(file) ?? file), sourcesContent: bounds || canRead ? (file) => allowed(file) !== void 0 : true }); const readContents = async (file) => { const path = withoutQuery(file); return isFilePath(path) ? (await readAllowed(path))?.contents : void 0; }; return { ...loader, name: "fs", read: readContents, readCompiled: readContents }; } /** Lexical containment can be escaped through a symlink, so roots and candidates are compared as real paths. */ function canonicalPath(path) { try { return normalizePath(realpathSync(path)); } catch {} const normalized = normalizePath(path); const parent = dirname(normalized); if (parent === normalized || parent === ".") return normalized; const base = canonicalPath(parent); return `${base === "/" ? "" : base}/${normalized.split("/").pop()}`; } function normalizePath(path) { const normalized = path.replace(/\\/g, "/"); const segments = []; for (const part of normalized.split("/")) if (part === "..") segments.pop(); else if (part !== "." && part !== "") segments.push(part); return `${normalized.startsWith("/") ? "/" : ""}${segments.join("/")}`; } /** * A synchronous `stat` on the page cache costs a few microseconds, whereas the * promise-based one materialises an exception for every missing sidecar. */ function statOrUndefined(path) { try { return statSync(path, { throwIfNoEntry: false }); } catch { return; } } /** Decode a file's map once per version of its contents. */ function parsed(file, dataUrl) { if (file.map === void 0) { const data = dataUrl ? parseDataUrl(dataUrl) : file.contents; file.map = (data === void 0 ? void 0 : parse(data)) ?? null; } return file.map ?? void 0; } function parse(raw) { try { return JSON.parse(raw); } catch { return; } } //#endregion //#region src/report/message.ts const DEFAULT_DISPLAY_MESSAGE_LENGTH = 1e3; /** Split a message into the part shown by default and the remainder. */ function clampMessage(message, max = DEFAULT_DISPLAY_MESSAGE_LENGTH) { return message.length <= max ? { head: message, rest: "" } : { head: message.slice(0, max), rest: message.slice(max) }; } //#endregion //#region src/report/stringify.ts /** Render an arbitrary value as text, never throwing on circular or exotic input. */ function stringifyValue(value, indent) { if (typeof value === "string") return value; try { return JSON.stringify(value, null, indent) ?? String(value); } catch { return String(value); } } //#endregion //#region src/report/tokenize.ts const KEYWORDS = new Set("abstract as async await break case catch class const continue debugger default delete do else enum export extends false finally for from function if implements import in instanceof interface let new null of package private protected public return satisfies static super switch this throw true try type typeof undefined var void while with yield".split(" ")); /** * Comments end at a line terminator (`\n`, `\r`, U+2028, U+2029) or, for block * comments, at their closer; spelling that out as character classes instead of * `.*$` and lazy `[\s\S]*?` keeps the match linear for any input. */ const TOKEN_RE = /(\/\/[^\n\r\u2028\u2029]*|\/\*(?:(?!\*\/)[^\n\r\u2028\u2029])*(?:\*\/)?|") || text.endsWith("--!>")) ? void 0 : "html-comment"; if (text.startsWith("`")) return text.length >= 2 && text.endsWith("`") && !isEscaped(text, text.length - 1) ? void 0 : "template"; } /** * Tokenize one line, starting inside `state` when the previous line left a * block comment, HTML comment or template literal open. */ function tokenizeFrom(line, lang, state) { if (!lang || lang === "md") return { tokens: [{ type: "text", text: line }], state: void 0 }; const tokens = []; let last = 0; if (state) { const end = closeOf(line, state); const type = state === "template" ? "string" : "comment"; if (end < 0) return { tokens: line ? [{ type, text: line }] : [], state }; tokens.push({ type, text: line.slice(0, end) }); last = end; } let inTag = false; let open; TOKEN_RE.lastIndex = last; for (let match = TOKEN_RE.exec(line); match; match = TOKEN_RE.exec(line)) { if (match.index > last) tokens.push({ type: "text", text: line.slice(last, match.index) }); let text = match[0]; let group = 1; while (match[group] === void 0) group++; let type = GROUPS[group - 1]; open = void 0; if (group <= 2) { open = openedBy(text); if (open === "template") text = line.slice(match.index); else if (open && match.index + text.length < line.length) open = void 0; } else if (type === "tag") inTag = !text.endsWith(">"); else if (type === "variable") { if (inTag) type = "attribute"; else if (lang === "css" && line[match.index + text.length] === ":") type = "attribute"; else if (KEYWORDS.has(text)) type = "keyword"; else if (isUpper(text.charCodeAt(0))) type = "type"; else if (line[match.index + text.length] === "(") type = "function"; else type = "text"; } tokens.push({ type, text }); last = match.index + text.length; TOKEN_RE.lastIndex = last; } if (last < line.length) { tokens.push({ type: "text", text: line.slice(last) }); open = void 0; } return { tokens, state: open }; } /** Small built-in tokenizer for JS/TS/Vue/HTML/CSS snippets. */ const defaultTokenizer = (line, lang) => tokenizeFrom(line, lang, void 0).tokens; function isUpper(code) { return code >= 65 && code <= 90; } /** Lines of context before a snippet that are scanned for a construct it starts inside. */ const CONTEXT_LINES = 30; const CONTEXT_CHARS = 2400; const OPENER_RE = /\/\/|\/\*|