import { captureStackTrace } from "errx"; import { isScriptProtocol } from "ufo"; //#region src/app/utils.ts const PROBE_BASE_A = "https://a.invalid/probe/"; const PROBE_BASE_B = "https://b.invalid/probe/"; function parseAnchorURL(value, base = PROBE_BASE_A) { try { return new URL(value, base); } catch { return null; } } function resolvesWithoutBase(value) { const resolved = parseAnchorURL(value); return !!resolved && resolved.href === parseAnchorURL(value, PROBE_BASE_B)?.href; } /** Whether a URL string is a path rooted at the current origin, so carries no protocol. */ function isRootedPath(value) { if (value[0] !== "/" || value[1] === "/" || value[1] === "\\") return false; return value[1] !== " " && value[1] !== "\n" && value[1] !== "\r" || !resolvesWithoutBase(value); } /** Whether a URL string carries its own protocol or authority. */ function isAbsoluteHref(value) { return !!value && !isRootedPath(value) && resolvesWithoutBase(value); } /** The script-capable protocol a URL string would navigate to, or `null` when it is safe. */ function getScriptProtocol(value) { if (!value || isRootedPath(value)) return null; let resolved = parseAnchorURL(value.replace(/[\u0000-\u001F\u007F\s]+/g, "")); while (resolved?.protocol === "view-source:") resolved = parseAnchorURL(resolved.pathname); if (!resolved) return ""; return isScriptProtocol(resolved.protocol) ? resolved.protocol : null; } /** Returns the value unchanged when safe to use as an anchor `href`, or `null`. */ function sanitizeAnchorHref(value) { return getScriptProtocol(value) === null ? value : null; } /** @since 3.9.0 */ function toArray(value) { return Array.isArray(value) ? value : [value]; } const BOT_RE = /bot\b|chrome-lighthouse|facebookexternalhit|google\b/i; function isBotUserAgent(userAgent) { return BOT_RE.test(userAgent); } const distURL = import.meta.dev ? import.meta.url.replace(/\/app\/.*$/, "/") : ""; function getUserTrace() { if (!import.meta.dev) return []; const trace = captureStackTrace(); const start = trace.findIndex((entry) => !entry.source.startsWith(distURL)); const end = trace.findLastIndex((entry) => !entry.source.includes("node_modules") && !entry.source.startsWith(distURL)); if (start === -1 || end === -1) return []; return trace.slice(start, end + 1).map((i) => ({ ...i, source: i.source.replace(/^file:\/\//, "") })); } function getUserCaller() { if (!import.meta.dev) return null; const { source, line, column } = captureStackTrace().find((entry) => !entry.source.startsWith(distURL)) ?? {}; if (!source) return null; return { source: source.replace(/^file:\/\//, ""), line, column }; } //#endregion export { getScriptProtocol, getUserCaller, getUserTrace, isAbsoluteHref, isBotUserAgent, isRootedPath, sanitizeAnchorHref, toArray };