@axis-network/butter-col 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -2
  3. package/bin/butter-col.js +4 -0
  4. package/dist/bootstrap.html +67 -0
  5. package/dist/client.js +2 -0
  6. package/dist/client.js.map +7 -0
  7. package/dist/controller.mjs +2 -0
  8. package/dist/controller.mjs.map +7 -0
  9. package/dist/formatter.mjs +2 -0
  10. package/dist/formatter.mjs.map +7 -0
  11. package/dist/sw.js +30 -0
  12. package/dist/sw.js.map +7 -0
  13. package/dist/transports/butter.js +2 -0
  14. package/dist/transports/butter.js.map +7 -0
  15. package/dist/types/controller/index.d.ts +65 -0
  16. package/dist/types/formatter/codecs.d.ts +17 -0
  17. package/dist/types/formatter/css.d.ts +10 -0
  18. package/dist/types/formatter/html.d.ts +61 -0
  19. package/dist/types/formatter/index.d.ts +77 -0
  20. package/dist/types/formatter/js.d.ts +27 -0
  21. package/dist/types/formatter/url.d.ts +40 -0
  22. package/lib/cli.js +257 -0
  23. package/lib/index.d.ts +68 -0
  24. package/lib/index.js +166 -0
  25. package/package.json +77 -4
  26. package/server/conf/butter-proxy.conf.template +29 -0
  27. package/server/conf/mime.types +19 -0
  28. package/server/conf/nginx.conf.template +152 -0
  29. package/server/config.lua +34 -0
  30. package/server/lua/butter/config.lua +160 -0
  31. package/server/lua/butter/cookiejar.lua +197 -0
  32. package/server/lua/butter/dns.lua +67 -0
  33. package/server/lua/butter/guard.lua +78 -0
  34. package/server/lua/butter/headers.lua +83 -0
  35. package/server/lua/butter/init.lua +18 -0
  36. package/server/lua/butter/ip.lua +165 -0
  37. package/server/lua/butter/limit.lua +18 -0
  38. package/server/lua/butter/session.lua +42 -0
  39. package/server/lua/butter/store.lua +77 -0
  40. package/server/lua/butter/transport.lua +248 -0
  41. package/server/lua/butter/url.lua +78 -0
  42. package/site/app.css +373 -0
  43. package/site/app.js +335 -0
  44. package/site/butter-col.png +0 -0
  45. package/site/index.html +139 -0
