Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682// node:url. URL and URLSearchParams are the browser's own; the legacy API// (url.parse, url.format, url.resolve, Url) follows Node's lib/url.js closely// enough for parseurl, express and serve-static. Hostnames are lowercased and// IDNA-encoded through the browser's URL parser.import { Buffer } from "./buffer";import { qs } from "./querystring";import { refusedClass } from "./refuse";
const protocolPattern = /^[a-z0-9.+-]+:/i;const portPattern = /:[0-9]*$/;const hostPattern = /^\/\/[^@/]+@[^@/]+/;const simplePathPattern = /^(\/\/?(?!\/)[^?\s]*)(\?[^\s]*)?$/;const hostlessProtocol = new Set(["javascript", "javascript:"]);const unsafeProtocol = hostlessProtocol;const slashedProtocol = new Set([ "http", "http:", "https", "https:", "ftp", "ftp:", "gopher", "gopher:", "file", "file:", "ws", "ws:", "wss", "wss:",]);const AUTO_ESCAPE: Record<string, string> = { "\t": "%09", "\n": "%0A", "\r": "%0D", " ": "%20", '"': "%22", "'": "%27", "<": "%3C", ">": "%3E", "\\": "%5C", "^": "%5E", "`": "%60", "{": "%7B", "|": "%7C", "}": "%7D",};// RFC 2396 never allows these in a hostname; a host-ending char also ends it.const NON_HOST = new Set("\t\n\r \"%';<>\\^`{|}");const HOST_END = new Set("#/?");
function invalidArgType(name: string, expected: string, value: unknown): TypeError { const got = value === null ? "null" : value === undefined ? "undefined" : `type ${typeof value}`; return Object.assign(new TypeError(`The "${name}" argument must be ${expected}. Received ${got}`), { code: "ERR_INVALID_ARG_TYPE" });}
function toASCII(hostname: string): string { if (/^[\x21-\x7e]*$/.test(hostname)) return hostname; try { return new URL(`http://${hostname}`).hostname; } catch { return ""; }}
const isIpv6Hostname = (h: string): boolean => h.startsWith("[") && h.endsWith("]");
// --- url.domainToASCII / url.domainToUnicode (IDNA/UTS#46) ---// Node (via ada) builds a URL with a special scheme, sets its hostname to the input —// the WHATWG "set the host" setter (state override "hostname"): a ':' outside brackets,// or any forbidden host code point, fails outright; '/', '?', '#' or '\' truncate// rather than failing — then runs UTS#46 ToASCII on what's left (non-strict: no// Std3ASCIIRules, no DNS length check), collapsing an all-numeric trailing label to// IPv4. domainToUnicode runs the same pipeline, then Punycode-decodes any xn-- label// back to Unicode. Invalid input answers '' rather than throwing, matching Node.
const PUNY_BASE = 36, PUNY_TMIN = 1, PUNY_TMAX = 26, PUNY_SKEW = 38, PUNY_DAMP = 700;const PUNY_INITIAL_BIAS = 72, PUNY_INITIAL_N = 0x80;const MAX_INT = 0x7fffffff;
function punyAdapt(delta: number, numPoints: number, firstTime: boolean): number { let k = 0; delta = firstTime ? Math.floor(delta / PUNY_DAMP) : delta >> 1; delta += Math.floor(delta / numPoints); const threshold = ((PUNY_BASE - PUNY_TMIN) * PUNY_TMAX) >> 1; while (delta > threshold) { delta = Math.floor(delta / (PUNY_BASE - PUNY_TMIN)); k += PUNY_BASE; } return Math.floor(k + ((PUNY_BASE - PUNY_TMIN + 1) * delta) / (delta + PUNY_SKEW));}
/** A basic code point's digit value (a-z -> 0-25, 0-9 -> 26-35), or BASE if none. */function punyBasicToDigit(cp: number): number { if (cp >= 0x30 && cp < 0x3a) return 26 + (cp - 0x30); if (cp >= 0x41 && cp < 0x5b) return cp - 0x41; if (cp >= 0x61 && cp < 0x7b) return cp - 0x61; return PUNY_BASE;}
function punyDigitToBasic(digit: number): number { return digit + 22 + 75 * (digit < 26 ? 1 : 0);}
/** RFC 3492 Punycode decode: an "xn--" label's suffix to its Unicode code points. * Throws (RangeError) on malformed input — the same algorithm bestiejs/punycode.js * implements, which Node's own now-deprecated `punycode` module ported. */function punycodeDecode(input: string): number[] { const output: number[] = []; const inputLength = input.length; let i = 0, n = PUNY_INITIAL_N, bias = PUNY_INITIAL_BIAS; let basic = input.lastIndexOf("-"); if (basic < 0) basic = 0; for (let j = 0; j < basic; ++j) { const cp = input.charCodeAt(j); if (cp >= 0x80) throw new RangeError("not-basic"); output.push(cp); } for (let index = basic > 0 ? basic + 1 : 0; index < inputLength;) { const oldi = i; for (let w = 1, k = PUNY_BASE; ; k += PUNY_BASE) { if (index >= inputLength) throw new RangeError("invalid-input"); const digit = punyBasicToDigit(input.charCodeAt(index++)); if (digit >= PUNY_BASE) throw new RangeError("invalid-input"); if (digit > Math.floor((MAX_INT - i) / w)) throw new RangeError("overflow"); i += digit * w; const t = k <= bias ? PUNY_TMIN : (k >= bias + PUNY_TMAX ? PUNY_TMAX : k - bias); if (digit < t) break; const baseMinusT = PUNY_BASE - t; if (w > Math.floor(MAX_INT / baseMinusT)) throw new RangeError("overflow"); w *= baseMinusT; } const out = output.length + 1; bias = punyAdapt(i - oldi, out, oldi === 0); if (Math.floor(i / out) > MAX_INT - n) throw new RangeError("overflow"); n += Math.floor(i / out); i %= out; output.splice(i++, 0, n); } return output;}
/** RFC 3492 Punycode encode: Unicode code points to an "xn--" label's suffix. */function punycodeEncode(input: number[]): string { const output: string[] = []; const inputLength = input.length; let n = PUNY_INITIAL_N, delta = 0, bias = PUNY_INITIAL_BIAS; for (const cp of input) if (cp < 0x80) output.push(String.fromCharCode(cp)); const basicLength = output.length; let handled = basicLength; if (basicLength) output.push("-"); while (handled < inputLength) { let m = MAX_INT; for (const cp of input) if (cp >= n && cp < m) m = cp; const handledPlusOne = handled + 1; if (m - n > Math.floor((MAX_INT - delta) / handledPlusOne)) throw new RangeError("overflow"); delta += (m - n) * handledPlusOne; n = m; for (const cp of input) { if (cp < n) { delta++; if (delta > MAX_INT) throw new RangeError("overflow"); } if (cp === n) { let q = delta; for (let k = PUNY_BASE; ; k += PUNY_BASE) { const t = k <= bias ? PUNY_TMIN : (k >= bias + PUNY_TMAX ? PUNY_TMAX : k - bias); if (q < t) break; const baseMinusT = PUNY_BASE - t; output.push(String.fromCharCode(punyDigitToBasic(t + ((q - t) % baseMinusT)))); q = Math.floor((q - t) / baseMinusT); } output.push(String.fromCharCode(punyDigitToBasic(q))); bias = punyAdapt(delta, handledPlusOne, handled === basicLength); delta = 0; ++handled; } } ++delta; ++n; } return output.join("");}
const isAsciiLabel = (s: string): boolean => /^[\x00-\x7f]*$/.test(s);
/** Disallowed in a decoded Punycode label: C0/C1 controls and surrogate halves — the * real UTS#46 disallowed-code-point table is much larger, but this covers what comes * up in practice (a malformed "xn--" label that decodes to a control code point). */const isDisallowedDecoded = (cp: number): boolean => cp <= 0x1f || (cp >= 0x7f && cp <= 0x9f) || (cp >= 0xd800 && cp <= 0xdfff);
/** Forbidden (WHATWG forbidden-domain-code-point) in an already-ASCII-ized domain: * '/', '?', '#', '\\' and ':' can't reach here (the hostname setter already failed * or truncated on them), so only these remain to check. */function hasForbiddenDomainChar(s: string): boolean { for (let i = 0; i < s.length; i++) { const c = s.charCodeAt(i); if (c <= 0x20 || c === 0x25 || c === 0x3c || c === 0x3e || c === 0x40 || c === 0x5b || c === 0x5c || c === 0x5d || c === 0x5e || c === 0x7c) return true; } return false;}
/** A Unicode string to its ASCII form, or null if it can't Punycode-encode. */function encodeNonAsciiLabel(label: string): string | null { if (isAsciiLabel(label)) return label; try { return `xn--${punycodeEncode(Array.from(label, (c) => c.codePointAt(0)!))}`; } catch { return null; }}
/** A domain label to its ASCII form, or null if invalid. An already-ASCII label is * only lowercased (Node's non-strict mode skips Std3ASCIIRules and DNS length * checks); a label with non-ASCII content is Punycode-encoded; an "xn--" label is * decoded to validate it — it must hold no disallowed code point and re-encode back * to itself — and then kept as its lowercased self. */function domainLabelToAscii(label: string): string | null { const lower = label.toLowerCase(); if (lower.startsWith("xn--")) { let decoded: number[]; try { decoded = punycodeDecode(lower.slice(4)); } catch { return null; } if (decoded.length === 0 || decoded.some(isDisallowedDecoded)) return null; return encodeNonAsciiLabel(String.fromCodePoint(...decoded)) === lower ? lower : null; } return isAsciiLabel(label) ? lower : encodeNonAsciiLabel(lower);}
/** Byte-level percent-decode (the URL spec's UTF-8-decode-of-percent-decode), * tolerant of a stray '%' not followed by two hex digits. */function percentDecodeUtf8(input: string): string { const bytes = new TextEncoder().encode(input); const out: number[] = []; for (let i = 0; i < bytes.length; i++) { const b = bytes[i]!; if (b === 0x25 && i + 2 < bytes.length) { const hex = String.fromCharCode(bytes[i + 1]!, bytes[i + 2]!); if (/^[0-9a-fA-F]{2}$/.test(hex)) { out.push(parseInt(hex, 16)); i += 2; continue; } } out.push(b); } return new TextDecoder("utf-8", { fatal: false }).decode(new Uint8Array(out));}
function parseIPv4Part(part: string): number | null { if (part === "") return null; let radix = 10; let digits = part; if (/^0x/i.test(digits)) { radix = 16; digits = digits.slice(2); } else if (digits.length > 1 && digits[0] === "0") { radix = 8; digits = digits.slice(1); } if (digits === "") return 0; const re = radix === 16 ? /^[0-9a-f]+$/i : radix === 8 ? /^[0-7]+$/ : /^[0-9]+$/; return re.test(digits) ? parseInt(digits, radix) : null;}
function endsInANumber(domain: string): boolean { const parts = domain.split("."); if (parts.length > 1 && parts[parts.length - 1] === "") parts.pop(); const last = parts[parts.length - 1]; if (!last) return false; return /^[0-9]+$/.test(last) || parseIPv4Part(last) !== null;}
/** The WHATWG IPv4 parser: up to 4 dot-separated parts (decimal, octal "0..." or hex * "0x..."), the last absorbing whatever bits remain, folded to a dotted-quad. */function parseIPv4(domain: string): string | null { const parts = domain.split("."); if (parts.length > 1 && parts[parts.length - 1] === "") parts.pop(); if (parts.length > 4) return null; const numbers: number[] = []; for (const part of parts) { const n = parseIPv4Part(part); if (n === null) return null; numbers.push(n); } for (let i = 0; i < numbers.length - 1; i++) if (numbers[i]! > 255) return null; if (numbers[numbers.length - 1]! >= 256 ** (5 - numbers.length)) return null; let ipv4 = numbers.pop()!; for (let i = 0; i < numbers.length; i++) ipv4 += numbers[i]! * 256 ** (3 - i); return [(ipv4 >>> 24) & 255, (ipv4 >>> 16) & 255, (ipv4 >>> 8) & 255, ipv4 & 255].join(".");}
/** The "set the host" setter (state override "hostname") plus UTS#46 ToASCII, over a * special-scheme URL — what bindingUrl.domainToASCII/domainToUnicode run before * either answers. null means the setter or ToASCII failed (both exports then answer * ''). A "[...]" form is assumed to be IPv6 and passed through unvalidated. */function hostnameSetterToAscii(input: string): string | null { if (input.startsWith("[")) { return input.endsWith("]") && /^[0-9a-fA-F:.]*$/.test(input.slice(1, -1)) ? input : null; } let buffer = ""; let insideBrackets = false; for (let i = 0; i < input.length; i++) { const ch = input[i]!; if (ch === ":" && !insideBrackets) return null; if (ch === "/" || ch === "?" || ch === "#" || ch === "\\") break; if (ch === "[") insideBrackets = true; else if (ch === "]") insideBrackets = false; buffer += ch; } if (buffer === "") return null; const labels: string[] = []; for (const label of percentDecodeUtf8(buffer).split(".")) { if (label === "") { labels.push(label); continue; } const ascii = domainLabelToAscii(label); if (ascii === null) return null; labels.push(ascii); } const ascii = labels.join("."); if (hasForbiddenDomainChar(ascii)) return null; return endsInANumber(ascii) ? parseIPv4(ascii) : ascii;}
function missingArg(name: string): Error { return Object.assign(new TypeError(`The "${name}" argument must be specified`), { code: "ERR_MISSING_ARGS" });}
/** url.domainToASCII: Node throws ERR_MISSING_ARGS with no argument at all (so * `arguments.length`, not a default parameter), then coerces whatever was passed * like a template literal would (a Symbol throws the ordinary TypeError that is). */function domainToASCII(domain?: unknown): string { if (arguments.length < 1) throw missingArg("domain"); return hostnameSetterToAscii(`${domain}`) ?? "";}
function domainToUnicode(domain?: unknown): string { if (arguments.length < 1) throw missingArg("domain"); const ascii = hostnameSetterToAscii(`${domain}`); if (ascii === null) return ""; return ascii.split(".").map((label) => { if (!label.startsWith("xn--")) return label; try { return String.fromCodePoint(...punycodeDecode(label.slice(4))); } catch { return label; } }).join(".");}
export interface Url { protocol: string | null; slashes: boolean | null; auth: string | null; host: string | null; port: string | null; hostname: string | null; hash: string | null; search: string | null; query: string | Record<string, unknown> | null; pathname: string | null; path: string | null; href: string | null; parse(url: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url; format(): string; resolve(relative: string): string; resolveObject(relative: string | Url): Url; parseHost(): void;}
export const Url = function Url(this: Url) { this.protocol = null; this.slashes = null; this.auth = null; this.host = null; this.port = null; this.hostname = null; this.hash = null; this.search = null; this.query = null; this.pathname = null; this.path = null; this.href = null;} as unknown as { new (): Url; prototype: Url };
Url.prototype.parse = function parse(this: Url, url: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url { if (typeof url !== "string") throw invalidArgType("url", "of type string", url); // Trim whitespace, and turn backslashes before the query or hash into slashes. let hasHash = false; let hasAt = false; let rest = ""; let split = false; const trimmed = url.replace(/^[\x00-\x20\u00a0\ufeff]+|[\x00-\x20\u00a0\ufeff]+$/g, ""); for (const ch of trimmed) { if (!split) { if (ch === "@") hasAt = true; else if (ch === "#") { hasHash = true; split = true; } else if (ch === "?") split = true; rest += ch === "\\" ? "/" : ch; } else { if (ch === "#") hasHash = true; rest += ch; } }
if (!slashesDenoteHost && !hasHash && !hasAt) { const simplePath = simplePathPattern.exec(rest); if (simplePath) { this.path = rest; this.href = rest; this.pathname = simplePath[1]!; if (simplePath[2]) { this.search = simplePath[2]; this.query = parseQueryString ? qs.parse(this.search.slice(1)) : this.search.slice(1); } else if (parseQueryString) { this.search = null; this.query = Object.create(null); } return this; } }
const protoMatch = protocolPattern.exec(rest); let proto: string | undefined; let lowerProto: string | undefined; if (protoMatch) { proto = protoMatch[0]; lowerProto = proto.toLowerCase(); this.protocol = lowerProto; rest = rest.slice(proto.length); }
let slashes = false; if (slashesDenoteHost || proto || hostPattern.test(rest)) { slashes = rest.startsWith("//"); if (slashes && !(lowerProto && hostlessProtocol.has(lowerProto))) { rest = rest.slice(2); this.slashes = true; } }
if (!(lowerProto && hostlessProtocol.has(lowerProto)) && (slashes || (proto && !slashedProtocol.has(proto)))) { let hostEnd = -1; let atSign = -1; let nonHost = -1; for (let i = 0; i < rest.length; ++i) { const ch = rest[i]!; if (NON_HOST.has(ch)) { if (nonHost === -1) nonHost = i; } else if (HOST_END.has(ch)) { if (nonHost === -1) nonHost = i; hostEnd = i; } else if (ch === "@") { atSign = i; nonHost = -1; } if (hostEnd !== -1) break; } let start = 0; if (atSign !== -1) { this.auth = decodeURIComponent(rest.slice(0, atSign)); start = atSign + 1; } if (nonHost === -1) { this.host = rest.slice(start); rest = ""; } else { this.host = rest.slice(start, nonHost); rest = rest.slice(nonHost); } this.parseHost(); if (typeof this.hostname !== "string") this.hostname = ""; const hostname = this.hostname; const ipv6 = isIpv6Hostname(hostname); if (!ipv6) { for (let i = 0; i < hostname.length; ++i) { if ("/\\#?:".includes(hostname[i]!)) { this.hostname = hostname.slice(0, i); rest = `/${hostname.slice(i)}${rest}`; break; } } } this.hostname = this.hostname.length > 255 ? "" : this.hostname.toLowerCase(); if (this.hostname !== "" && !ipv6) this.hostname = toASCII(this.hostname); this.host = (this.hostname || "") + (this.port ? `:${this.port}` : ""); if (ipv6) { this.hostname = this.hostname.slice(1, -1); if (rest[0] !== "/") rest = `/${rest}`; } }
if (!(lowerProto && unsafeProtocol.has(lowerProto))) { rest = rest.replace(/[\t\n\r "'<>\\^`{|}]/g, (c) => AUTO_ESCAPE[c]!); }
let questionIdx = -1; let hashIdx = -1; for (let i = 0; i < rest.length; ++i) { if (rest[i] === "#") { this.hash = rest.slice(i); hashIdx = i; break; } else if (rest[i] === "?" && questionIdx === -1) { questionIdx = i; } } if (questionIdx !== -1) { const end = hashIdx === -1 ? rest.length : hashIdx; this.search = rest.slice(questionIdx, end); this.query = rest.slice(questionIdx + 1, end); if (parseQueryString) this.query = qs.parse(this.query); } else if (parseQueryString) { this.search = null; this.query = Object.create(null); } const firstIdx = questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx) ? questionIdx : hashIdx; if (firstIdx === -1) { if (rest.length > 0) this.pathname = rest; } else if (firstIdx > 0) { this.pathname = rest.slice(0, firstIdx); } if (lowerProto && slashedProtocol.has(lowerProto) && this.hostname && !this.pathname) this.pathname = "/"; if (this.pathname || this.search) this.path = (this.pathname || "") + (this.search || ""); this.href = this.format(); return this;};
Url.prototype.parseHost = function parseHost(this: Url): void { let host = this.host ?? ""; const port = portPattern.exec(host); if (port) { if (port[0] !== ":") this.port = port[0].slice(1); host = host.slice(0, host.length - port[0].length); } if (host) this.hostname = host;};
Url.prototype.format = function format(this: Url): string { let auth = this.auth || ""; if (auth) auth = `${encodeURIComponent(auth).replace(/%3A/g, ":")}@`; let protocol = this.protocol || ""; let pathname = this.pathname || ""; let hash = this.hash || ""; let host = ""; let query = ""; if (this.host) { host = auth + this.host; } else if (this.hostname) { host = auth + (this.hostname.includes(":") && !isIpv6Hostname(this.hostname) ? `[${this.hostname}]` : this.hostname); if (this.port) host += `:${this.port}`; } if (this.query !== null && typeof this.query === "object") query = qs.stringify(this.query); let search = this.search || (query && `?${query}`) || ""; if (protocol && !protocol.endsWith(":")) protocol += ":"; pathname = pathname.replace(/[#?]/g, (c) => (c === "#" ? "%23" : "%3F")); if (this.slashes || slashedProtocol.has(protocol)) { if (this.slashes || host) { if (pathname && pathname[0] !== "/") pathname = `/${pathname}`; host = `//${host}`; } else if (protocol.startsWith("file")) { host = "//"; } } search = search.replace(/#/g, "%23"); if (hash && hash[0] !== "#") hash = `#${hash}`; if (search && search[0] !== "?") search = `?${search}`; return protocol + host + pathname + search + hash;};
/** Legacy resolution, via WHATWG resolution against a placeholder base for relative inputs. */Url.prototype.resolve = function resolve(this: Url, relative: string): string { const from = this.format(); const base = new URL(from, "resolve://"); const resolved = new URL(relative, base); if (resolved.protocol === "resolve:") { const { pathname, search, hash } = resolved; return from.startsWith("/") || from === "" ? `${pathname}${search}${hash}` : `${pathname.slice(1)}${search}${hash}`; } return resolved.toString();};
Url.prototype.resolveObject = function resolveObject(this: Url, relative: string | Url): Url { const rel = typeof relative === "string" ? relative : relative.format(); return parse(this.resolve(rel));};
export function parse(url: string | Url, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url { if (url instanceof Url) return url; return new Url().parse(url, parseQueryString, slashesDenoteHost);}
export function format(urlObject: unknown, options?: { auth?: boolean; fragment?: boolean; search?: boolean; unicode?: boolean }): string { if (typeof urlObject === "string") return parse(urlObject).format(); if (typeof urlObject !== "object" || urlObject === null) throw invalidArgType("urlObject", "one of type object or string", urlObject); if (urlObject instanceof URL) { const u = new URL(urlObject.href); if (options?.auth === false) { u.username = ""; u.password = ""; } if (options?.fragment === false) u.hash = ""; if (options?.search === false) u.search = ""; return u.href; } return Url.prototype.format.call(urlObject as Url);}
export function resolve(from: string, to: string): string { return parse(from, false, true).resolve(to);}
function fileUrlError(code: string, message: string): TypeError { return Object.assign(new TypeError(message), { code });}
export function fileURLToPath(input: string | URL): string { const url = typeof input === "string" ? new URL(input) : input; if (!(url instanceof URL)) throw invalidArgType("path", "of type string or an instance of URL", input); if (url.protocol !== "file:") throw fileUrlError("ERR_INVALID_URL_SCHEME", "The URL must be of scheme file"); if (url.hostname !== "") throw fileUrlError("ERR_INVALID_FILE_URL_HOST", 'File URL host must be "localhost" or empty on linux'); if (/%2f/i.test(url.pathname)) throw fileUrlError("ERR_INVALID_FILE_URL_PATH", "File URL path must not include encoded / characters"); return decodeURIComponent(url.pathname);}
/** fileURLToPath, as a Buffer — for a path whose bytes are not valid UTF-8 on a * case-sensitive filesystem; this runtime's VFS is UTF-8, so it is fileURLToPath * wrapped, kept for API parity. */export function fileURLToPathBuffer(input: string | URL): Buffer { return Buffer.from(fileURLToPath(input));}
/** url.URLPattern: the browser's own, re-exported — it is the same WHATWG URL Pattern * API Node ports (see this chunk's report for where it is missing). */export const URLPattern = (globalThis as { URLPattern?: unknown }).URLPattern as typeof globalThis.URLPattern ?? refusedClass("url", "URLPattern", "this engine has no native URLPattern");
// The runtime worker's own script URL, however it was served (e.g. /dist/runtime/runtime.js or// /__nib/0.1.0/runtime.js): import.meta.url, since this module is bundled into that one// worker file, resolves to the worker's own address, with any `?nib-host`-style query// string stripped so it still matches frames that include one.const RUNTIME_SCRIPT_URL = import.meta.url.split("?")[0]!;
/** Node's isInsideNodeModules: the first frame outside the runtime decides. */function calledFromNodeModules(): boolean { const frames = (new Error().stack ?? "").split("\n").slice(1); const guest = frames.find((f) => !f.includes(RUNTIME_SCRIPT_URL)); return guest?.includes("/node_modules/") ?? false;}
export function makeUrl(resolvePath: (p: string) => string, emitWarning: (msg: string, type: string, code: string) => void) { let warnedParse = false; /** url.parse warns once (DEP0169), and only for calls from outside node_modules. */ function parseWithWarning(url: string | Url, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url { if (!warnedParse && !calledFromNodeModules()) { warnedParse = true; emitWarning("`url.parse()` behavior is not standardized and prone to errors that have security implications. " + "Use the WHATWG URL API instead. CVEs are not issued for `url.parse()` vulnerabilities.", "DeprecationWarning", "DEP0169"); } return parse(url, parseQueryString, slashesDenoteHost); }
function pathToFileURL(filepath: string): URL { if (typeof filepath !== "string") throw invalidArgType("path", "of type string", filepath); let resolved = resolvePath(filepath); if (filepath.endsWith("/") && !resolved.endsWith("/")) resolved += "/"; const out = new URL("file://"); out.pathname = resolved.replace(/[%\\\n\r\t]/g, (c) => ({ "%": "%25", "\\": "%5C", "\n": "%0A", "\r": "%0D", "\t": "%09" })[c]!); return out; }
return { URL, URLSearchParams, Url, parse: parseWithWarning, format, resolve, resolveObject: (from: string, to: string) => parse(from, false, true).resolveObject(to), fileURLToPath, fileURLToPathBuffer, pathToFileURL, URLPattern, domainToASCII, domainToUnicode, urlToHttpOptions: (url: URL) => ({ protocol: url.protocol, hostname: url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname, hash: url.hash, search: url.search, pathname: url.pathname, path: `${url.pathname || ""}${url.search || ""}`, href: url.href, ...(url.port !== "" ? { port: Number(url.port) } : {}), ...(url.username || url.password ? { auth: `${decodeURIComponent(url.username)}:${decodeURIComponent(url.password)}` } : {}), }), };}