{
  "version": 3,
  "sources": ["safe-url.js"],
  "sourcesContent": ["/* SAFE LINK SCHEMES (launch review SEC-8, 2026-09-14).\n\n   esc() stops a value breaking out of an attribute. It does nothing about what\n   the attribute points at: a javascript:alert(1) href is perfectly escaped and\n   still runs when clicked, and data:, vbscript:, file: and blob: URLs are just\n   as dangerous. Links in our pages come from model output (report citations,\n   answers), crawled pages and customer input, so every dynamic href, src, form\n   action and navigation goes through safeHref() first. Its result still goes\n   through esc() (or attr()) when it lands in HTML; the two jobs stay separate.\n\n   Allowed: http and https, mailto only when the caller passes { mailto: true },\n   and same-origin relative references (\"/account/...\", \"#/q/3\", \"?id=1\",\n   \"page.html\"). Refused, so the fallback comes back instead: every other\n   scheme, protocol-relative and backslash hosts (\"//x\", \"/\\x\"), a colon before\n   the first \"/\", \"?\" or \"#\" that is not a known scheme, and anything the URL\n   parser rejects. { absolute: true } also refuses relative references, for\n   values that must name another site (a citation, a thumbnail).\n\n   The scheme is read from a normalized copy: HTML entities and percent-escapes\n   decoded (repeatedly), whitespace and control characters removed anywhere,\n   lowercased. So \" jAvAscript:\", \"java\\tscript:\", \"&#106;avascript:\" and\n   \"%6Aavascript:\" are all seen as javascript:. The value returned is the\n   caller's own string with the ends trimmed, never the normalized copy.\n\n   No imports: the browser loads this as /account/safe-url.js (shared.js\n   re-exports it next to esc()), and Pages Functions import it by relative\n   path. test/link-scheme-scan.test.js fails when a dynamic URL reaches a sink\n   without it. */\n\nconst ch = (cp) => String.fromCharCode(cp);\n\nconst NAMED = {\n  colon: \":\", tab: ch(9), newline: ch(10), sol: \"/\", bsol: ch(92), lpar: \"(\", rpar: \")\",\n  amp: \"&\", quot: ch(34), apos: \"'\", lt: \"<\", gt: \">\", nbsp: ch(0xa0), shy: ch(0xad),\n  period: \".\", comma: \",\", semi: \";\", excl: \"!\", num: \"#\", quest: \"?\", percnt: \"%\",\n  equals: \"=\", plus: \"+\", commat: \"@\", dollar: \"$\", lowbar: \"_\", hyphen: \"-\", dash: \"-\",\n};\n\n/* Character classes built from code points, so no invisible character ever sits\n   in this file's source. EDGE: whitespace, C0 and C1 controls and the Unicode\n   spaces a browser trims. INVISIBLE adds the joiners and fillers a browser or a\n   font may drop, removed anywhere in the copy we check. */\nconst hex4 = (cp) => \"\\\\u\" + cp.toString(16).padStart(4, \"0\");\nconst cls = (ranges) => \"[\" + ranges.map(([a, b = a]) => (a === b ? hex4(a) : hex4(a) + \"-\" + hex4(b))).join(\"\") + \"]\";\nconst EDGE_RANGES = [[0x00, 0x20], [0x7f, 0xa0], [0xad], [0x1680], [0x180e], [0x2000, 0x200f],\n  [0x2028, 0x202f], [0x205f, 0x206f], [0x3000], [0xfeff]];\nconst INVISIBLE_RANGES = [...EDGE_RANGES, [0x34f], [0x61c], [0x115f, 0x1160], [0x17b4, 0x17b5],\n  [0x180b, 0x180d], [0x3164], [0xfe00, 0xfe0f], [0xffa0], [0xfff0, 0xfff8]];\nconst EDGE = cls(EDGE_RANGES);\nconst INVISIBLE = new RegExp(cls(INVISIBLE_RANGES), \"g\");\nconst EDGES = new RegExp(`^${EDGE}+|${EDGE}+$`, \"g\");\nconst BASE = \"https://base.invalid/\";\n\nfunction decodeEntities(s) {\n  return s.replace(/&(#x[0-9a-f]+|#[0-9]+|[a-z][a-z0-9]*);?/gi, (m, body) => {\n    if (body[0] === \"#\") {\n      const hex = body[1] === \"x\" || body[1] === \"X\";\n      const cp = parseInt(body.slice(hex ? 2 : 1), hex ? 16 : 10);\n      return cp >= 0 && cp <= 0x10ffff ? String.fromCodePoint(cp) : \"\";\n    }\n    const k = body.toLowerCase();\n    return Object.prototype.hasOwnProperty.call(NAMED, k) ? NAMED[k] : m;\n  });\n}\n\nconst decodePercent = (s) => s.replace(/%([0-9a-f]{2})/gi, (_, h) => String.fromCharCode(parseInt(h, 16)));\n\n/* The string a browser could read a scheme from, as conservatively as we can\n   read it. Exported for the tests. */\nexport function normalizeUrlForCheck(value) {\n  let s = String(value ?? \"\");\n  for (let i = 0; i < 6; i++) {\n    const next = decodeEntities(decodePercent(s));\n    if (next === s) break;\n    s = next;\n  }\n  return s.replace(INVISIBLE, \"\").toLowerCase();\n}\n\n/* `value` if it is a link we allow, otherwise `fallback` (\"#\" unless given). */\nexport function safeHref(value, { mailto = false, absolute = false, fallback = \"#\" } = {}) {\n  if (value == null) return fallback;\n  const raw = String(value).replace(EDGES, \"\");\n  if (!raw) return fallback;\n  const n = normalizeUrlForCheck(raw);\n  if (!n) return fallback;\n  const m = /^([a-z][a-z0-9+.-]*):/.exec(n);\n  if (m) {\n    const scheme = m[1];\n    if (!(scheme === \"http\" || scheme === \"https\" || (mailto && scheme === \"mailto\"))) return fallback;\n    try {\n      const u = new URL(raw);\n      if (u.protocol !== `${scheme}:`) return fallback;\n    } catch { return fallback; }\n    return raw;\n  }\n  if (absolute) return fallback;\n  // No scheme: it must be a same-origin reference. A colon ahead of the first\n  // \"/\", \"?\" or \"#\" is either a scheme we could not read or a path a browser\n  // might; neither is worth the guess.\n  const head = n.split(/[/?#]/, 1)[0];\n  if (head.includes(\":\")) return fallback;\n  if (/^[\\\\/]{2}/.test(n)) return fallback;\n  try {\n    const u = new URL(raw, BASE);\n    if (u.origin !== new URL(BASE).origin) return fallback;\n  } catch { return fallback; }\n  return raw;\n}\n\n// A link from our screens to a page on a client's store (or a site we publish to), opened by the team: ?rl-op=1 makes\n// the page show this browser an Edit button back to its room (rl.js, rl-op.js; the site Worker on Cloudflare sites).\nexport function opHref(value) {\n  try { const u = new URL(String(value || \"\")); if (!/^https?:$/.test(u.protocol)) return value; u.searchParams.set(\"rl-op\", \"1\"); return u.href; }\n  catch { return value; }\n}\n"],
  "mappings": "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",
  "names": ["ch", "cp", "NAMED", "hex4", "cls", "ranges", "a", "b", "EDGE_RANGES", "INVISIBLE_RANGES", "EDGE", "INVISIBLE", "EDGES", "BASE", "decodeEntities", "s", "m", "body", "hex", "k", "decodePercent", "_", "h", "value", "i", "next", "mailto", "absolute", "fallback", "raw", "n", "scheme", "u"]
}