package/dist/sw.js.map ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../butter-transport/src/errors.ts", "../../butter-transport/src/util.ts", "../../butter-transport/src/index.ts", "../src/formatter/codecs.js", "../src/formatter/url.js", "../src/formatter/css.js", "../src/formatter/js.js", "../src/formatter/html.js", "../src/formatter/index.js", "../src/sw/config.ts", "../src/sw/route.ts", "../src/sw/request.ts", "../src/sw/charset.ts", "../src/sw/response.ts", "../src/sw/error-page.ts", "../src/sw/cookie-jar.ts", "../src/sw/transports.ts", "../src/sw/index.ts"],
4
+ "sourcesContent": ["/**\n * Error codes butter-col understands. Transports may use their own codes too\n * (prefix them, e.g. \"BARE_...\"); they are shown to the visitor as-is.\n */\nexport const TRANSPORT_ERROR_CODES = {\n /** The transport's backend could not be reached. */\n UNREACHABLE: \"TRANSPORT_UNREACHABLE\",\n /** The backend answered with something the transport could not understand. */\n BAD_RESPONSE: \"BAD_TRANSPORT_RESPONSE\",\n /** The target's host name did not resolve. */\n DNS_FAILURE: \"DNS_FAILURE\",\n /** The target refused or reset the connection, or TLS failed. */\n UPSTREAM_UNREACHABLE: \"UPSTREAM_UNREACHABLE\",\n /** The target did not answer in time. */\n UPSTREAM_TIMEOUT: \"UPSTREAM_TIMEOUT\",\n /** The backend refused this target (policy). */\n BLOCKED: \"BLOCKED\",\n /** Anything else. */\n ERROR: \"TRANSPORT_ERROR\",\n} as const;\n\n/**\n * Throw this from request() to give the visitor a meaningful error page.\n * `status` is the HTTP status of that page (502 by default).\n */\nexport class TransportError extends Error {\n readonly code: string;\n readonly status: number;\n\n constructor(code: string, message: string, status = 502) {\n super(message);\n this.name = \"TransportError\";\n this.code = code;\n this.status = status;\n }\n\n /** Errors cross realms (service worker \u2192 page); recognise them by shape too. */\n static is(value: unknown): value is TransportError {\n return value instanceof TransportError ||\n (!!value && typeof value === \"object\" && (value as Error).name === \"TransportError\" &&\n typeof (value as TransportError).code === \"string\");\n }\n}\n", "import type { HeaderList } from \"./types.js\";\n\n/** Headers / object / list \u2192 HeaderList (names lower-cased). */\nexport function headersToList(headers: Headers | Record<string, string | string[]> | HeaderList): HeaderList {\n const list: HeaderList = [];\n if (Array.isArray(headers)) {\n for (const [name, value] of headers) list.push([name.toLowerCase(), value]);\n } else if (typeof Headers !== \"undefined\" && headers instanceof Headers) {\n headers.forEach((value, name) => list.push([name, value]));\n // Headers joins Set-Cookie values; getSetCookie() keeps them apart.\n const setCookies = typeof headers.getSetCookie === \"function\" ? headers.getSetCookie() : [];\n if (setCookies.length) {\n const rest = list.filter(([name]) => name !== \"set-cookie\");\n return [...rest, ...setCookies.map((value): [string, string] => [\"set-cookie\", value])];\n }\n } else {\n for (const [name, value] of Object.entries(headers)) {\n for (const v of Array.isArray(value) ? value : [value]) list.push([name.toLowerCase(), String(v)]);\n }\n }\n return list;\n}\n\n/** HeaderList \u2192 object, joining repeated names with \", \" (Set-Cookie kept as array). */\nexport function listToObject(list: HeaderList): Record<string, string | string[]> {\n const out: Record<string, string | string[]> = {};\n for (const [name, value] of list) {\n const key = name.toLowerCase();\n if (key === \"set-cookie\") {\n const prev = out[key];\n out[key] = Array.isArray(prev) ? [...prev, value] : [value];\n } else {\n out[key] = key in out ? `${out[key]}, ${value}` : value;\n }\n }\n return out;\n}\n\n/** Read any TransportResponse body into an ArrayBuffer. */\nexport async function readBody(body: ReadableStream<Uint8Array> | ArrayBuffer | Blob | string | null): Promise<ArrayBuffer> {\n if (body == null) return new ArrayBuffer(0);\n if (body instanceof ArrayBuffer) return body;\n return new Response(body as BodyInit).arrayBuffer();\n}\n", "/**\n * @axis-network/butter-transport: the transport API for butter-col.\n *\n * Transport authors import registerTransport() and the types from here and\n * ship a classic script that registers their factory. butter-col loads that\n * script in the service worker, in proxied pages and in workers, and creates\n * an instance in each realm that needs one.\n */\nimport type { ButterTransport, TransportEnvironment, TransportFactory } from \"./types.js\";\n\nexport * from \"./types.js\";\nexport { TransportError, TRANSPORT_ERROR_CODES } from \"./errors.js\";\nexport { headersToList, listToObject, readBody } from \"./util.js\";\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __butter$transports: Map<string, TransportFactory> | undefined;\n}\n\n/** The registry is a global so separately bundled transports share it. */\nfunction registry(): Map<string, TransportFactory> {\n return (globalThis.__butter$transports ??= new Map());\n}\n\nconst NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;\n\n/**\n * Register a transport factory under `name`. Call it at the top level of your\n * transport's script.\n *\n * ```js\n * registerTransport(\"mine\", (options, env) => ({ meta: {...}, request(req) {...} }));\n * ```\n */\nexport function registerTransport<Options>(name: string, factory: TransportFactory<Options>): void {\n if (!NAME.test(name)) throw new TypeError(`butter-transport: invalid transport name \"${name}\"`);\n if (typeof factory !== \"function\") throw new TypeError(`butter-transport: factory for \"${name}\" is not a function`);\n if (registry().has(name)) console.warn(`[butter] transport \"${name}\" registered twice; the last one wins`);\n registry().set(name, factory as TransportFactory);\n}\n\nexport function getTransportFactory(name: string): TransportFactory | undefined {\n return registry().get(name);\n}\n\n/** Names of every registered transport in this realm. */\nexport function listTransports(): string[] {\n return [...registry().keys()];\n}\n\n/**\n * Create, validate and initialise a transport. Used by butter-col itself;\n * also handy in tests.\n */\nexport async function createTransport(name: string, options: unknown, env: TransportEnvironment): Promise<ButterTransport> {\n const factory = getTransportFactory(name);\n if (!factory) {\n throw new Error(`butter-transport: no transport named \"${name}\" is registered (loaded: ${listTransports().join(\", \") || \"none\"})`);\n }\n const transport = await factory(options, env);\n const problems = validateTransport(transport);\n if (problems.length) throw new TypeError(`butter-transport: \"${name}\" is invalid: ${problems.join(\"; \")}`);\n if (transport.meta.name !== name) {\n throw new TypeError(`butter-transport: \"${name}\" reports meta.name \"${transport.meta.name}\"`);\n }\n await transport.init?.();\n return transport;\n}\n\n/** Structural checks; returns a list of problems (empty when valid). */\nexport function validateTransport(transport: unknown): string[] {\n const problems: string[] = [];\n const t = transport as Partial<ButterTransport> | null;\n if (!t || typeof t !== \"object\") return [\"factory did not return an object\"];\n const meta = t.meta;\n if (!meta || typeof meta !== \"object\") problems.push(\"meta is missing\");\n else {\n if (typeof meta.name !== \"string\") problems.push(\"meta.name must be a string\");\n if (meta.cookies !== \"server\" && meta.cookies !== \"client\") problems.push('meta.cookies must be \"server\" or \"client\"');\n if (typeof meta.websockets !== \"boolean\") problems.push(\"meta.websockets must be a boolean\");\n if (meta.websockets && typeof t.connect !== \"function\") problems.push(\"meta.websockets is true but connect() is missing\");\n }\n if (typeof t.request !== \"function\") problems.push(\"request() is missing\");\n if (t.init !== undefined && typeof t.init !== \"function\") problems.push(\"init must be a function\");\n if (t.destroy !== undefined && typeof t.destroy !== \"function\") problems.push(\"destroy must be a function\");\n return problems;\n}\n", "/**\n * URL codecs. A codec turns an absolute target URL (without its fragment)\n * into the string that follows the proxy prefix, and back again.\n *\n * Every codec must satisfy: decode(encode(url)) === url.\n */\n\n/** @typedef {{ encode(url: string): string, decode(encoded: string): string }} Codec */\n\n/** Structure preserving: /butter/go/https://example.com/a/b?c */\nconst none = {\n encode: (url) => url,\n decode: (encoded) => encoded,\n};\n\n/** encodeURIComponent of the whole URL (same idea as Scramjet's \"plain\"). */\nconst plain = {\n encode: (url) => encodeURIComponent(url),\n decode: (encoded) => decodeURIComponent(encoded),\n};\n\n/** XOR every second character with 2 (same scheme as Ultraviolet). */\nfunction xorString(str) {\n let out = \"\";\n for (let i = 0; i < str.length; i++) {\n out += i % 2 ? String.fromCharCode(str.charCodeAt(i) ^ 2) : str[i];\n }\n return out;\n}\n\nconst xor = {\n encode: (url) => encodeURIComponent(xorString(url)),\n decode: (encoded) => xorString(decodeURIComponent(encoded)),\n};\n\n/** URL-safe base64 of the UTF-8 bytes, without padding. */\nconst base64 = {\n encode(url) {\n const bytes = new TextEncoder().encode(url);\n let bin = \"\";\n for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);\n return btoa(bin).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n },\n decode(encoded) {\n let b64 = encoded.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (b64.length % 4) b64 += \"=\";\n const bin = atob(b64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n },\n};\n\n/** @type {Record<string, Codec>} */\nexport const codecs = { none, plain, xor, base64 };\n\n/**\n * @param {string} name\n * @returns {Codec}\n */\nexport function getCodec(name) {\n const codec = codecs[name];\n if (!codec) throw new Error(`butter: unknown codec \"${name}\"`);\n return codec;\n}\n", "import { getCodec } from \"./codecs.js\";\n\n/** Marker placed after the prefix for module workers: /butter/go/~m/<encoded> */\nexport const MODULE_WORKER_MARK = \"~m/\";\n\nconst PASSTHROUGH_SCHEMES = /^(?:data|blob|about|mailto|tel|sms|javascript|intent|chrome|chrome-extension|moz-extension|file|filesystem):/i;\nconst TARGET_SCHEME = /^(?:https?|wss?):\\/\\//i;\n\n/**\n * @typedef {object} UrlRewriterOptions\n * @property {string} origin Proxy origin, e.g. \"https://proxy.example\"\n * @property {string} prefix Path prefix for proxied URLs, e.g. \"/butter/go/\"\n * @property {string} codec Codec name, see codecs.js\n */\n\n/**\n * @param {UrlRewriterOptions} options\n */\nexport function createUrlRewriter({ origin, prefix, codec: codecName }) {\n const codec = getCodec(codecName);\n const absPrefix = origin + prefix;\n\n /**\n * Decode the part of a proxied path that follows the prefix.\n * Returns null when the tail is not a valid encoded URL (for example a\n * relative path that the browser resolved against the proxy URL).\n * @param {string} tail\n * @returns {string | null}\n */\n function decodeTail(tail) {\n if (tail.startsWith(MODULE_WORKER_MARK)) tail = tail.slice(MODULE_WORKER_MARK.length);\n let decoded;\n try {\n decoded = codec.decode(tail);\n } catch {\n return null;\n }\n if (!TARGET_SCHEME.test(decoded)) return null;\n try {\n return new URL(decoded).href;\n } catch {\n return null;\n }\n }\n\n /**\n * Is this an already rewritten URL (absolute or path-absolute)?\n * @param {string} url\n */\n function isProxied(url) {\n return url.startsWith(absPrefix) || url.startsWith(prefix);\n }\n\n /**\n * Rewrite a URL found in a page so that it points through the proxy.\n * Non-http(s) URLs and fragment-only references are returned unchanged.\n * @param {string} raw\n * @param {string | URL} base The target (unproxied) base URL\n * @param {{ moduleWorker?: boolean }} [opts]\n * @returns {string}\n */\n function rewrite(raw, base, opts) {\n if (raw == null) return raw;\n const str = String(raw);\n const trimmed = str.trim();\n if (!trimmed || trimmed.startsWith(\"#\") || PASSTHROUGH_SCHEMES.test(trimmed)) return str;\n if (isProxied(trimmed)) return trimmed;\n let abs;\n try {\n abs = new URL(trimmed, base);\n } catch {\n return str;\n }\n if (abs.protocol !== \"http:\" && abs.protocol !== \"https:\") return str;\n const hash = abs.hash;\n abs.hash = \"\";\n const mark = opts && opts.moduleWorker ? MODULE_WORKER_MARK : \"\";\n return absPrefix + mark + codec.encode(abs.href) + hash;\n }\n\n /**\n * Turn a proxied URL back into the target URL. Anything that is not a\n * proxied URL is returned unchanged.\n * @param {string} url\n * @returns {string}\n */\n function unrewrite(url) {\n if (url == null) return url;\n const str = String(url);\n let rest;\n if (str.startsWith(absPrefix)) rest = str.slice(absPrefix.length);\n else if (str.startsWith(prefix)) rest = str.slice(prefix.length);\n else return str;\n\n let hash = \"\";\n const hashAt = rest.indexOf(\"#\");\n if (hashAt !== -1) {\n hash = rest.slice(hashAt);\n rest = rest.slice(0, hashAt);\n }\n let search = \"\";\n const searchAt = rest.indexOf(\"?\");\n if (searchAt !== -1) {\n search = rest.slice(searchAt);\n rest = rest.slice(0, searchAt);\n }\n const decoded = decodeTail(rest);\n if (decoded === null) return str;\n if (!search) return decoded + hash;\n const target = new URL(decoded);\n target.search = search;\n return target.href + hash;\n }\n\n /**\n * @param {string} value A srcset attribute value\n * @param {string | URL} base\n * @param {(url: string, base: string | URL) => string} [map]\n */\n function rewriteSrcset(value, base, map = rewrite) {\n // Follows the HTML \"parse a srcset attribute\" algorithm closely enough to\n // keep data: URLs (which contain commas) intact.\n let out = \"\";\n let i = 0;\n const n = value.length;\n while (i < n) {\n const wsStart = i;\n while (i < n && (/\\s/.test(value[i]) || value[i] === \",\")) i++;\n out += value.slice(wsStart, i);\n if (i >= n) break;\n const urlStart = i;\n while (i < n && !/\\s/.test(value[i])) i++;\n let url = value.slice(urlStart, i);\n let trailing = \"\";\n const commas = url.match(/,+$/);\n if (commas) {\n trailing = commas[0];\n url = url.slice(0, -trailing.length);\n }\n out += map(url, base) + trailing;\n if (trailing) continue;\n // Descriptors run until the next comma that is not inside parentheses.\n const descStart = i;\n let depth = 0;\n while (i < n) {\n const ch = value[i];\n if (ch === \"(\") depth++;\n else if (ch === \")\") depth = Math.max(0, depth - 1);\n else if (ch === \",\" && depth === 0) break;\n i++;\n }\n out += value.slice(descStart, i);\n }\n return out;\n }\n\n return {\n origin,\n prefix,\n codec,\n decodeTail,\n isProxied,\n rewrite,\n unrewrite,\n rewriteSrcset,\n unrewriteSrcset: (value) => rewriteSrcset(value, \"\", (url) => unrewrite(url)),\n };\n}\n\n/** @typedef {ReturnType<typeof createUrlRewriter>} UrlRewriter */\n", "/**\n * CSS rewriting: url(...) references and @import strings.\n */\n\nconst URL_FUNCTION = /url\\(\\s*(?:\"((?:[^\"\\\\\\n]|\\\\.)*)\"|'((?:[^'\\\\\\n]|\\\\.)*)'|((?:[^)\\s\"'\\\\]|\\\\.)*))\\s*\\)/gi;\nconst IMPORT_STRING = /@import\\s+(?:\"((?:[^\"\\\\\\n]|\\\\.)*)\"|'((?:[^'\\\\\\n]|\\\\.)*)')/gi;\n\n/**\n * @param {string} css\n * @param {string | URL} base\n * @param {(url: string, base: string | URL) => string} rewriteUrl\n * @returns {string}\n */\nexport function rewriteCss(css, base, rewriteUrl) {\n if (!css || (css.indexOf(\"url(\") === -1 && css.indexOf(\"URL(\") === -1 && css.indexOf(\"@import\") === -1)) {\n return css;\n }\n const map = (raw) => {\n // CSS escapes inside URLs are rare; only unescape the trivial cases.\n const url = raw.replace(/\\\\([^0-9a-fA-F\\n])/g, \"$1\");\n if (!url || url.startsWith(\"#\") || /^data:/i.test(url)) return null;\n const rewritten = rewriteUrl(url, base);\n return rewritten === url ? null : rewritten;\n };\n return css\n .replace(URL_FUNCTION, (match, dq, sq, bare) => {\n const raw = dq ?? sq ?? bare ?? \"\";\n const rewritten = map(raw);\n return rewritten === null ? match : `url(\"${rewritten.replace(/[\"\\\\\\n]/g, \"\\\\$&\")}\")`;\n })\n .replace(IMPORT_STRING, (match, dq, sq) => {\n const rewritten = map(dq ?? sq ?? \"\");\n return rewritten === null ? match : `@import \"${rewritten.replace(/[\"\\\\\\n]/g, \"\\\\$&\")}\"`;\n });\n}\n", "/**\n * JavaScript rewriting.\n *\n * butter-col does not parse JavaScript into an AST. Instead a small,\n * error-tolerant tokenizer walks the source and applies a handful of targeted\n * edits. Everything else (fetches, imports, workers, ...) is handled at\n * runtime by the service worker and the client hooks, so the rewriter only\n * has to deal with what cannot be hooked:\n *\n * location / x.location / x?.location / x[\"location\"]\n * -> __butter$location (resolved by an accessor on Object.prototype)\n * eval(code)\n * -> eval(__butter$js(code)) (stays a *direct* eval)\n * import.meta.url\n * -> __butter$unrewrite(import.meta.url)\n *\n * The tokenizer never edits inside strings, template text, comments or regex\n * literals, and every edit is local, so a mis-scan can at worst miss an edit.\n */\n\nexport const LOCATION = \"__butter$location\";\nexport const EVAL_WRAP = \"__butter$js\";\nexport const UNREWRITE = \"__butter$unrewrite\";\n\nconst NEEDS_REWRITE = /location|eval|import\\s*\\.\\s*meta/;\n\n/** Identifiers after which a `/` starts a regular expression. */\nconst EXPR_KEYWORDS = new Set([\n \"return\", \"typeof\", \"instanceof\", \"in\", \"of\", \"new\", \"delete\", \"void\", \"throw\",\n \"case\", \"do\", \"else\", \"yield\", \"await\", \"extends\",\n]);\n\n/** Punctuators after which `{` opens an object literal rather than a block. */\nconst OBJECT_AFTER_PUNC = new Set([\n \"(\", \"[\", \",\", \"=\", \":\", \"?\", \"...\", \"!\", \"&\", \"|\", \"^\", \"~\", \"+\", \"-\", \"*\", \"/\", \"%\",\n \"<\", \">\", \"${\", \"??\", \"?.\",\n]);\nconst OBJECT_AFTER_KEYWORD = new Set([\n \"return\", \"yield\", \"await\", \"typeof\", \"void\", \"in\", \"of\", \"case\", \"throw\", \"delete\",\n]);\n\nconst IMPORT_META_URL = /import\\s*\\.\\s*meta\\s*\\.\\s*url(?![\\w$])/y;\n\nfunction isIdStart(c) {\n return (c >= 97 && c <= 122) || (c >= 65 && c <= 90) || c === 36 || c === 95 || c >= 128;\n}\n\nfunction isIdPart(c) {\n return isIdStart(c) || (c >= 48 && c <= 57);\n}\n\nfunction isWhitespace(c) {\n return c === 32 || c === 9 || c === 10 || c === 13 || c === 11 || c === 12 ||\n c === 0xa0 || c === 0xfeff || c === 0x2028 || c === 0x2029 || c === 0x1680 ||\n (c >= 0x2000 && c <= 0x200a) || c === 0x202f || c === 0x205f || c === 0x3000;\n}\n\nfunction isLineTerminator(c) {\n return c === 10 || c === 13 || c === 0x2028 || c === 0x2029;\n}\n\n/**\n * @param {string} src\n * @returns {string}\n */\nexport function rewriteJs(src) {\n if (typeof src !== \"string\" || !NEEDS_REWRITE.test(src)) return src;\n\n const n = src.length;\n /** @type {Array<[number, number, string]>} start, end, replacement */\n const edits = [];\n\n let prevType = \"\"; // \"id\" | \"num\" | \"str\" | \"tmpl\" | \"regex\" | \"punc\"\n let prevVal = \"\";\n let prev2Type = \"\";\n let prev2Val = \"\";\n /** Open `{` kinds: \"obj\" | \"block\" | \"ie-import\" | \"ie-export\" | \"tmpl\" */\n const braces = [];\n /** Open `(`: null, or the index of the `(` of an eval call */\n const parens = [];\n /** Inside an import statement, before `from` */\n let inImport = false;\n\n const push = (type, val) => {\n prev2Type = prevType;\n prev2Val = prevVal;\n prevType = type;\n prevVal = val;\n };\n\n /** Index of the next character that is not whitespace or a comment. */\n const skipTrivia = (pos) => {\n while (pos < n) {\n const c = src.charCodeAt(pos);\n if (isWhitespace(c)) {\n pos++;\n } else if (c === 47 && src.charCodeAt(pos + 1) === 47) {\n while (pos < n && !isLineTerminator(src.charCodeAt(pos))) pos++;\n } else if (c === 47 && src.charCodeAt(pos + 1) === 42) {\n const end = src.indexOf(\"*/\", pos + 2);\n pos = end === -1 ? n : end + 2;\n } else {\n break;\n }\n }\n return pos;\n };\n\n const peekWord = (pos) => {\n let j = skipTrivia(pos);\n const start = j;\n while (j < n && isIdPart(src.charCodeAt(j))) j++;\n return src.slice(start, j);\n };\n\n const regexAllowed = () => {\n if (!prevType) return true;\n if (prevType === \"punc\") return prevVal !== \")\" && prevVal !== \"]\" && prevVal !== \"++\" && prevVal !== \"--\";\n if (prevType === \"id\") return EXPR_KEYWORDS.has(prevVal);\n return false;\n };\n\n const scanString = (i, quote) => {\n let j = i + 1;\n while (j < n) {\n const c = src.charCodeAt(j);\n if (c === 92) j += 2;\n else if (c === quote) return j + 1;\n else if (c === 10 || c === 13) return j; // unterminated, recover\n else j++;\n }\n return n;\n };\n\n /** Scan template text starting at j. Returns [end, opensSubstitution]. */\n const scanTemplate = (j) => {\n while (j < n) {\n const c = src.charCodeAt(j);\n if (c === 92) j += 2;\n else if (c === 96) return [j + 1, false];\n else if (c === 36 && src.charCodeAt(j + 1) === 123) return [j + 2, true];\n else j++;\n }\n return [n, false];\n };\n\n /** Returns the end of the regex literal starting at i, or -1. */\n const scanRegex = (i) => {\n let j = i + 1;\n let inClass = false;\n while (j < n) {\n const c = src.charCodeAt(j);\n if (isLineTerminator(c)) return -1;\n if (c === 92) {\n if (j + 1 >= n || isLineTerminator(src.charCodeAt(j + 1))) return -1;\n j += 2;\n continue;\n }\n if (inClass) {\n if (c === 93) inClass = false;\n } else if (c === 91) {\n inClass = true;\n } else if (c === 47) {\n j++;\n while (j < n && isIdPart(src.charCodeAt(j))) j++;\n return j;\n }\n j++;\n }\n return -1;\n };\n\n const handleTemplate = (start) => {\n const [end, opens] = scanTemplate(start);\n if (opens) {\n braces.push(\"tmpl\");\n push(\"punc\", \"${\");\n } else {\n push(\"tmpl\", \"\");\n }\n return end;\n };\n\n const handleLocation = (start, end) => {\n if (prevType === \"punc\" && (prevVal === \".\" || prevVal === \"?.\")) {\n edits.push([start, end, LOCATION]);\n return;\n }\n const top = braces[braces.length - 1];\n if (top === \"ie-import\" || top === \"ie-export\") {\n const importing = top === \"ie-import\";\n if (prevType === \"id\" && prevVal === \"as\") {\n if (importing) edits.push([start, end, LOCATION]); // local binding\n } else if (peekWord(end) === \"as\") {\n if (!importing) edits.push([start, end, LOCATION]); // local binding\n } else {\n edits.push([start, end, importing ? `location as ${LOCATION}` : `${LOCATION} as location`]);\n }\n return;\n }\n if (top === \"obj\" && prevType === \"punc\" && (prevVal === \"{\" || prevVal === \",\")) {\n const next = src[skipTrivia(end)];\n if (next === \":\") return; // property key\n if (next === \",\" || next === \"}\") {\n edits.push([start, end, `location: ${LOCATION}`]); // shorthand property\n return;\n }\n }\n edits.push([start, end, LOCATION]);\n };\n\n let i = 0;\n if (src.startsWith(\"#!\")) {\n while (i < n && !isLineTerminator(src.charCodeAt(i))) i++;\n }\n\n while (i < n) {\n const c = src.charCodeAt(i);\n\n if (isWhitespace(c)) {\n i++;\n continue;\n }\n\n // Comments, regular expressions and division.\n if (c === 47) {\n const d = src.charCodeAt(i + 1);\n if (d === 47 || d === 42) {\n i = skipTrivia(i);\n continue;\n }\n if (regexAllowed()) {\n const end = scanRegex(i);\n if (end !== -1) {\n i = end;\n push(\"regex\", \"\");\n continue;\n }\n }\n i++;\n push(\"punc\", \"/\");\n continue;\n }\n\n // Strings.\n if (c === 34 || c === 39) {\n const end = scanString(i, c);\n const value = src.slice(i + 1, end - 1);\n if (\n value === \"location\" &&\n prevType === \"punc\" && prevVal === \"[\" &&\n ((prev2Type === \"id\" && !EXPR_KEYWORDS.has(prev2Val)) || prev2Val === \")\" || prev2Val === \"]\") &&\n src[skipTrivia(end)] === \"]\"\n ) {\n const quote = src[i];\n edits.push([i, end, quote + LOCATION + quote]);\n }\n i = end;\n push(\"str\", value);\n continue;\n }\n\n // Template literals.\n if (c === 96) {\n i = handleTemplate(i + 1);\n continue;\n }\n\n // Identifiers and keywords.\n if (isIdStart(c) || c === 92) {\n let j = i;\n let escaped = false;\n while (j < n) {\n const cc = src.charCodeAt(j);\n if (cc === 92) {\n // \\uXXXX or \\u{X...} escape inside an identifier\n escaped = true;\n if (src.charCodeAt(j + 2) === 123) {\n const close = src.indexOf(\"}\", j);\n j = close === -1 ? n : close + 1;\n } else {\n j += 6;\n }\n } else if (isIdPart(cc) && !isWhitespace(cc)) {\n j++;\n } else {\n break;\n }\n }\n const value = src.slice(i, j);\n const afterDot = prevType === \"punc\" && (prevVal === \".\" || prevVal === \"?.\");\n\n if (!escaped) {\n if (value === \"location\") {\n handleLocation(i, j);\n } else if (value === \"import\" && !afterDot) {\n IMPORT_META_URL.lastIndex = i;\n if (IMPORT_META_URL.test(src)) {\n const end = IMPORT_META_URL.lastIndex;\n edits.push([i, end, `${UNREWRITE}(import.meta.url)`]);\n i = end;\n push(\"punc\", \")\");\n continue;\n }\n const next = src[skipTrivia(j)];\n if (next !== \"(\" && next !== \".\") inImport = true;\n } else if (value === \"from\" && inImport) {\n inImport = false;\n }\n }\n\n i = j;\n push(\"id\", value);\n continue;\n }\n\n // Private names and numbers.\n if (c === 35) {\n let j = i + 1;\n while (j < n && isIdPart(src.charCodeAt(j))) j++;\n push(\"id\", src.slice(i, j));\n i = j;\n continue;\n }\n if ((c >= 48 && c <= 57) || (c === 46 && src.charCodeAt(i + 1) >= 48 && src.charCodeAt(i + 1) <= 57)) {\n let j = i + 1;\n while (j < n) {\n const cc = src.charCodeAt(j);\n if (isIdPart(cc) || cc === 46) j++;\n else break;\n }\n i = j;\n push(\"num\", \"\");\n continue;\n }\n\n // Punctuators.\n switch (c) {\n case 123: { // {\n let kind = \"block\";\n if (prevType === \"id\" && (prevVal === \"import\" || prevVal === \"export\") && prev2Val !== \".\" && prev2Val !== \"?.\") {\n kind = \"ie-\" + prevVal;\n } else if (inImport && prevType === \"punc\" && prevVal === \",\") {\n kind = \"ie-import\";\n } else if ((prevType === \"punc\" && OBJECT_AFTER_PUNC.has(prevVal)) || (prevType === \"id\" && OBJECT_AFTER_KEYWORD.has(prevVal))) {\n kind = \"obj\";\n }\n braces.push(kind);\n i++;\n push(\"punc\", \"{\");\n continue;\n }\n case 125: { // }\n const kind = braces.pop();\n if (kind === \"tmpl\") {\n i = handleTemplate(i + 1);\n continue;\n }\n i++;\n push(\"punc\", \"}\");\n continue;\n }\n case 40: { // (\n const isEvalCall = prevType === \"id\" && prevVal === \"eval\" &&\n prev2Val !== \".\" && prev2Val !== \"?.\" && prev2Val !== \"function\";\n parens.push(isEvalCall ? i : null);\n i++;\n push(\"punc\", \"(\");\n continue;\n }\n case 41: { // )\n const open = parens.pop();\n // `eval(x) {` is a method definition, not a call.\n if (open != null && src[skipTrivia(i + 1)] !== \"{\") {\n edits.push([open + 1, open + 1, EVAL_WRAP + \"(\"]);\n edits.push([i, i, \")\"]);\n }\n i++;\n push(\"punc\", \")\");\n continue;\n }\n case 46: // .\n if (src.charCodeAt(i + 1) === 46 && src.charCodeAt(i + 2) === 46) {\n i += 3;\n push(\"punc\", \"...\");\n } else {\n i++;\n push(\"punc\", \".\");\n }\n continue;\n case 63: { // ?\n const d = src.charCodeAt(i + 1);\n const e = src.charCodeAt(i + 2);\n if (d === 46 && !(e >= 48 && e <= 57)) {\n i += 2;\n push(\"punc\", \"?.\");\n } else if (d === 63) {\n i += 2;\n push(\"punc\", \"??\");\n } else {\n i++;\n push(\"punc\", \"?\");\n }\n continue;\n }\n case 43: // +\n case 45: // -\n if (src.charCodeAt(i + 1) === c) {\n i += 2;\n push(\"punc\", c === 43 ? \"++\" : \"--\");\n } else {\n i++;\n push(\"punc\", src[i - 1]);\n }\n continue;\n case 61: // =\n if (src.charCodeAt(i + 1) === 62) {\n i += 2;\n push(\"punc\", \"=>\");\n } else {\n i++;\n push(\"punc\", \"=\");\n }\n continue;\n case 59: // ;\n inImport = false;\n i++;\n push(\"punc\", \";\");\n continue;\n default:\n i++;\n push(\"punc\", src[i - 1]);\n }\n }\n\n if (edits.length === 0) return src;\n\n edits.sort((a, b) => a[0] - b[0] || (a[1] - a[0]) - (b[1] - b[0]));\n let out = \"\";\n let last = 0;\n for (const [start, end, text] of edits) {\n if (start < last) continue; // overlapping edit; keep the first\n out += src.slice(last, start) + text;\n last = end;\n }\n return out + src.slice(last);\n}\n", "/**\n * HTML rewriting with a small, forgiving tokenizer (service workers have no\n * DOMParser). Only start tags are modified; text, comments and end tags are\n * copied through byte for byte, and unchanged tags keep their exact source.\n */\n\nconst RAW_TEXT = new Set([\"script\", \"style\", \"textarea\", \"title\", \"xmp\", \"iframe\", \"noembed\", \"noframes\", \"plaintext\"]);\n\nconst JS_TYPE = /^(?:|module|(?:text|application)\\/(?:x-)?(?:javascript|ecmascript)|text\\/jscript|text\\/livescript|text\\/javascript1\\.[0-5])$/i;\n\n/** Attributes holding a single URL, on any element. */\nexport const URL_ATTRS = new Set([\n \"href\", \"src\", \"action\", \"formaction\", \"poster\", \"background\", \"cite\", \"longdesc\",\n \"manifest\", \"codebase\", \"icon\", \"lowsrc\", \"dynsrc\", \"xlink:href\",\n]);\n\n/** Attributes whose name is changed so the browser ignores them. */\nconst NEUTRALIZE = {\n integrity: \"data-butter-integrity\",\n sandbox: \"data-butter-sandbox\", // sandboxed frames cannot be controlled by the service worker\n};\n\nconst NAMED_ENTITIES = { amp: \"&\", lt: \"<\", gt: \">\", quot: '\"', apos: \"'\", nbsp: String.fromCharCode(0xa0) };\nconst ENTITY = /&(?:#(\\d{1,7});?|#[xX]([0-9a-fA-F]{1,6});?|(amp|lt|gt|quot|apos|nbsp)(?:;|(?![=\\w])))/g;\n\n/** @param {string} value */\nexport function decodeEntities(value) {\n if (value.indexOf(\"&\") === -1) return value;\n return value.replace(ENTITY, (match, dec, hex, named) => {\n if (named) return NAMED_ENTITIES[named];\n const code = dec ? parseInt(dec, 10) : parseInt(hex, 16);\n return code > 0 && code <= 0x10ffff ? String.fromCodePoint(code) : String.fromCharCode(0xfffd);\n });\n}\n\n/** @param {string} value */\nexport function escapeAttribute(value) {\n return value.replace(/&/g, \"&amp;\").replace(/\"/g, \"&quot;\");\n}\n\nfunction isWs(c) {\n return c === 32 || c === 9 || c === 10 || c === 12 || c === 13;\n}\n\nfunction isLetter(c) {\n return (c >= 97 && c <= 122) || (c >= 65 && c <= 90);\n}\n\n/**\n * @typedef {object} RewriteContext\n * @property {string} base Target base URL used to resolve relative URLs\n * @property {string} [inject] Markup inserted at the start of the document\n */\n\n/**\n * Rewrite one attribute value. Returns the new value (possibly identical).\n * Shared by the HTML rewriter and the client runtime's setAttribute hook.\n *\n * @param {import(\"./index.js\").Formatter} f\n * @param {string} tag Lower-case tag name\n * @param {string} name Attribute name\n * @param {string} value Decoded attribute value\n * @param {RewriteContext} ctx\n * @returns {string}\n */\nexport function rewriteAttribute(f, tag, name, value, ctx) {\n const attr = name.toLowerCase();\n if (attr.length > 2 && attr.startsWith(\"on\")) return f.js(value);\n switch (attr) {\n case \"style\":\n return f.css(value, ctx.base);\n case \"srcset\":\n case \"imagesrcset\":\n return f.url.rewriteSrcset(value, ctx.base);\n case \"ping\":\n return value.split(/(\\s+)/).map((part) => (part.trim() ? f.rewriteUrl(part, ctx.base) : part)).join(\"\");\n case \"srcdoc\":\n return tag === \"iframe\" ? f.html(value, { base: ctx.base, inject: ctx.inject }) : value;\n case \"data\":\n return tag === \"object\" ? f.rewriteUrl(value, ctx.base) : value;\n }\n if (URL_ATTRS.has(attr)) {\n const trimmed = value.trim();\n if (/^javascript:/i.test(trimmed)) {\n const code = trimmed.slice(11);\n return code.indexOf(\"%\") === -1 ? \"javascript:\" + f.js(code) : value;\n }\n return f.rewriteUrl(value, ctx.base);\n }\n return value;\n}\n\n/**\n * @typedef {object} Attr\n * @property {string} name\n * @property {string | null} value Decoded value, null for boolean attributes\n * @property {number} start\n * @property {number} end\n * @property {string} [newName]\n * @property {string | null} [newValue]\n */\n\n/**\n * Parse a start tag beginning at `i` (src[i] === \"<\", src[i + 1] is a letter).\n * @param {string} src\n * @param {number} i\n */\nfunction parseStartTag(src, i) {\n const n = src.length;\n let j = i + 1;\n while (j < n) {\n const c = src.charCodeAt(j);\n if (isWs(c) || c === 47 || c === 62) break;\n j++;\n }\n const name = src.slice(i + 1, j).toLowerCase();\n /** @type {Attr[]} */\n const attrs = [];\n let selfClosing = false;\n\n while (j < n) {\n let c = src.charCodeAt(j);\n if (isWs(c)) {\n j++;\n continue;\n }\n if (c === 47) {\n if (src.charCodeAt(j + 1) === 62) {\n selfClosing = true;\n return { name, attrs, selfClosing, end: j + 2 };\n }\n j++;\n continue;\n }\n if (c === 62) return { name, attrs, selfClosing, end: j + 1 };\n\n const start = j++;\n while (j < n) {\n c = src.charCodeAt(j);\n if (isWs(c) || c === 47 || c === 62 || c === 61) break;\n j++;\n }\n const attrName = src.slice(start, j);\n let k = j;\n while (k < n && isWs(src.charCodeAt(k))) k++;\n if (src.charCodeAt(k) !== 61) {\n attrs.push({ name: attrName, value: null, start, end: j });\n continue;\n }\n k++;\n while (k < n && isWs(src.charCodeAt(k))) k++;\n const q = src.charCodeAt(k);\n let raw;\n if (q === 34 || q === 39) {\n const close = src.indexOf(q === 34 ? '\"' : \"'\", k + 1);\n if (close === -1) return null;\n raw = src.slice(k + 1, close);\n j = close + 1;\n } else {\n const vs = k;\n while (k < n && !isWs(src.charCodeAt(k)) && src.charCodeAt(k) !== 62) k++;\n raw = src.slice(vs, k);\n j = k;\n }\n attrs.push({ name: attrName, value: decodeEntities(raw), start, end: j });\n }\n return null; // unterminated tag at end of input\n}\n\n/** @param {Attr[]} attrs @param {string} name */\nfunction findAttr(attrs, name) {\n for (const attr of attrs) if (attr.name.toLowerCase() === name) return attr;\n return undefined;\n}\n\n/**\n * Work out the changes for one start tag, recording them on the attrs.\n * @returns {boolean} whether anything changed\n */\nfunction transformTag(f, tag, ctx, state) {\n let changed = false;\n const { name, attrs } = tag;\n\n if (name === \"meta\") {\n const equiv = findAttr(attrs, \"http-equiv\");\n const kind = equiv && equiv.value ? equiv.value.trim().toLowerCase() : \"\";\n if (kind.startsWith(\"content-security-policy\")) {\n equiv.newName = \"data-butter-http-equiv\";\n return true;\n }\n if (kind === \"refresh\") {\n const content = findAttr(attrs, \"content\");\n if (content && content.value) {\n const value = rewriteRefresh(f, content.value, ctx.base);\n if (value !== content.value) {\n content.newValue = value;\n changed = true;\n }\n }\n }\n return changed;\n }\n\n if (name === \"base\" && !state.baseSeen) {\n const href = findAttr(attrs, \"href\");\n if (href && href.value !== null) {\n state.baseSeen = true;\n try {\n ctx.base = new URL(href.value.trim(), ctx.base).href;\n } catch {\n // keep the previous base\n }\n }\n }\n\n for (const attr of attrs) {\n if (attr.value === null) continue;\n const lower = attr.name.toLowerCase();\n if (lower === \"data-butter\") return false; // our own markup\n const rename = NEUTRALIZE[lower];\n if (rename && (lower !== \"sandbox\" || name === \"iframe\")) {\n attr.newName = rename;\n changed = true;\n continue;\n }\n const value = rewriteAttribute(f, name, attr.name, attr.value, ctx);\n if (value !== attr.value) {\n attr.newValue = value;\n changed = true;\n }\n }\n return changed;\n}\n\n/**\n * Rewrite the URL in a refresh value such as \"5; url=/next\".\n * @returns {string}\n */\nexport function rewriteRefresh(f, value, base) {\n const match = value.match(/^(\\s*[\\d.]*\\s*[;,]?\\s*(?:url\\s*=\\s*)?)(['\"]?)(.*?)\\2(\\s*)$/i);\n if (!match || !match[3]) return value;\n return match[1] + match[2] + f.rewriteUrl(match[3], base) + match[2] + match[4];\n}\n\nfunction serializeTag(src, tag, i) {\n let out = \"\";\n let last = i;\n for (const attr of tag.attrs) {\n if (attr.newName === undefined && attr.newValue === undefined) continue;\n const name = attr.newName ?? attr.name;\n const value = attr.newValue !== undefined ? attr.newValue : attr.value;\n out += src.slice(last, attr.start) + name + (value === null ? \"\" : `=\"${escapeAttribute(value)}\"`);\n last = attr.end;\n }\n return out + src.slice(last, tag.end);\n}\n\nconst closeTagCache = new Map();\nfunction findRawTextEnd(src, name, from) {\n let re = closeTagCache.get(name);\n if (!re) {\n re = new RegExp(`</${name}(?=[\\\\s/>])`, \"ig\");\n closeTagCache.set(name, re);\n }\n re.lastIndex = from;\n const match = re.exec(src);\n return match ? match.index : src.length;\n}\n\n/**\n * @param {string} html\n * @param {import(\"./index.js\").Formatter} f\n * @param {RewriteContext} options\n * @returns {string}\n */\nexport function rewriteHtml(html, f, options) {\n const ctx = { base: options.base, inject: options.inject };\n const state = { injected: !options.inject, baseSeen: false, foreign: 0, doctypeEnd: -1 };\n const n = html.length;\n let out = \"\";\n let i = 0;\n\n const inject = () => {\n state.injected = true;\n return ctx.inject || \"\";\n };\n\n while (i < n) {\n const lt = html.indexOf(\"<\", i);\n if (lt === -1) {\n out += html.slice(i);\n break;\n }\n out += html.slice(i, lt);\n i = lt;\n const next = html.charCodeAt(i + 1);\n\n // Comments, doctype, CDATA and other markup declarations.\n if (next === 33 || next === 63) {\n let end;\n if (html.startsWith(\"<!--\", i)) {\n if (html.startsWith(\"<!-->\", i)) end = i + 5;\n else if (html.startsWith(\"<!--->\", i)) end = i + 6;\n else {\n const close = html.indexOf(\"-->\", i + 4);\n end = close === -1 ? n : close + 3;\n }\n } else if (state.foreign && html.startsWith(\"<![CDATA[\", i)) {\n const close = html.indexOf(\"]]>\", i + 9);\n end = close === -1 ? n : close + 3;\n } else {\n const close = html.indexOf(\">\", i + 2);\n end = close === -1 ? n : close + 1;\n if (/^<!doctype/i.test(html.slice(i, i + 9))) state.doctypeEnd = out.length + (end - i);\n }\n out += html.slice(i, end);\n i = end;\n continue;\n }\n\n // End tags.\n if (next === 47) {\n const close = html.indexOf(\">\", i + 2);\n const end = close === -1 ? n : close + 1;\n if (isLetter(html.charCodeAt(i + 2))) {\n const name = html.slice(i + 2, end).match(/^[^\\s/>]*/)[0].toLowerCase();\n if ((name === \"svg\" || name === \"math\") && state.foreign > 0) state.foreign--;\n }\n out += html.slice(i, end);\n i = end;\n continue;\n }\n\n if (!isLetter(next)) {\n out += \"<\";\n i++;\n continue;\n }\n\n // Start tags.\n const tag = parseStartTag(html, i);\n if (!tag) {\n out += html.slice(i);\n break;\n }\n\n if (!state.injected && tag.name !== \"html\") {\n if (tag.name !== \"head\") out += inject();\n }\n\n out += transformTag(f, tag, ctx, state) ? serializeTag(html, tag, i) : html.slice(i, tag.end);\n i = tag.end;\n\n if (!state.injected && tag.name === \"head\") out += inject();\n if ((tag.name === \"svg\" || tag.name === \"math\") && !tag.selfClosing) state.foreign++;\n\n const raw = state.foreign\n ? (tag.name === \"script\" || tag.name === \"style\") && !tag.selfClosing\n : RAW_TEXT.has(tag.name);\n if (!raw) continue;\n\n if (tag.name === \"plaintext\") {\n out += html.slice(i);\n break;\n }\n const end = findRawTextEnd(html, tag.name, i);\n let content = html.slice(i, end);\n if (tag.name === \"script\" && !findAttr(tag.attrs, \"data-butter\")) {\n const type = findAttr(tag.attrs, \"type\");\n if (JS_TYPE.test(type && type.value ? type.value.trim() : \"\")) content = f.js(content);\n } else if (tag.name === \"style\") {\n content = f.css(content, ctx.base);\n }\n out += content;\n i = end;\n }\n\n if (!state.injected) {\n const markup = inject();\n if (state.doctypeEnd !== -1) out = out.slice(0, state.doctypeEnd) + markup + out.slice(state.doctypeEnd);\n else out = markup + out;\n }\n return out;\n}\n", "/**\n * butter-col formatter: everything that turns a target site's content into\n * content that keeps working when served from the proxy origin.\n *\n * It is shared by the service worker (src/sw) and the in-page runtime\n * (src/client) and has no dependencies on either environment beyond the\n * standard URL / TextEncoder APIs.\n */\n\nimport { createUrlRewriter, MODULE_WORKER_MARK } from \"./url.js\";\nimport { rewriteCss } from \"./css.js\";\nimport { rewriteJs, LOCATION, EVAL_WRAP, UNREWRITE } from \"./js.js\";\nimport { rewriteHtml, rewriteAttribute, rewriteRefresh, URL_ATTRS, decodeEntities, escapeAttribute } from \"./html.js\";\nimport { codecs, getCodec } from \"./codecs.js\";\n\n/**\n * @typedef {object} FormatterConfig\n * @property {string} origin Proxy origin\n * @property {string} prefix Prefix for proxied URLs, e.g. \"/butter/go/\"\n * @property {string} codec Codec name\n */\n\n/**\n * @param {FormatterConfig} config\n */\nexport function createFormatter(config) {\n const url = createUrlRewriter(config);\n\n const formatter = {\n config,\n url,\n /** @param {string} raw @param {string | URL} base @param {{ moduleWorker?: boolean }} [opts] */\n rewriteUrl: (raw, base, opts) => url.rewrite(raw, base, opts),\n /** @param {string} proxied */\n unrewriteUrl: (proxied) => url.unrewrite(proxied),\n /** @param {string} src @param {string | URL} base */\n css: (src, base) => rewriteCss(src, base, url.rewrite),\n /** @param {string} src */\n js: (src) => rewriteJs(src),\n /** @param {string} src @param {{ base: string, inject?: string }} options */\n html: (src, options) => rewriteHtml(src, formatter, options),\n /** @param {string} tag @param {string} name @param {string} value @param {{ base: string, inject?: string }} ctx */\n attr: (tag, name, value, ctx) => rewriteAttribute(formatter, tag, name, value, ctx),\n /** @param {string} value @param {string} base */\n refresh: (value, base) => rewriteRefresh(formatter, value, base),\n };\n return formatter;\n}\n\n/** @typedef {ReturnType<typeof createFormatter>} Formatter */\n\nexport {\n codecs,\n getCodec,\n rewriteCss,\n rewriteJs,\n rewriteHtml,\n decodeEntities,\n escapeAttribute,\n URL_ATTRS,\n MODULE_WORKER_MARK,\n LOCATION,\n EVAL_WRAP,\n UNREWRITE,\n};\n", "/**\n * Runtime configuration, served as /butter/config.js\n * (`self.__butter$config = {...}`). The Lua server renders it from its\n * config.lua; static deployments get one from `butter-col static`.\n */\nimport type { ButterPublicConfig } from \"@axis-network/butter-transport\";\n\nexport type ButterConfig = ButterPublicConfig;\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __butter$config: ButterConfig | undefined;\n}\n\nexport function loadConfig(scope: ServiceWorkerGlobalScope): ButterConfig {\n const url = new URL(\"config.js\", scope.location.href).href;\n scope.importScripts(url);\n const config = globalThis.__butter$config;\n if (!config) throw new Error(`butter: ${url} did not define __butter$config`);\n if (!Array.isArray(config.transports) || config.transports.length === 0) {\n throw new Error(`butter: ${url} lists no transports`);\n }\n return config;\n}\n", "import { MODULE_WORKER_MARK, type Formatter } from \"../formatter/index.js\";\nimport type { ButterConfig } from \"./config.js\";\n\nexport interface Route {\n /** Upstream URL to fetch (no fragment). */\n target: URL;\n /** Target URL of the document or worker that made the request, if known. */\n initiator: URL | null;\n /** The request is for a module worker's main script. */\n moduleWorker: boolean;\n}\n\n/** `passthrough`: let the browser handle it. `invalid`: undecodable proxied URL. */\nexport type RouteResult = Route | \"passthrough\" | \"invalid\";\n\ninterface Initiator {\n target: URL | null;\n /** The request comes from proxied content (including about:blank frames and blob: workers). */\n proxied: boolean;\n}\n\nexport function createRouter(scope: ServiceWorkerGlobalScope, config: ButterConfig, f: Formatter) {\n const ownOrigin = scope.location.origin;\n\n function targetOf(url: string): URL | null {\n const target = f.unrewriteUrl(url);\n if (target === url || !/^https?:/i.test(target)) return null;\n try {\n return new URL(target);\n } catch {\n return null;\n }\n }\n\n async function initiatorOf(event: FetchEvent): Promise<Initiator> {\n if (event.clientId) {\n const client = await scope.clients.get(event.clientId);\n if (client) {\n const target = targetOf(client.url);\n if (target) return { target, proxied: true };\n // about:blank / about:srcdoc frames and blob: workers created by proxied pages\n return { target: null, proxied: !client.url.startsWith(ownOrigin) };\n }\n }\n const referrer = event.request.referrer;\n if (referrer) {\n const target = targetOf(referrer);\n if (target) return { target, proxied: true };\n }\n return { target: null, proxied: false };\n }\n\n /**\n * Cheap synchronous check so that requests which certainly do not concern\n * the proxy never pay for respondWith().\n */\n function canSkip(request: Request): boolean {\n const url = new URL(request.url);\n if (url.origin !== ownOrigin) return false;\n if (url.pathname.startsWith(config.prefix)) return false;\n if (url.pathname.startsWith(config.base)) return true;\n // A request from a page without a client id (navigations) is only\n // interesting when it was referred by proxied content.\n return request.mode === \"navigate\" && (!request.referrer || targetOf(request.referrer) === null);\n }\n\n async function route(event: FetchEvent): Promise<RouteResult> {\n const url = new URL(event.request.url);\n\n if (url.origin === ownOrigin) {\n if (url.pathname.startsWith(config.prefix)) {\n const tail = url.pathname.slice(config.prefix.length);\n const moduleWorker = tail.startsWith(MODULE_WORKER_MARK);\n const decoded = f.url.decodeTail(tail);\n if (decoded) {\n const target = new URL(decoded);\n // \"?x=1\" links and GET forms replace the search of the proxied URL.\n if (url.search) target.search = url.search;\n return { target, initiator: (await initiatorOf(event)).target, moduleWorker };\n }\n // A relative URL resolved against the proxied URL by the browser,\n // e.g. /butter/go/img.png: resolve it against the initiator instead.\n const initiator = await initiatorOf(event);\n if (initiator.target) {\n return { target: new URL(tail + url.search, initiator.target), initiator: initiator.target, moduleWorker: false };\n }\n return \"invalid\";\n }\n if (url.pathname.startsWith(config.base)) return \"passthrough\";\n\n // A path-absolute URL that escaped rewriting, e.g. fetch(\"/api\").\n const initiator = await initiatorOf(event);\n if (initiator.target) {\n return { target: new URL(url.pathname + url.search, initiator.target), initiator: initiator.target, moduleWorker: false };\n }\n return \"passthrough\";\n }\n\n // Cross-origin request made by a controlled client: proxy it as-is.\n const initiator = await initiatorOf(event);\n if (!initiator.proxied) return \"passthrough\";\n const target = new URL(url.href);\n target.hash = \"\";\n return { target, initiator: initiator.target, moduleWorker: false };\n }\n\n return { route, canSkip };\n}\n", "/**\n * Builds the header set sent upstream for an intercepted request.\n *\n * Browsers hide or forbid several headers (Cookie, Referer, Origin,\n * Sec-Fetch-*) from service workers, and the ones they would send point at\n * the proxy origin anyway. They are recomputed here as if the target site\n * had been visited directly. Cookie is added by the server from its jar;\n * User-Agent, Accept-Language and Accept-Encoding come from the browser's\n * own request to the transport.\n */\nimport type { Formatter } from \"../formatter/index.js\";\nimport type { Route } from \"./route.js\";\n\nconst SKIP = new Set([\n \"host\", \"cookie\", \"cookie2\", \"origin\", \"referer\", \"connection\", \"keep-alive\",\n \"proxy-authorization\", \"proxy-connection\", \"te\", \"trailer\", \"transfer-encoding\",\n \"upgrade\", \"content-length\", \"service-worker\",\n]);\n\nfunction registrableDomain(host: string): string {\n // Without a public suffix list, approximate with the last two labels.\n if (/^[\\d.]+$/.test(host) || host.includes(\":\")) return host;\n return host.split(\".\").slice(-2).join(\".\");\n}\n\nfunction fetchSite(initiator: URL | null, target: URL): string {\n if (!initiator) return \"none\";\n if (initiator.origin === target.origin) return \"same-origin\";\n if (initiator.protocol === target.protocol && registrableDomain(initiator.hostname) === registrableDomain(target.hostname)) {\n return \"same-site\";\n }\n return \"cross-site\";\n}\n\n/** Apply the request's referrer policy as if the referrer were the target page. */\nexport function computeReferrer(request: Request, target: URL, f: Formatter): URL | null {\n const raw = request.referrer;\n if (!raw || raw === \"about:client\") return null;\n const unproxied = f.unrewriteUrl(raw);\n if (unproxied === raw) return null;\n let referrer: URL;\n try {\n referrer = new URL(unproxied);\n } catch {\n return null;\n }\n referrer.hash = \"\";\n referrer.username = \"\";\n referrer.password = \"\";\n return referrer;\n}\n\nfunction referrerHeader(policy: ReferrerPolicy, referrer: URL, target: URL): string | null {\n const sameOrigin = referrer.origin === target.origin;\n const downgrade = referrer.protocol === \"https:\" && target.protocol !== \"https:\";\n const origin = referrer.origin + \"/\";\n switch (policy) {\n case \"no-referrer\":\n return null;\n case \"origin\":\n return origin;\n case \"unsafe-url\":\n return referrer.href;\n case \"same-origin\":\n return sameOrigin ? referrer.href : null;\n case \"strict-origin\":\n return downgrade ? null : origin;\n case \"origin-when-cross-origin\":\n return sameOrigin ? referrer.href : origin;\n case \"no-referrer-when-downgrade\":\n return downgrade ? null : referrer.href;\n default: // strict-origin-when-cross-origin\n return downgrade ? null : sameOrigin ? referrer.href : origin;\n }\n}\n\nexport function buildRequestHeaders(request: Request, route: Route, f: Formatter): Record<string, string> {\n const headers: Record<string, string> = {};\n request.headers.forEach((value, name) => {\n if (!SKIP.has(name) && !name.startsWith(\"x-butter-\")) headers[name] = value;\n });\n\n const referrer = computeReferrer(request, route.target, f);\n if (referrer) {\n const value = referrerHeader(request.referrerPolicy, referrer, route.target);\n if (value) headers[\"referer\"] = value;\n }\n\n const initiator = route.initiator ?? referrer;\n const unsafeMethod = request.method !== \"GET\" && request.method !== \"HEAD\";\n const crossOrigin = !!initiator && initiator.origin !== route.target.origin;\n if (initiator && (unsafeMethod || (request.mode === \"cors\" && crossOrigin))) {\n headers[\"origin\"] = initiator.origin;\n }\n\n headers[\"sec-fetch-dest\"] = request.destination || \"empty\";\n headers[\"sec-fetch-mode\"] = request.mode;\n headers[\"sec-fetch-site\"] = fetchSite(initiator, route.target);\n if (request.mode === \"navigate\") headers[\"sec-fetch-user\"] = \"?1\";\n\n return headers;\n}\n", "/**\n * Decoding of text bodies. Response.text() always assumes UTF-8, which\n * garbles pages served as Shift_JIS, windows-1252 and so on, so the charset\n * is taken from the BOM, the Content-Type header, or (for HTML) a <meta>\n * prescan, like the browser would.\n */\n\nfunction charsetFromContentType(contentType: string): string | null {\n const match = contentType.match(/;\\s*charset\\s*=\\s*\"?([^\";\\s]+)/i);\n return match ? match[1] : null;\n}\n\nfunction prescanHtml(bytes: Uint8Array): string | null {\n const head = String.fromCharCode(...bytes.subarray(0, 1024));\n const meta =\n head.match(/<meta[^>]+charset\\s*=\\s*[\"']?\\s*([\\w.:-]+)/i) ||\n head.match(/<meta[^>]+content\\s*=\\s*[\"'][^\"']*charset\\s*=\\s*([\\w.:-]+)/i);\n return meta ? meta[1] : null;\n}\n\nfunction bomCharset(bytes: Uint8Array): string | null {\n if (bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf) return \"utf-8\";\n if (bytes[0] === 0xfe && bytes[1] === 0xff) return \"utf-16be\";\n if (bytes[0] === 0xff && bytes[1] === 0xfe) return \"utf-16le\";\n return null;\n}\n\nexport function decodeText(buffer: ArrayBuffer, contentType: string, isHtml: boolean): string {\n const bytes = new Uint8Array(buffer);\n const label = bomCharset(bytes) || charsetFromContentType(contentType) || (isHtml ? prescanHtml(bytes) : null) || \"utf-8\";\n let decoder: TextDecoder;\n try {\n decoder = new TextDecoder(label);\n } catch {\n decoder = new TextDecoder(\"utf-8\");\n }\n return decoder.decode(bytes);\n}\n\n/** Replace (or add) the charset parameter; rewritten bodies are always UTF-8. */\nexport function withUtf8(contentType: string, fallback: string): string {\n const mime = contentType.split(\";\")[0].trim() || fallback;\n return `${mime}; charset=utf-8`;\n}\n", "import type { Formatter } from \"../formatter/index.js\";\nimport type { ButterConfig } from \"./config.js\";\nimport type { Route } from \"./route.js\";\nimport type { TransportResponse } from \"@axis-network/butter-transport\";\nimport type { TransportInfo } from \"./transports.js\";\nimport { decodeText, withUtf8 } from \"./charset.js\";\n\n/** U+2028 / U+2029 are valid in JSON but end a line in older JS engines. */\nconst LINE_SEPARATORS = new RegExp(\"[\" + String.fromCharCode(0x2028, 0x2029) + \"]\", \"g\");\n\n/** Response headers that would break or escape the proxy if passed through. */\nconst DROP = new Set([\n \"content-security-policy\", \"content-security-policy-report-only\", \"x-webkit-csp\", \"x-content-security-policy\",\n \"x-frame-options\", \"x-xss-protection\", \"strict-transport-security\", \"expect-ct\", \"public-key-pins\",\n \"cross-origin-opener-policy\", \"cross-origin-opener-policy-report-only\",\n \"cross-origin-embedder-policy\", \"cross-origin-embedder-policy-report-only\", \"cross-origin-resource-policy\",\n \"origin-agent-cluster\", \"document-policy\", \"clear-site-data\", \"service-worker-allowed\",\n \"report-to\", \"reporting-endpoints\", \"nel\", \"alt-svc\",\n \"set-cookie\", \"set-cookie2\", // handled by the server-side cookie jar\n \"content-encoding\", \"content-length\", \"transfer-encoding\", \"connection\", \"keep-alive\", \"trailer\",\n]);\n\nconst NULL_BODY_STATUS = new Set([101, 103, 204, 205, 304]);\nexport const DOCUMENT_DESTINATIONS = new Set([\"document\", \"iframe\", \"frame\", \"embed\", \"object\"]);\n\ntype BodyKind = \"html\" | \"js\" | \"worker\" | \"css\" | \"manifest\";\n\nfunction mimeOf(contentType: string): string {\n return contentType.split(\";\")[0].trim().toLowerCase();\n}\n\nfunction isJsMime(mime: string): boolean {\n return /^(?:text|application)\\/(?:x-)?(?:javascript|ecmascript)$/.test(mime) || mime === \"text/jscript\" || mime === \"application/x-javascript\";\n}\n\nfunction bodyKind(destination: RequestDestination, mime: string): BodyKind | null {\n const html = mime === \"text/html\" || mime === \"application/xhtml+xml\";\n const scriptLike = isJsMime(mime) || mime === \"\" || mime === \"text/plain\" || mime === \"application/octet-stream\";\n if (DOCUMENT_DESTINATIONS.has(destination)) return html || mime === \"\" ? \"html\" : null;\n switch (destination) {\n case \"script\":\n case \"audioworklet\":\n case \"paintworklet\":\n return scriptLike ? \"js\" : null;\n case \"worker\":\n case \"sharedworker\":\n return scriptLike ? \"worker\" : null;\n case \"style\":\n return mime === \"text/css\" || mime === \"\" || mime === \"text/plain\" ? \"css\" : null;\n case \"manifest\":\n return \"manifest\";\n default:\n return null;\n }\n}\n\nfunction rewriteLinkHeader(value: string, base: URL, f: Formatter): string {\n return value.replace(/<([^>]*)>/g, (_, url: string) => `<${f.rewriteUrl(url, base)}>`);\n}\n\nexport function rewriteResponseHeaders(pairs: Array<[string, string]>, target: URL, f: Formatter): Headers {\n const headers = new Headers();\n for (const [name, value] of pairs) {\n const lower = name.toLowerCase();\n if (DROP.has(lower)) continue;\n try {\n switch (lower) {\n case \"location\":\n case \"content-location\":\n headers.append(name, f.rewriteUrl(value, target));\n break;\n case \"refresh\":\n headers.append(name, f.refresh(value, target.href));\n break;\n case \"link\":\n headers.append(name, rewriteLinkHeader(value, target, f));\n break;\n default:\n headers.append(name, value);\n }\n } catch {\n // Header values the Fetch API refuses are dropped rather than failing the response.\n }\n }\n return headers;\n}\n\n/** JSON safe to embed in an inline <script>. */\nfunction scriptJson(value: unknown): string {\n return JSON.stringify(value).replace(/</g, \"\\\\u003c\").replace(LINE_SEPARATORS, (ch) => \"\\\\u\" + ch.charCodeAt(0).toString(16));\n}\n\n/** Everything the body rewriter needs besides the response itself. */\nexport interface ProxyContext {\n config: ButterConfig;\n f: Formatter;\n /** Active transport, so pages and workers can open WebSockets through it. */\n transport: TransportInfo;\n /** document.cookie view for documents. */\n documentCookies: string | null;\n}\n\nconst escapeAttr = (value: string) => value.replace(/&/g, \"&amp;\").replace(/\"/g, \"&quot;\");\n\n/** Markup injected at the top of every proxied document. */\nexport function documentInjection(ctx: ProxyContext, target: URL): string {\n const boot = { url: target.href, cookies: ctx.documentCookies ?? \"\", transport: ctx.transport };\n return (\n `<script data-butter>self.__butter$config=${scriptJson(ctx.config)};self.__butter$boot=${scriptJson(boot)};</script>` +\n transportUrls(ctx).map((url) => `<script src=\"${escapeAttr(url)}\" data-butter></script>`).join(\"\") +\n `<script src=\"${escapeAttr(ctx.config.client)}\" data-butter></script>`\n );\n}\n\n/**\n * Every configured transport is loaded (not only the active one) so that\n * transports can wrap or fall back to each other in pages too.\n */\nfunction transportUrls(ctx: ProxyContext): string[] {\n const urls = ctx.config.transports.map((t) => t.url);\n return urls.includes(ctx.transport.url) ? urls : [...urls, ctx.transport.url];\n}\n\n/** Code prepended to worker scripts so the runtime is installed before the worker's own code runs. */\nfunction workerPrelude(ctx: ProxyContext, module: boolean): string {\n const boot = `self.__butter$boot=${scriptJson({ transport: ctx.transport })};`;\n const scripts = [ctx.config.config, ...transportUrls(ctx), ctx.config.client].map((s) => JSON.stringify(s));\n if (!module) return `${boot}importScripts(${scripts.join(\",\")});`;\n // Module imports run before the module body, so the boot data is imported too.\n const bootModule = JSON.stringify(\"data:text/javascript,\" + encodeURIComponent(boot));\n return [bootModule, ...scripts].map((s) => `import ${s};`).join(\"\");\n}\n\nfunction rewriteManifest(text: string, base: URL, f: Formatter): string {\n try {\n const manifest = JSON.parse(text);\n for (const key of [\"start_url\", \"scope\", \"id\"]) {\n if (typeof manifest[key] === \"string\") manifest[key] = f.rewriteUrl(manifest[key], base);\n }\n for (const listKey of [\"icons\", \"screenshots\", \"shortcuts\"]) {\n const list = manifest[listKey];\n if (!Array.isArray(list)) continue;\n for (const item of list) {\n if (item && typeof item.src === \"string\") item.src = f.rewriteUrl(item.src, base);\n if (item && typeof item.url === \"string\") item.url = f.rewriteUrl(item.url, base);\n }\n }\n return JSON.stringify(manifest);\n } catch {\n return text;\n }\n}\n\nfunction rewriteBody(kind: BodyKind, text: string, route: Route, ctx: ProxyContext): string {\n const { f } = ctx;\n switch (kind) {\n case \"html\":\n return f.html(text, { base: route.target.href, inject: documentInjection(ctx, route.target) });\n case \"js\":\n return f.js(text);\n case \"worker\": {\n const prelude = workerPrelude(ctx, route.moduleWorker);\n const code = f.js(text);\n // Keep a \"use strict\" directive in first position.\n const directive = !route.moduleWorker && code.match(/^\\s*(['\"])use strict\\1\\s*;?/);\n return directive ? directive[0] + prelude + code.slice(directive[0].length) : prelude + code;\n }\n case \"css\":\n return f.css(text, route.target);\n case \"manifest\":\n return rewriteManifest(text, route.target, f);\n }\n}\n\nconst DEFAULT_MIME: Record<BodyKind, string> = {\n html: \"text/html\",\n js: \"text/javascript\",\n worker: \"text/javascript\",\n css: \"text/css\",\n manifest: \"application/manifest+json\",\n};\n\nexport async function buildResponse(request: Request, route: Route, res: TransportResponse, ctx: ProxyContext): Promise<Response> {\n const headers = rewriteResponseHeaders(res.headers, route.target, ctx.f);\n const nullBody = NULL_BODY_STATUS.has(res.status) || request.method === \"HEAD\";\n let body: BodyInit | null = nullBody ? null : (res.body as BodyInit | null);\n\n if (!nullBody && res.body != null) {\n const contentType = headers.get(\"content-type\") || \"\";\n const kind = bodyKind(request.destination, mimeOf(contentType));\n if (kind) {\n const buffer = await new Response(res.body as BodyInit).arrayBuffer();\n const text = decodeText(buffer, contentType, kind === \"html\");\n body = rewriteBody(kind, text, route, ctx);\n headers.set(\"content-type\", withUtf8(contentType, DEFAULT_MIME[kind]));\n }\n }\n\n const status = res.status >= 200 && res.status <= 599 ? res.status : 502;\n return new Response(body, { status, statusText: res.statusText, headers });\n}\n", "import { TransportError } from \"@axis-network/butter-transport\";\n\nconst escapeHtml = (s: string) =>\n s.replace(/[&<>\"']/g, (ch) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" })[ch]!);\n\nexport function describeError(err: unknown): { code: string; message: string; status: number } {\n // Transports bundle their own copy of TransportError, so match by shape.\n if (TransportError.is(err)) return { code: err.code, message: err.message, status: err.status || 502 };\n if (err instanceof Error) return { code: \"INTERNAL_ERROR\", message: err.message, status: 500 };\n return { code: \"INTERNAL_ERROR\", message: String(err), status: 500 };\n}\n\nexport function errorResponse(request: Request, target: URL | null, err: unknown): Response {\n const { code, message, status } = describeError(err);\n const headers = { \"X-Butter-Error-Code\": code, \"Cache-Control\": \"no-store\" };\n\n if (request.mode !== \"navigate\" && request.destination !== \"iframe\" && request.destination !== \"frame\") {\n return new Response(`butter-col: ${code}: ${message}`, {\n status,\n headers: { ...headers, \"Content-Type\": \"text/plain; charset=utf-8\" },\n });\n }\n\n const where = target ? escapeHtml(target.href) : \"the requested page\";\n const html = `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>butter-col \u00B7 ${escapeHtml(code)}</title>\n<style>\n :root { color-scheme: light dark; --bg: #fbf8f1; --fg: #2a2418; --muted: #75694f; --accent: #e3a92b; --card: #fff; }\n @media (prefers-color-scheme: dark) { :root { --bg: #17140e; --fg: #f1eadb; --muted: #a99c80; --card: #221e16; } }\n body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: var(--bg); color: var(--fg);\n font: 16px/1.55 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif; }\n main { max-width: 34rem; margin: 1.5rem; padding: 2rem; background: var(--card); border-radius: 14px;\n border-top: 5px solid var(--accent); box-shadow: 0 10px 30px rgb(0 0 0 / .08); }\n h1 { margin: 0 0 .5rem; font-size: 1.35rem; }\n code { font: .85em ui-monospace, SFMono-Regular, Consolas, monospace; overflow-wrap: anywhere; }\n .code { color: var(--muted); font-size: .85rem; letter-spacing: .04em; }\n button { margin-top: 1rem; font: inherit; padding: .5rem 1rem; border-radius: 8px; border: 0; background: var(--accent); color: #2a2418; cursor: pointer; }\n</style>\n</head>\n<body>\n<main>\n <div class=\"code\">${escapeHtml(code)}</div>\n <h1>butter-col couldn't load this page</h1>\n <p>${escapeHtml(message)}</p>\n <p><code>${where}</code></p>\n <button onclick=\"location.reload()\">Try again</button>\n</main>\n</body>\n</html>`;\n return new Response(html, { status, headers: { ...headers, \"Content-Type\": \"text/html; charset=utf-8\" } });\n}\n", "/**\n * Cookie jar for transports with meta.cookies === \"client\" (Bare, Wisp-style\n * transports, ...): a port of server/lua/butter/cookiejar.lua, kept in the\n * service worker and persisted to IndexedDB.\n *\n * Transports with meta.cookies === \"server\" (the built-in \"butter\" transport)\n * never touch this; their backend keeps the jar.\n */\n\nexport interface Cookie {\n name: string;\n value: string;\n domain: string;\n hostOnly: boolean;\n path: string;\n secure: boolean;\n httpOnly: boolean;\n sameSite?: string;\n /** Expiry in ms since the epoch; absent for session cookies. */\n expires?: number;\n created: number;\n}\n\ninterface Target {\n host: string;\n path: string;\n secure: boolean;\n}\n\nconst MONTHS: Record<string, number> = { jan: 1, feb: 2, mar: 3, apr: 4, may: 5, jun: 6, jul: 7, aug: 8, sep: 9, oct: 10, nov: 11, dec: 12 };\n\nfunction target(url: URL): Target {\n return {\n host: url.hostname.replace(/^\\[|\\]$/g, \"\").toLowerCase(),\n path: url.pathname || \"/\",\n secure: url.protocol === \"https:\" || url.protocol === \"wss:\",\n };\n}\n\n/** RFC 6265 section 5.1.1 date parsing; returns ms since the epoch. */\nexport function parseCookieDate(value: string): number | null {\n let hour: number | undefined, minute = 0, second = 0, day: number | undefined, month: number | undefined, year: number | undefined;\n for (const token of value.split(/[\\s,;\\-/]+/)) {\n if (!token) continue;\n const time = token.match(/^(\\d\\d?):(\\d\\d?):(\\d\\d?)/);\n if (time && hour === undefined) {\n hour = Number(time[1]);\n minute = Number(time[2]);\n second = Number(time[3]);\n } else if (day === undefined && /^\\d\\d?$/.test(token)) {\n day = Number(token);\n } else if (month === undefined && MONTHS[token.slice(0, 3).toLowerCase()]) {\n month = MONTHS[token.slice(0, 3).toLowerCase()];\n } else if (year === undefined && /^\\d{2,4}$/.test(token)) {\n year = Number(token);\n }\n }\n if (hour === undefined || day === undefined || month === undefined || year === undefined) return null;\n if (year >= 70 && year <= 99) year += 1900;\n else if (year < 70) year += 2000;\n if (day < 1 || day > 31 || year < 1601 || hour > 23 || minute > 59 || second > 59) return null;\n return Date.UTC(year, month - 1, day, hour, minute, second);\n}\n\nexport function defaultPath(path: string): string {\n if (!path.startsWith(\"/\")) return \"/\";\n const last = path.lastIndexOf(\"/\");\n return last <= 0 ? \"/\" : path.slice(0, last);\n}\n\nconst isIp = (host: string) => /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(host) || host.includes(\":\");\n\nexport function domainMatch(host: string, domain: string): boolean {\n if (host === domain) return true;\n if (isIp(host)) return false;\n return host.length > domain.length && host.endsWith(\".\" + domain);\n}\n\nexport function pathMatch(requestPath: string, cookiePath: string): boolean {\n if (requestPath === cookiePath) return true;\n if (!requestPath.startsWith(cookiePath)) return false;\n return cookiePath.endsWith(\"/\") || requestPath[cookiePath.length] === \"/\";\n}\n\n/** Parse one Set-Cookie value received from `url`. */\nexport function parseSetCookie(header: string, url: URL, now: number): Cookie | null {\n const t = target(url);\n const semi = header.indexOf(\";\");\n const pair = semi === -1 ? header : header.slice(0, semi);\n const attrs = semi === -1 ? \"\" : header.slice(semi);\n const eq = pair.indexOf(\"=\");\n const name = eq === -1 ? \"\" : pair.slice(0, eq).trim();\n const value = eq === -1 ? pair.trim() : pair.slice(eq + 1).trim();\n if (!name && !value) return null;\n if (name.length + value.length > 4096) return null;\n // eslint-disable-next-line no-control-regex\n if (/[\\x00-\\x1f\\x7f]/.test(name + value)) return null;\n\n const cookie: Cookie = { name, value, domain: t.host, hostOnly: true, path: \"/\", secure: false, httpOnly: false, created: now };\n let maxAge: number | undefined;\n let expires: number | undefined;\n let domain: string | undefined;\n let path: string | undefined;\n for (const attr of attrs.split(\";\").slice(1)) {\n const at = attr.indexOf(\"=\");\n const key = (at === -1 ? attr : attr.slice(0, at)).trim().toLowerCase();\n const val = at === -1 ? \"\" : attr.slice(at + 1).trim();\n if (key === \"expires\") expires = parseCookieDate(val) ?? expires;\n else if (key === \"max-age\" && /^-?\\d+$/.test(val)) maxAge = Number(val);\n else if (key === \"domain\" && val) domain = val.replace(/^\\./, \"\").toLowerCase();\n else if (key === \"path\" && val.startsWith(\"/\")) path = val;\n else if (key === \"secure\") cookie.secure = true;\n else if (key === \"httponly\") cookie.httpOnly = true;\n else if (key === \"samesite\") cookie.sameSite = val.toLowerCase();\n }\n\n if (maxAge !== undefined) cookie.expires = maxAge <= 0 ? 0 : now + maxAge * 1000;\n else if (expires !== undefined) cookie.expires = expires;\n\n if (domain) {\n if (!domainMatch(t.host, domain)) return null;\n if (domain !== t.host && !domain.includes(\".\")) return null; // \"com\", \"localhost\"\n cookie.domain = domain;\n cookie.hostOnly = false;\n }\n cookie.path = path ?? defaultPath(t.path);\n\n if (cookie.secure && !t.secure) return null;\n if (name.startsWith(\"__Secure-\") && !cookie.secure) return null;\n if (name.startsWith(\"__Host-\") && !(cookie.secure && cookie.hostOnly && cookie.path === \"/\")) return null;\n return cookie;\n}\n\nexport interface UpsertOptions {\n /** From document.cookie: may not create or overwrite HttpOnly cookies. */\n fromScript?: boolean;\n max?: number;\n}\n\nexport function upsert(list: Cookie[], cookie: Cookie, now: number, opts: UpsertOptions = {}): Cookie[] {\n if (opts.fromScript && cookie.httpOnly) return list;\n const out: Cookie[] = [];\n let created: number | undefined;\n for (const c of list) {\n if (c.expires !== undefined && c.expires <= now) continue;\n if (c.name === cookie.name && c.domain === cookie.domain && c.path === cookie.path) {\n if (opts.fromScript && c.httpOnly) return list;\n created = c.created;\n continue;\n }\n out.push(c);\n }\n cookie.created = created ?? now;\n if (!(cookie.expires !== undefined && cookie.expires <= now)) out.push(cookie);\n const max = opts.max ?? 3000;\n if (out.length > max) {\n out.sort((a, b) => b.created - a.created);\n out.length = max;\n }\n return out;\n}\n\nexport function cookieHeader(list: Cookie[], url: URL, now: number, opts: { forScript?: boolean } = {}): string {\n const t = target(url);\n return list\n .filter((c) =>\n !(c.expires !== undefined && c.expires <= now) &&\n (c.hostOnly ? c.domain === t.host : domainMatch(t.host, c.domain)) &&\n pathMatch(t.path, c.path) &&\n (!c.secure || t.secure) &&\n !(opts.forScript && c.httpOnly))\n .sort((a, b) => b.path.length - a.path.length || a.created - b.created)\n .map((c) => (c.name ? `${c.name}=${c.value}` : c.value))\n .join(\"; \");\n}\n\n// --- persistence ----------------------------------------------------------------\n\nconst DB_NAME = \"butter-col\";\nconst STORE = \"kv\";\n\nfunction openDb(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const req = indexedDB.open(DB_NAME, 1);\n req.onupgradeneeded = () => req.result.createObjectStore(STORE);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\n/** Tiny key-value store shared by the cookie jar and the transport settings. */\nexport const kv = {\n async get<T>(key: string): Promise<T | undefined> {\n const db = await openDb();\n return new Promise((resolve, reject) => {\n const req = db.transaction(STORE, \"readonly\").objectStore(STORE).get(key);\n req.onsuccess = () => resolve(req.result as T | undefined);\n req.onerror = () => reject(req.error);\n });\n },\n async set(key: string, value: unknown): Promise<void> {\n const db = await openDb();\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).put(value, key);\n tx.oncomplete = () => resolve();\n tx.onerror = () => reject(tx.error);\n });\n },\n};\n\n/** The service worker's jar: an in-memory list mirrored to IndexedDB. */\nexport function createCookieStore() {\n let list: Promise<Cookie[]> = kv.get<Cookie[]>(\"cookies\").then((v) => v ?? [], () => []);\n let writes = Promise.resolve();\n\n const save = (next: Cookie[]) => {\n writes = writes.then(() => kv.set(\"cookies\", next)).catch((err) => console.warn(\"[butter] could not save cookies\", err));\n };\n\n return {\n async header(url: URL, opts?: { forScript?: boolean }): Promise<string> {\n return cookieHeader(await list, url, Date.now(), opts);\n },\n async store(url: URL, setCookies: string[], opts?: UpsertOptions): Promise<void> {\n // Chain updates so concurrent responses can't overwrite each other.\n list = list.then((current) => {\n const now = Date.now();\n for (const value of setCookies) {\n const cookie = parseSetCookie(value, url, now);\n if (cookie) current = upsert(current, cookie, now, opts);\n }\n save(current);\n return current;\n });\n await list;\n },\n };\n}\n", "/**\n * Transport management in the service worker: loads every transport listed\n * in the configuration, remembers which one the visitor picked (IndexedDB),\n * and hands out the active instance.\n */\nimport {\n createTransport,\n getTransportFactory,\n type ButterPublicConfig,\n type ButterTransport,\n type TransportEntry,\n} from \"@axis-network/butter-transport\";\nimport { kv } from \"./cookie-jar.js\";\n\nexport interface TransportSelection {\n name: string;\n /** Visitor-supplied options; when absent the configured options apply. */\n options?: unknown;\n}\n\nexport interface ActiveTransport {\n transport: ButterTransport;\n entry: TransportEntry;\n options: unknown;\n}\n\n/** What pages are told about a transport (sent in boot data and to the controller). */\nexport interface TransportInfo {\n name: string;\n url: string;\n options: unknown;\n}\n\nexport function createTransportManager(scope: ServiceWorkerGlobalScope, config: ButterPublicConfig) {\n // importScripts only works while the worker script is evaluating, so every\n // configured transport is loaded up front; the visitor can switch among them.\n for (const entry of config.transports) {\n try {\n scope.importScripts(new URL(entry.url, scope.location.href).href);\n } catch (err) {\n console.error(`[butter] could not load transport \"${entry.name}\" from ${entry.url}:`, err);\n }\n }\n\n const env = {\n scope: \"serviceworker\" as const,\n origin: scope.location.origin,\n config,\n fetch: scope.fetch.bind(scope),\n WebSocket: (scope as unknown as { WebSocket?: typeof WebSocket }).WebSocket,\n };\n\n const entryFor = (name: string) => config.transports.find((e) => e.name === name);\n let selection: TransportSelection | null = null;\n let current: Promise<ActiveTransport> | null = null;\n\n async function loadSelection(): Promise<TransportSelection> {\n if (selection) return selection;\n const stored = await kv.get<TransportSelection>(\"transport\").catch(() => undefined);\n selection = stored && entryFor(stored.name) ? stored : { name: config.defaultTransport };\n return selection;\n }\n\n async function start(): Promise<ActiveTransport> {\n const sel = await loadSelection();\n const entry = entryFor(sel.name) ?? entryFor(config.defaultTransport) ?? config.transports[0];\n if (!entry) throw new Error(\"butter: no transports are configured\");\n const options = sel.options !== undefined ? sel.options : entry.options;\n const transport = await createTransport(entry.name, options, env);\n return { transport, entry, options };\n }\n\n function active(): Promise<ActiveTransport> {\n if (!current) {\n current = start();\n current.catch(() => {\n current = null; // retry on the next request\n });\n }\n return current;\n }\n\n async function select(next: TransportSelection): Promise<TransportInfo> {\n if (!entryFor(next.name)) throw new Error(`Transport \"${next.name}\" is not offered by this deployment.`);\n if (!getTransportFactory(next.name)) throw new Error(`Transport \"${next.name}\" failed to load.`);\n const previous = current;\n selection = { name: next.name, options: next.options };\n current = null;\n const started = await active(); // fail before persisting a broken choice\n await kv.set(\"transport\", selection);\n previous?.then((p) => p.transport.destroy?.()).catch(() => {});\n return info(started);\n }\n\n async function reset(): Promise<TransportInfo> {\n const previous = current;\n selection = { name: config.defaultTransport };\n current = null;\n await kv.set(\"transport\", undefined);\n previous?.then((p) => p.transport.destroy?.()).catch(() => {});\n return info(await active());\n }\n\n const info = (a: ActiveTransport): TransportInfo => ({ name: a.entry.name, url: a.entry.url, options: a.options });\n\n function list() {\n return config.transports.map((e) => ({ name: e.name, url: e.url, loaded: !!getTransportFactory(e.name) }));\n }\n\n return { active, select, reset, list, info };\n}\n\nexport type TransportManager = ReturnType<typeof createTransportManager>;\n", "/// <reference lib=\"webworker\" />\n/**\n * butter-col service worker.\n *\n * Registered at /butter/sw.js with scope \"/\" (the server sends\n * Service-Worker-Allowed: /). It intercepts requests made by proxied pages,\n * sends them through the active transport and rewrites what comes back.\n */\nimport { TransportError, TRANSPORT_ERROR_CODES, type TransportResponse } from \"@axis-network/butter-transport\";\nimport { createFormatter } from \"../formatter/index.js\";\nimport { loadConfig } from \"./config.js\";\nimport { createRouter, type Route } from \"./route.js\";\nimport { buildRequestHeaders } from \"./request.js\";\nimport { buildResponse, DOCUMENT_DESTINATIONS } from \"./response.js\";\nimport { errorResponse } from \"./error-page.js\";\nimport { createTransportManager } from \"./transports.js\";\nimport { createCookieStore } from \"./cookie-jar.js\";\n\ndeclare const self: ServiceWorkerGlobalScope;\n\nconst config = loadConfig(self);\nconst formatter = createFormatter({ origin: self.location.origin, prefix: config.prefix, codec: config.codec });\nconst router = createRouter(self, config, formatter);\nconst transports = createTransportManager(self, config);\nconst cookies = createCookieStore();\n\nself.addEventListener(\"install\", () => {\n void self.skipWaiting();\n});\n\nself.addEventListener(\"activate\", (event) => {\n event.waitUntil(self.clients.claim());\n});\n\n// --- messages from pages (ButterController, client runtime) ----------------------\n\ntype Message =\n | { type: \"butter:version\" }\n | { type: \"butter:get-transport\" }\n | { type: \"butter:set-transport\"; name: string; options?: unknown }\n | { type: \"butter:reset-transport\" }\n | { type: \"butter:socket-cookies\"; url: string }\n | { type: \"butter:store-cookies\"; url: string; setCookies: string[] };\n\nasync function handleMessage(message: Message): Promise<unknown> {\n switch (message.type) {\n case \"butter:version\":\n return { version: config.version };\n case \"butter:get-transport\":\n return { active: transports.info(await transports.active()), available: transports.list() };\n case \"butter:set-transport\":\n return { active: await transports.select({ name: message.name, options: message.options }) };\n case \"butter:reset-transport\":\n return { active: await transports.reset() };\n case \"butter:socket-cookies\": {\n const { transport } = await transports.active();\n if (transport.meta.cookies !== \"client\") return { cookie: \"\" };\n return { cookie: await cookies.header(new URL(message.url)) };\n }\n case \"butter:store-cookies\": {\n const { transport } = await transports.active();\n if (transport.meta.cookies === \"client\" && Array.isArray(message.setCookies)) {\n await cookies.store(new URL(message.url), message.setCookies.map(String));\n }\n return { ok: true };\n }\n default:\n throw new Error(\"Unknown message\");\n }\n}\n\n/**\n * Changing the transport decides where all of a visitor's traffic goes, so\n * only butter-col's own pages (the launcher) may do it, never proxied ones:\n * a hostile site could otherwise point the visitor at its own backend.\n */\nfunction mayChangeTransport(source: ExtendableMessageEvent[\"source\"]): boolean {\n // Only window/worker clients have a URL; MessagePorts and ServiceWorkers are refused.\n if (!source || !(\"url\" in source) || typeof source.url !== \"string\") return false;\n let url: URL;\n try {\n url = new URL(source.url);\n } catch {\n return false;\n }\n return url.origin === self.location.origin && !url.pathname.startsWith(config.prefix) && formatter.unrewriteUrl(source.url) === source.url;\n}\n\nself.addEventListener(\"message\", (event) => {\n const message = event.data as Message;\n if (!message || typeof message.type !== \"string\" || !message.type.startsWith(\"butter:\")) return;\n const reply = (data: unknown) => (event.ports[0] ?? event.source)?.postMessage(data);\n if ((message.type === \"butter:set-transport\" || message.type === \"butter:reset-transport\") && !mayChangeTransport(event.source)) {\n reply({ ok: false, error: \"Transports can only be changed from butter-col's own pages.\" });\n return;\n }\n event.waitUntil(\n handleMessage(message).then(\n (result) => reply({ ok: true, result }),\n (err) => reply({ ok: false, error: String((err as Error)?.message ?? err) }),\n ),\n );\n});\n\n// --- fetch -------------------------------------------------------------------------\n\nself.addEventListener(\"fetch\", (event) => {\n const url = new URL(event.request.url);\n if (url.origin === self.location.origin && url.pathname === config.transport.cookie && event.request.method === \"POST\") {\n event.respondWith(handleCookieWrite(event.request));\n return;\n }\n if (router.canSkip(event.request)) return;\n event.respondWith(handle(event));\n});\n\n/**\n * document.cookie writes: client-cookie transports keep the jar here; for\n * server-cookie transports the request goes on to the server.\n */\nasync function handleCookieWrite(request: Request): Promise<Response> {\n const { transport } = await transports.active().catch(() => ({ transport: null }));\n if (!transport || transport.meta.cookies !== \"client\") return fetch(request);\n try {\n const data = (await request.json()) as { url?: unknown; cookie?: unknown };\n if (typeof data.url !== \"string\" || typeof data.cookie !== \"string\" || data.cookie.length > 4096) {\n return new Response(null, { status: 400 });\n }\n await cookies.store(new URL(data.url), [data.cookie], { fromScript: true });\n return new Response(null, { status: 204 });\n } catch {\n return new Response(null, { status: 400 });\n }\n}\n\nasync function handle(event: FetchEvent): Promise<Response> {\n const result = await router.route(event);\n if (result === \"passthrough\") return fetch(event.request);\n if (result === \"invalid\") {\n return new Response(\"butter-col: this URL could not be decoded.\", {\n status: 400,\n headers: { \"Content-Type\": \"text/plain; charset=utf-8\" },\n });\n }\n try {\n return await proxy(event.request, result);\n } catch (err) {\n if ((err as Error)?.name === \"AbortError\") return Response.error();\n return errorResponse(event.request, result.target, err);\n }\n}\n\nfunction checkResponse(res: TransportResponse | undefined, name: string): TransportResponse {\n if (!res || !Number.isInteger(res.status) || !Array.isArray(res.headers)) {\n throw new TransportError(TRANSPORT_ERROR_CODES.BAD_RESPONSE, `The \"${name}\" transport returned an invalid response.`);\n }\n return res;\n}\n\nasync function proxy(request: Request, route: Route): Promise<Response> {\n const active = await transports.active();\n const { transport } = active;\n const clientCookies = transport.meta.cookies === \"client\";\n const isDocument = DOCUMENT_DESTINATIONS.has(request.destination);\n\n const headers = buildRequestHeaders(request, route, formatter);\n if (clientCookies) {\n const cookie = await cookies.header(route.target);\n if (cookie) headers[\"cookie\"] = cookie;\n }\n\n const hasBody = request.method !== \"GET\" && request.method !== \"HEAD\";\n const res = checkResponse(\n await transport.request({\n url: route.target,\n method: request.method,\n headers,\n body: hasBody ? await request.arrayBuffer() : null,\n signal: request.signal,\n context: {\n destination: request.destination,\n mode: request.mode,\n wantDocumentCookies: isDocument && !clientCookies,\n },\n }),\n active.entry.name,\n );\n\n let documentCookies: string | null = null;\n if (clientCookies) {\n const setCookies = res.headers.filter(([name]) => name.toLowerCase() === \"set-cookie\").map(([, value]) => value);\n if (setCookies.length) await cookies.store(route.target, setCookies);\n if (isDocument) documentCookies = await cookies.header(route.target, { forScript: true });\n } else if (isDocument) {\n documentCookies = res.documentCookies ?? \"\";\n }\n\n return buildResponse(request, route, res, {\n config,\n f: formatter,\n transport: transports.info(active),\n documentCookies,\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["TRANSPORT_ERROR_CODES", "TransportError", "_TransportError", "code", "message", "status", "value", "registry", "getTransportFactory", "name", "registry", "listTransports", "createTransport", "options", "env", "factory", "transport", "problems", "validateTransport", "t", "meta", "none", "url", "encoded", "plain", "xorString", "str", "out", "i", "xor", "base64", "bytes", "bin", "b64", "codecs", "getCodec", "name", "codec", "MODULE_WORKER_MARK", "PASSTHROUGH_SCHEMES", "TARGET_SCHEME", "createUrlRewriter", "origin", "prefix", "codecName", "codec", "getCodec", "absPrefix", "decodeTail", "tail", "decoded", "isProxied", "url", "rewrite", "raw", "base", "opts", "str", "trimmed", "abs", "hash", "mark", "unrewrite", "rest", "hashAt", "search", "searchAt", "target", "rewriteSrcset", "value", "map", "out", "i", "n", "wsStart", "urlStart", "trailing", "commas", "descStart", "depth", "ch", "URL_FUNCTION", "IMPORT_STRING", "rewriteCss", "css", "base", "rewriteUrl", "map", "raw", "url", "rewritten", "match", "dq", "sq", "bare", "LOCATION", "EVAL_WRAP", "UNREWRITE", "NEEDS_REWRITE", "EXPR_KEYWORDS", "OBJECT_AFTER_PUNC", "OBJECT_AFTER_KEYWORD", "IMPORT_META_URL", "isIdStart", "c", "isIdPart", "isWhitespace", "isLineTerminator", "rewriteJs", "src", "n", "edits", "prevType", "prevVal", "prev2Type", "prev2Val", "braces", "parens", "inImport", "push", "type", "val", "skipTrivia", "pos", "end", "peekWord", "j", "start", "regexAllowed", "scanString", "i", "quote", "scanTemplate", "scanRegex", "inClass", "handleTemplate", "opens", "handleLocation", "top", "importing", "next", "d", "value", "escaped", "cc", "close", "afterDot", "kind", "isEvalCall", "open", "e", "a", "b", "out", "last", "text", "RAW_TEXT", "JS_TYPE", "URL_ATTRS", "NEUTRALIZE", "NAMED_ENTITIES", "ENTITY", "decodeEntities", "value", "match", "dec", "hex", "named", "code", "escapeAttribute", "isWs", "c", "isLetter", "rewriteAttribute", "f", "tag", "name", "ctx", "attr", "part", "trimmed", "parseStartTag", "src", "i", "n", "j", "attrs", "selfClosing", "start", "attrName", "k", "q", "raw", "close", "vs", "findAttr", "transformTag", "state", "changed", "equiv", "kind", "content", "rewriteRefresh", "href", "lower", "rename", "base", "serializeTag", "out", "last", "closeTagCache", "findRawTextEnd", "from", "re", "rewriteHtml", "html", "options", "inject", "lt", "next", "end", "type", "markup", "createFormatter", "config", "url", "createUrlRewriter", "formatter", "raw", "base", "opts", "proxied", "src", "rewriteCss", "rewriteJs", "options", "rewriteHtml", "tag", "name", "value", "ctx", "rewriteAttribute", "rewriteRefresh", "loadConfig", "scope", "url", "config", "createRouter", "scope", "config", "f", "ownOrigin", "targetOf", "url", "target", "initiatorOf", "event", "client", "referrer", "canSkip", "request", "route", "tail", "moduleWorker", "MODULE_WORKER_MARK", "decoded", "initiator", "SKIP", "registrableDomain", "host", "fetchSite", "initiator", "target", "computeReferrer", "request", "f", "raw", "unproxied", "referrer", "referrerHeader", "policy", "sameOrigin", "downgrade", "origin", "buildRequestHeaders", "route", "headers", "value", "name", "unsafeMethod", "crossOrigin", "charsetFromContentType", "contentType", "match", "prescanHtml", "bytes", "head", "meta", "bomCharset", "decodeText", "buffer", "isHtml", "label", "decoder", "withUtf8", "fallback", "LINE_SEPARATORS", "DROP", "NULL_BODY_STATUS", "DOCUMENT_DESTINATIONS", "mimeOf", "contentType", "isJsMime", "mime", "bodyKind", "destination", "html", "scriptLike", "rewriteLinkHeader", "value", "base", "f", "_", "url", "rewriteResponseHeaders", "pairs", "target", "headers", "name", "lower", "scriptJson", "ch", "escapeAttr", "documentInjection", "ctx", "boot", "transportUrls", "urls", "t", "workerPrelude", "module", "scripts", "rewriteManifest", "text", "manifest", "key", "listKey", "list", "item", "rewriteBody", "kind", "route", "prelude", "code", "directive", "DEFAULT_MIME", "buildResponse", "request", "res", "nullBody", "body", "buffer", "decodeText", "withUtf8", "status", "escapeHtml", "s", "ch", "describeError", "err", "TransportError", "errorResponse", "request", "target", "code", "message", "status", "headers", "where", "html", "MONTHS", "target", "url", "parseCookieDate", "value", "hour", "minute", "second", "day", "month", "year", "token", "time", "defaultPath", "path", "last", "isIp", "host", "domainMatch", "domain", "pathMatch", "requestPath", "cookiePath", "parseSetCookie", "header", "now", "t", "semi", "pair", "attrs", "eq", "name", "cookie", "maxAge", "expires", "attr", "at", "key", "val", "upsert", "list", "opts", "out", "created", "c", "max", "a", "b", "cookieHeader", "DB_NAME", "STORE", "openDb", "resolve", "reject", "req", "kv", "db", "tx", "createCookieStore", "v", "writes", "save", "next", "err", "setCookies", "current", "createTransportManager", "scope", "config", "entry", "err", "env", "entryFor", "name", "e", "selection", "current", "loadSelection", "stored", "kv", "start", "sel", "options", "createTransport", "active", "select", "next", "getTransportFactory", "previous", "started", "p", "info", "reset", "a", "list", "config", "loadConfig", "formatter", "createFormatter", "router", "createRouter", "transports", "createTransportManager", "cookies", "createCookieStore", "event", "handleMessage", "message", "transport", "mayChangeTransport", "source", "url", "reply", "data", "result", "err", "handleCookieWrite", "handle", "request", "proxy", "errorResponse", "checkResponse", "res", "name", "TransportError", "TRANSPORT_ERROR_CODES", "route", "active", "clientCookies", "isDocument", "DOCUMENT_DESTINATIONS", "headers", "buildRequestHeaders", "cookie", "hasBody", "documentCookies", "setCookies", "value", "buildResponse"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";(()=>{var a={UNREACHABLE:"TRANSPORT_UNREACHABLE",BAD_RESPONSE:"BAD_TRANSPORT_RESPONSE",DNS_FAILURE:"DNS_FAILURE",UPSTREAM_UNREACHABLE:"UPSTREAM_UNREACHABLE",UPSTREAM_TIMEOUT:"UPSTREAM_TIMEOUT",BLOCKED:"BLOCKED",ERROR:"TRANSPORT_ERROR"},c=class w extends Error{code;status;constructor(t,u,d=502){super(u),this.name="TransportError",this.code=t,this.status=d}static is(t){return t instanceof w||!!t&&typeof t=="object"&&t.name==="TransportError"&&typeof t.code=="string"}};function h(){return globalThis.__butter$transports??=new Map}var T=/^[a-z0-9][a-z0-9._-]{0,63}$/;function y(r,t){if(!T.test(r))throw new TypeError(`butter-transport: invalid transport name "${r}"`);if(typeof t!="function")throw new TypeError(`butter-transport: factory for "${r}" is not a function`);h().has(r)&&console.warn(`[butter] transport "${r}" registered twice; the last one wins`),h().set(r,t)}function m(r){return JSON.stringify(r).replace(/[^ -~]/g,t=>"\\u"+t.charCodeAt(0).toString(16).padStart(4,"0"))}function b(r,t){try{let u=JSON.parse(r);return new c(u.code||a.ERROR,u.message||"Transport error",t)}catch{return new c(a.ERROR,r,t)}}function l(r,t){let u=new URL(r?.endpoint??t.config.transport.fetch,t.origin).href,d=new URL(r?.websocket??t.config.transport.ws,t.origin);return d.protocol=d.protocol==="https:"?"wss:":"ws:",{meta:{name:"butter",version:t.config.version,description:"butter-col's built-in transport (OpenResty server).",cookies:"server",websockets:!0},async request(s){let f=new Headers;f.set("X-Butter-URL",s.url.href),f.set("X-Butter-Headers",m(s.headers)),s.context.wantDocumentCookies&&f.set("X-Butter-Want-Cookies","1");let o;try{o=await t.fetch(u,{method:s.method,headers:f,body:s.body,redirect:"manual",credentials:"same-origin",cache:"no-store",referrerPolicy:"no-referrer",signal:s.signal})}catch(e){throw e?.name==="AbortError"?e:new c(a.UNREACHABLE,"Could not reach the butter-col server.")}let i=o.headers.get("X-Butter-Error");if(i)throw b(i,o.status);let E=Number(o.headers.get("X-Butter-Status"));if(!E)throw new c(a.BAD_RESPONSE,`Unexpected response from the transport endpoint (HTTP ${o.status}).`);let p;try{let e=JSON.parse(o.headers.get("X-Butter-Headers")||"[]");p=Array.isArray(e)?e:[]}catch{throw new c(a.BAD_RESPONSE,"The transport sent malformed response headers.")}return{status:E,statusText:o.headers.get("X-Butter-Status-Text")||"",headers:p,body:o.body,documentCookies:o.headers.get("X-Butter-Document-Cookies")}},connect(s,f,o,i){let E=t.WebSocket;if(!E)throw new c(a.ERROR,"WebSocket is not available here.");let p=new URL(d.href);p.searchParams.set("url",s.href),o.origin&&p.searchParams.set("origin",o.origin);let e=new E(p.href,f);return e.binaryType="arraybuffer",e.onopen=()=>i.open(e.protocol,e.extensions),e.onmessage=n=>i.message(n.data),e.onerror=()=>i.error("The butter-col WebSocket tunnel failed."),e.onclose=n=>i.close(n.code,n.reason,n.wasClean),{send:n=>e.send(n),close:(n,R)=>e.close(n,R),get bufferedAmount(){return e.bufferedAmount}}}}}y("butter",l);})();
2
+ //# sourceMappingURL=butter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../butter-transport/src/errors.ts", "../../../butter-transport/src/util.ts", "../../../butter-transport/src/index.ts", "../../src/transports/butter.ts"],
4
+ "sourcesContent": ["/**\n * Error codes butter-col understands. Transports may use their own codes too\n * (prefix them, e.g. \"BARE_...\"); they are shown to the visitor as-is.\n */\nexport const TRANSPORT_ERROR_CODES = {\n /** The transport's backend could not be reached. */\n UNREACHABLE: \"TRANSPORT_UNREACHABLE\",\n /** The backend answered with something the transport could not understand. */\n BAD_RESPONSE: \"BAD_TRANSPORT_RESPONSE\",\n /** The target's host name did not resolve. */\n DNS_FAILURE: \"DNS_FAILURE\",\n /** The target refused or reset the connection, or TLS failed. */\n UPSTREAM_UNREACHABLE: \"UPSTREAM_UNREACHABLE\",\n /** The target did not answer in time. */\n UPSTREAM_TIMEOUT: \"UPSTREAM_TIMEOUT\",\n /** The backend refused this target (policy). */\n BLOCKED: \"BLOCKED\",\n /** Anything else. */\n ERROR: \"TRANSPORT_ERROR\",\n} as const;\n\n/**\n * Throw this from request() to give the visitor a meaningful error page.\n * `status` is the HTTP status of that page (502 by default).\n */\nexport class TransportError extends Error {\n readonly code: string;\n readonly status: number;\n\n constructor(code: string, message: string, status = 502) {\n super(message);\n this.name = \"TransportError\";\n this.code = code;\n this.status = status;\n }\n\n /** Errors cross realms (service worker \u2192 page); recognise them by shape too. */\n static is(value: unknown): value is TransportError {\n return value instanceof TransportError ||\n (!!value && typeof value === \"object\" && (value as Error).name === \"TransportError\" &&\n typeof (value as TransportError).code === \"string\");\n }\n}\n", "import type { HeaderList } from \"./types.js\";\n\n/** Headers / object / list \u2192 HeaderList (names lower-cased). */\nexport function headersToList(headers: Headers | Record<string, string | string[]> | HeaderList): HeaderList {\n const list: HeaderList = [];\n if (Array.isArray(headers)) {\n for (const [name, value] of headers) list.push([name.toLowerCase(), value]);\n } else if (typeof Headers !== \"undefined\" && headers instanceof Headers) {\n headers.forEach((value, name) => list.push([name, value]));\n // Headers joins Set-Cookie values; getSetCookie() keeps them apart.\n const setCookies = typeof headers.getSetCookie === \"function\" ? headers.getSetCookie() : [];\n if (setCookies.length) {\n const rest = list.filter(([name]) => name !== \"set-cookie\");\n return [...rest, ...setCookies.map((value): [string, string] => [\"set-cookie\", value])];\n }\n } else {\n for (const [name, value] of Object.entries(headers)) {\n for (const v of Array.isArray(value) ? value : [value]) list.push([name.toLowerCase(), String(v)]);\n }\n }\n return list;\n}\n\n/** HeaderList \u2192 object, joining repeated names with \", \" (Set-Cookie kept as array). */\nexport function listToObject(list: HeaderList): Record<string, string | string[]> {\n const out: Record<string, string | string[]> = {};\n for (const [name, value] of list) {\n const key = name.toLowerCase();\n if (key === \"set-cookie\") {\n const prev = out[key];\n out[key] = Array.isArray(prev) ? [...prev, value] : [value];\n } else {\n out[key] = key in out ? `${out[key]}, ${value}` : value;\n }\n }\n return out;\n}\n\n/** Read any TransportResponse body into an ArrayBuffer. */\nexport async function readBody(body: ReadableStream<Uint8Array> | ArrayBuffer | Blob | string | null): Promise<ArrayBuffer> {\n if (body == null) return new ArrayBuffer(0);\n if (body instanceof ArrayBuffer) return body;\n return new Response(body as BodyInit).arrayBuffer();\n}\n", "/**\n * @axis-network/butter-transport: the transport API for butter-col.\n *\n * Transport authors import registerTransport() and the types from here and\n * ship a classic script that registers their factory. butter-col loads that\n * script in the service worker, in proxied pages and in workers, and creates\n * an instance in each realm that needs one.\n */\nimport type { ButterTransport, TransportEnvironment, TransportFactory } from \"./types.js\";\n\nexport * from \"./types.js\";\nexport { TransportError, TRANSPORT_ERROR_CODES } from \"./errors.js\";\nexport { headersToList, listToObject, readBody } from \"./util.js\";\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __butter$transports: Map<string, TransportFactory> | undefined;\n}\n\n/** The registry is a global so separately bundled transports share it. */\nfunction registry(): Map<string, TransportFactory> {\n return (globalThis.__butter$transports ??= new Map());\n}\n\nconst NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;\n\n/**\n * Register a transport factory under `name`. Call it at the top level of your\n * transport's script.\n *\n * ```js\n * registerTransport(\"mine\", (options, env) => ({ meta: {...}, request(req) {...} }));\n * ```\n */\nexport function registerTransport<Options>(name: string, factory: TransportFactory<Options>): void {\n if (!NAME.test(name)) throw new TypeError(`butter-transport: invalid transport name \"${name}\"`);\n if (typeof factory !== \"function\") throw new TypeError(`butter-transport: factory for \"${name}\" is not a function`);\n if (registry().has(name)) console.warn(`[butter] transport \"${name}\" registered twice; the last one wins`);\n registry().set(name, factory as TransportFactory);\n}\n\nexport function getTransportFactory(name: string): TransportFactory | undefined {\n return registry().get(name);\n}\n\n/** Names of every registered transport in this realm. */\nexport function listTransports(): string[] {\n return [...registry().keys()];\n}\n\n/**\n * Create, validate and initialise a transport. Used by butter-col itself;\n * also handy in tests.\n */\nexport async function createTransport(name: string, options: unknown, env: TransportEnvironment): Promise<ButterTransport> {\n const factory = getTransportFactory(name);\n if (!factory) {\n throw new Error(`butter-transport: no transport named \"${name}\" is registered (loaded: ${listTransports().join(\", \") || \"none\"})`);\n }\n const transport = await factory(options, env);\n const problems = validateTransport(transport);\n if (problems.length) throw new TypeError(`butter-transport: \"${name}\" is invalid: ${problems.join(\"; \")}`);\n if (transport.meta.name !== name) {\n throw new TypeError(`butter-transport: \"${name}\" reports meta.name \"${transport.meta.name}\"`);\n }\n await transport.init?.();\n return transport;\n}\n\n/** Structural checks; returns a list of problems (empty when valid). */\nexport function validateTransport(transport: unknown): string[] {\n const problems: string[] = [];\n const t = transport as Partial<ButterTransport> | null;\n if (!t || typeof t !== \"object\") return [\"factory did not return an object\"];\n const meta = t.meta;\n if (!meta || typeof meta !== \"object\") problems.push(\"meta is missing\");\n else {\n if (typeof meta.name !== \"string\") problems.push(\"meta.name must be a string\");\n if (meta.cookies !== \"server\" && meta.cookies !== \"client\") problems.push('meta.cookies must be \"server\" or \"client\"');\n if (typeof meta.websockets !== \"boolean\") problems.push(\"meta.websockets must be a boolean\");\n if (meta.websockets && typeof t.connect !== \"function\") problems.push(\"meta.websockets is true but connect() is missing\");\n }\n if (typeof t.request !== \"function\") problems.push(\"request() is missing\");\n if (t.init !== undefined && typeof t.init !== \"function\") problems.push(\"init must be a function\");\n if (t.destroy !== undefined && typeof t.destroy !== \"function\") problems.push(\"destroy must be a function\");\n return problems;\n}\n", "/**\n * The \"butter\" transport: butter-col's built-in transport, talking to its own\n * OpenResty server over /butter/fetch and /butter/ws.\n *\n * HTTP request: <method> /butter/fetch\n * X-Butter-URL: <target>\n * X-Butter-Headers: <JSON object>\n * X-Butter-Want-Cookies: 1 (documents)\n * HTTP response: 200 + X-Butter-Status / X-Butter-Status-Text /\n * X-Butter-Headers (JSON [name, value][]) /\n * X-Butter-Document-Cookies, body streamed\n * WebSocket: /butter/ws?url=<target>&origin=<page origin>\n *\n * Cookies live in the server's per-session jar, so meta.cookies is \"server\".\n */\nimport {\n registerTransport,\n TransportError,\n TRANSPORT_ERROR_CODES,\n type ButterTransport,\n type HeaderList,\n type TransportEnvironment,\n} from \"@axis-network/butter-transport\";\n\nexport interface ButterTransportOptions {\n /** HTTP endpoint (default: config.transport.fetch). Relative to the proxy origin. */\n endpoint?: string;\n /** WebSocket endpoint (default: config.transport.ws). */\n websocket?: string;\n}\n\n/** JSON with every non-ASCII character escaped, so it is a valid header value. */\nfunction asciiJson(value: unknown): string {\n return JSON.stringify(value).replace(/[^ -~]/g, (ch) => \"\\\\u\" + ch.charCodeAt(0).toString(16).padStart(4, \"0\"));\n}\n\nfunction parseError(header: string, status: number): TransportError {\n try {\n const parsed = JSON.parse(header) as { code?: string; message?: string };\n return new TransportError(parsed.code || TRANSPORT_ERROR_CODES.ERROR, parsed.message || \"Transport error\", status);\n } catch {\n return new TransportError(TRANSPORT_ERROR_CODES.ERROR, header, status);\n }\n}\n\nexport function createButterTransport(options: ButterTransportOptions | undefined, env: TransportEnvironment): ButterTransport {\n const fetchEndpoint = new URL(options?.endpoint ?? env.config.transport.fetch, env.origin).href;\n const wsEndpoint = new URL(options?.websocket ?? env.config.transport.ws, env.origin);\n wsEndpoint.protocol = wsEndpoint.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n\n return {\n meta: {\n name: \"butter\",\n version: env.config.version,\n description: \"butter-col's built-in transport (OpenResty server).\",\n cookies: \"server\",\n websockets: true,\n },\n\n async request(req) {\n const headers = new Headers();\n headers.set(\"X-Butter-URL\", req.url.href);\n headers.set(\"X-Butter-Headers\", asciiJson(req.headers));\n if (req.context.wantDocumentCookies) headers.set(\"X-Butter-Want-Cookies\", \"1\");\n\n let res: Response;\n try {\n res = await env.fetch(fetchEndpoint, {\n method: req.method,\n headers,\n body: req.body,\n redirect: \"manual\",\n credentials: \"same-origin\",\n cache: \"no-store\",\n referrerPolicy: \"no-referrer\",\n signal: req.signal,\n });\n } catch (err) {\n if ((err as Error)?.name === \"AbortError\") throw err;\n throw new TransportError(TRANSPORT_ERROR_CODES.UNREACHABLE, \"Could not reach the butter-col server.\");\n }\n\n const error = res.headers.get(\"X-Butter-Error\");\n if (error) throw parseError(error, res.status);\n\n const status = Number(res.headers.get(\"X-Butter-Status\"));\n if (!status) {\n throw new TransportError(\n TRANSPORT_ERROR_CODES.BAD_RESPONSE,\n `Unexpected response from the transport endpoint (HTTP ${res.status}).`,\n );\n }\n\n let pairs: HeaderList;\n try {\n const parsed = JSON.parse(res.headers.get(\"X-Butter-Headers\") || \"[]\");\n pairs = Array.isArray(parsed) ? parsed : [];\n } catch {\n throw new TransportError(TRANSPORT_ERROR_CODES.BAD_RESPONSE, \"The transport sent malformed response headers.\");\n }\n\n return {\n status,\n statusText: res.headers.get(\"X-Butter-Status-Text\") || \"\",\n headers: pairs,\n body: res.body,\n documentCookies: res.headers.get(\"X-Butter-Document-Cookies\"),\n };\n },\n\n connect(url, protocols, headers, handlers) {\n const NativeWebSocket = env.WebSocket;\n if (!NativeWebSocket) throw new TransportError(TRANSPORT_ERROR_CODES.ERROR, \"WebSocket is not available here.\");\n const endpoint = new URL(wsEndpoint.href);\n endpoint.searchParams.set(\"url\", url.href);\n if (headers.origin) endpoint.searchParams.set(\"origin\", headers.origin);\n\n const ws = new NativeWebSocket(endpoint.href, protocols);\n ws.binaryType = \"arraybuffer\";\n ws.onopen = () => handlers.open(ws.protocol, ws.extensions);\n ws.onmessage = (event) => handlers.message(event.data);\n ws.onerror = () => handlers.error(\"The butter-col WebSocket tunnel failed.\");\n ws.onclose = (event) => handlers.close(event.code, event.reason, event.wasClean);\n\n return {\n send: (data) => ws.send(data as Parameters<WebSocket[\"send\"]>[0]),\n close: (code, reason) => ws.close(code, reason),\n get bufferedAmount() {\n return ws.bufferedAmount;\n },\n };\n },\n };\n}\n\nregisterTransport<ButterTransportOptions>(\"butter\", createButterTransport);\n"],
5
+ "mappings": "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",
6
+ "names": ["TRANSPORT_ERROR_CODES", "TransportError", "_TransportError", "code", "message", "status", "value", "registry", "NAME", "registerTransport", "name", "factory", "asciiJson", "value", "ch", "parseError", "header", "status", "parsed", "TransportError", "TRANSPORT_ERROR_CODES", "createButterTransport", "options", "env", "fetchEndpoint", "wsEndpoint", "req", "headers", "res", "err", "error", "pairs", "url", "protocols", "handlers", "NativeWebSocket", "endpoint", "ws", "event", "data", "code", "reason", "registerTransport"]
7
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * ButterController: the page-side API of butter-col.
3
+ *
4
+ * import { ButterController } from "/butter/controller.mjs";
5
+ * const butter = await new ButterController().init();
6
+ * await butter.setTransport("bare", { server: "https://bare.example/" });
7
+ * butter.go("example.com");
8
+ *
9
+ * It installs the service worker, reads /butter/config.js, encodes URLs with
10
+ * the configured codec and talks to the service worker to list and switch
11
+ * transports (the choice is remembered per browser).
12
+ */
13
+ import type { ButterPublicConfig, TransportEntry } from "@axis-network/butter-transport";
14
+ export interface ButterControllerOptions {
15
+ /** Service worker script (default "/butter/sw.js"). */
16
+ serviceWorker?: string;
17
+ /** Registration scope (default "/"); the server must allow it with Service-Worker-Allowed. */
18
+ scope?: string;
19
+ /** Configuration script (default "/butter/config.js"). */
20
+ config?: string;
21
+ /** Search URL used by go() for input that is not a URL; "%s" is the query. */
22
+ searchEngine?: string;
23
+ }
24
+ export interface TransportStatus {
25
+ active: {
26
+ name: string;
27
+ url: string;
28
+ options: unknown;
29
+ };
30
+ available: Array<{
31
+ name: string;
32
+ url: string;
33
+ loaded: boolean;
34
+ }>;
35
+ }
36
+ export declare class ButterController {
37
+ #private;
38
+ readonly options: Required<ButterControllerOptions>;
39
+ config: ButterPublicConfig | null;
40
+ registration: ServiceWorkerRegistration | null;
41
+ constructor(options?: ButterControllerOptions);
42
+ /** Register the service worker (waiting until it is active) and load the configuration. */
43
+ init(): Promise<this>;
44
+ /** Proxy URL for an absolute target URL. */
45
+ encodeUrl(url: string | URL): string;
46
+ /** Target URL for a proxy URL (input returned unchanged if it is not one). */
47
+ decodeUrl(url: string | URL): string;
48
+ /** Turn what someone typed into a URL: full URL, bare domain, or a search. */
49
+ resolveInput(input: string): string;
50
+ /** Navigate this tab to `input` through the proxy. */
51
+ go(input: string): void;
52
+ /** Transports this deployment offers (from the configuration). */
53
+ get transports(): TransportEntry[];
54
+ /** Which transport is active for this browser, and which could be. */
55
+ getTransport(): Promise<TransportStatus>;
56
+ /**
57
+ * Switch transports for this browser. `options` replaces the configured
58
+ * options for that transport; omit it to use the deployment's defaults.
59
+ * Pages already open keep their WebSocket transport until reloaded.
60
+ */
61
+ setTransport(name: string, options?: unknown): Promise<TransportStatus["active"]>;
62
+ /** Go back to the deployment's default transport. */
63
+ resetTransport(): Promise<TransportStatus["active"]>;
64
+ }
65
+ export default ButterController;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * URL codecs. A codec turns an absolute target URL (without its fragment)
3
+ * into the string that follows the proxy prefix, and back again.
4
+ *
5
+ * Every codec must satisfy: decode(encode(url)) === url.
6
+ */
7
+ export type Codec = {
8
+ encode(url: string): string;
9
+ decode(encoded: string): string;
10
+ };
11
+ /** @type {Record<string, Codec>} */
12
+ export declare const codecs: Record<string, Codec>;
13
+ /**
14
+ * @param {string} name
15
+ * @returns {Codec}
16
+ */
17
+ export declare function getCodec(name: string): Codec;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * CSS rewriting: url(...) references and @import strings.
3
+ */
4
+ /**
5
+ * @param {string} css
6
+ * @param {string | URL} base
7
+ * @param {(url: string, base: string | URL) => string} rewriteUrl
8
+ * @returns {string}
9
+ */
10
+ export declare function rewriteCss(css: string, base: string | URL, rewriteUrl: (url: string, base: string | URL) => string): string;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * HTML rewriting with a small, forgiving tokenizer (service workers have no
3
+ * DOMParser). Only start tags are modified; text, comments and end tags are
4
+ * copied through byte for byte, and unchanged tags keep their exact source.
5
+ */
6
+ /** Attributes holding a single URL, on any element. */
7
+ export declare const URL_ATTRS: Set<string>;
8
+ /** @param {string} value */
9
+ export declare function decodeEntities(value: string): string;
10
+ /** @param {string} value */
11
+ export declare function escapeAttribute(value: string): string;
12
+ export type RewriteContext = {
13
+ /**
14
+ * Target base URL used to resolve relative URLs
15
+ */
16
+ base: string;
17
+ /**
18
+ * Markup inserted at the start of the document
19
+ */
20
+ inject?: string;
21
+ };
22
+ /**
23
+ * @typedef {object} RewriteContext
24
+ * @property {string} base Target base URL used to resolve relative URLs
25
+ * @property {string} [inject] Markup inserted at the start of the document
26
+ */
27
+ /**
28
+ * Rewrite one attribute value. Returns the new value (possibly identical).
29
+ * Shared by the HTML rewriter and the client runtime's setAttribute hook.
30
+ *
31
+ * @param {import("./index.js").Formatter} f
32
+ * @param {string} tag Lower-case tag name
33
+ * @param {string} name Attribute name
34
+ * @param {string} value Decoded attribute value
35
+ * @param {RewriteContext} ctx
36
+ * @returns {string}
37
+ */
38
+ export declare function rewriteAttribute(f: import("./index.js").Formatter, tag: string, name: string, value: string, ctx: RewriteContext): string;
39
+ export type Attr = {
40
+ name: string;
41
+ /**
42
+ * Decoded value, null for boolean attributes
43
+ */
44
+ value: string | null;
45
+ start: number;
46
+ end: number;
47
+ newName?: string;
48
+ newValue?: string | null;
49
+ };
50
+ /**
51
+ * Rewrite the URL in a refresh value such as "5; url=/next".
52
+ * @returns {string}
53
+ */
54
+ export declare function rewriteRefresh(f: any, value: any, base: any): string;
55
+ /**
56
+ * @param {string} html
57
+ * @param {import("./index.js").Formatter} f
58
+ * @param {RewriteContext} options
59
+ * @returns {string}
60
+ */
61
+ export declare function rewriteHtml(html: string, f: import("./index.js").Formatter, options: RewriteContext): string;