/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/url.ts
1 709 строк
51 KB
Kenta Moriuchi
chore: make prefer-primordials lint an internal plugin (#35978)
23 июл 2026, 11:53
Не верифицирован
23 июл 2026, 11:53
5b55d08
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. (function () { const { core, primordials } = __bootstrap; const { ArrayPrototypeConcat, ArrayPrototypeJoin, ArrayPrototypePop, ArrayPrototypePush, ArrayPrototypeShift, ArrayPrototypeSlice, ArrayPrototypeSplice, ArrayPrototypeUnshift, FunctionPrototypeCall, Int8Array, ObjectCreate, ObjectKeys, ObjectPrototypeIsPrototypeOf, RegExpPrototypeExec, RegExpPrototypeTest, SafeRegExp, SafeSet, SetPrototypeHas, StringPrototypeCharAt, StringPrototypeCharCodeAt, StringPrototypeCodePointAt, StringPrototypeIncludes, StringPrototypeIndexOf, StringPrototypeReplace, StringPrototypeSlice, StringPrototypeSplit, StringPrototypeStartsWith, StringPrototypeToLowerCase, TypedArrayPrototypeGetByteLength, TypedArrayPrototypeGetByteOffset, TypedArrayPrototypeGetBuffer, TypedArrayPrototypeSubarray, Uint8Array, decodeURIComponent, } = primordials; const { ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_INVALID_FILE_URL_HOST, ERR_INVALID_FILE_URL_PATH, ERR_INVALID_URL, ERR_INVALID_URL_SCHEME, } = core.loadExtScript("ext:deno_node/internal/errors.ts"); const { validateString } = core.loadExtScript( "ext:deno_node/internal/validators.mjs", ); const { CHAR_0, CHAR_9, CHAR_AT, CHAR_BACKWARD_SLASH, CHAR_CARRIAGE_RETURN, CHAR_CIRCUMFLEX_ACCENT, CHAR_DOT, CHAR_DOUBLE_QUOTE, CHAR_FORM_FEED, CHAR_FORWARD_SLASH, CHAR_GRAVE_ACCENT, CHAR_HASH, CHAR_HYPHEN_MINUS, CHAR_LEFT_ANGLE_BRACKET, CHAR_LEFT_CURLY_BRACKET, CHAR_LEFT_SQUARE_BRACKET, CHAR_LINE_FEED, CHAR_LOWERCASE_A, CHAR_LOWERCASE_Z, CHAR_NO_BREAK_SPACE, CHAR_PERCENT, CHAR_PLUS, CHAR_QUESTION_MARK, CHAR_RIGHT_ANGLE_BRACKET, CHAR_RIGHT_CURLY_BRACKET, CHAR_RIGHT_SQUARE_BRACKET, CHAR_SEMICOLON, CHAR_SINGLE_QUOTE, CHAR_SPACE, CHAR_TAB, CHAR_UNDERSCORE, CHAR_UPPERCASE_A, CHAR_UPPERCASE_Z, CHAR_VERTICAL_LINE, CHAR_ZERO_WIDTH_NOBREAK_SPACE, } = core.loadExtScript("ext:deno_node/path/_constants.ts"); const lazyPath = core.createLazyLoader("node:path"); const { domainToASCII: idnaToASCII, domainToUnicode: idnaToUnicode, } = core.loadExtScript("ext:deno_node/internal/idna.ts"); const { isWindows, osType } = core.loadExtScript("ext:deno_node/_util/os.ts"); let _Buffer; function lazyBuffer() { return _Buffer ??= core.loadExtScript("ext:deno_node/internal/buffer.mjs").Buffer; } const { encodeStr, hexTable } = core.loadExtScript( "ext:deno_node/internal/querystring.ts", ); const querystring = core.loadExtScript("ext:deno_node/querystring.js").default; const { URL, URLSearchParams } = core.loadExtScript("ext:deno_web/00_url.js"); const { urlToHttpOptions } = core.loadExtScript( "ext:deno_node/internal/url.ts", ); const forwardSlashRegEx = new SafeRegExp(/\//g); const percentRegEx = new SafeRegExp(/%/g); const backslashRegEx = new SafeRegExp(/\\/g); const newlineRegEx = new SafeRegExp(/\n/g); const carriageReturnRegEx = new SafeRegExp(/\r/g); const tabRegEx = new SafeRegExp(/\t/g); const caretRegEx = new SafeRegExp(/\^/g); const leftBracketRegEx = new SafeRegExp(/\[/g); const rightBracketRegEx = new SafeRegExp(/]/g); const pipeRegEx = new SafeRegExp(/\|/g); const tildeRegEx = new SafeRegExp(/~/g); const hashRegEx = new SafeRegExp(/#/g); // Reference: RFC 3986, RFC 1808, RFC 2396 // define these here so at least they only have to be // compiled once on the first module load. const protocolPattern = new SafeRegExp(/^[a-z0-9.+-]+:/i); const fileProtocolPattern = new SafeRegExp(/^file:?$/); const portPattern = new SafeRegExp(/:[0-9]*$/); const hostPattern = new SafeRegExp(/^\/\/[^@/]+@[^@/]+/); // Special case for a simple path URL const simplePathPattern = new SafeRegExp(/^(\/\/?(?!\/)[^?\s]*)(\?[^\s]*)?$/); // Protocols that can allow "unsafe" and "unwise" chars. const unsafeProtocol = new SafeSet(["javascript", "javascript:"]); // Protocols that never have a hostname. const hostlessProtocol = new SafeSet(["javascript", "javascript:"]); // Protocols that always contain a // bit. const slashedProtocol = new SafeSet([ "http", "http:", "https", "https:", "ftp", "ftp:", "gopher", "gopher:", "file", "file:", "ws", "ws:", "wss", "wss:", ]); const hostnameMaxLen = 255; // These characters do not need escaping: // ! - . _ ~ // ' ( ) * : // digits // alpha (uppercase) // alpha (lowercase) // deno-fmt-ignore const noEscapeAuth = new Int8Array([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0x0F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 0x1F 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, // 0x20 - 0x2F 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 0x30 - 0x3F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 0x4F 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 0x50 - 0x5F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6F 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 0x70 - 0x7F ]); // This prevents some common spoofing bugs due to our use of IDNA toASCII. For // compatibility, the set of characters we use here is the *intersection* of // "forbidden host code point" in the WHATWG URL Standard [1] and the // characters in the host parsing loop in Url.prototype.parse, with the // following additions: // // - ':' since this could cause a "protocol spoofing" bug // - '@' since this could cause parts of the hostname to be confused with auth // - '[' and ']' since this could cause a non-IPv6 hostname to be interpreted // as IPv6 by isIpv6Hostname above // // [1]: https://url.spec.whatwg.org/#forbidden-host-code-point const forbiddenHostChars = new SafeRegExp(/[\0\t\n\r #%/:<>?@[\\\]^|]/); // For IPv6, permit '[', ']', and ':'. const forbiddenHostCharsIpv6 = new SafeRegExp(/[\0\t\n\r #%/<>?@\\^|]/); const _url = URL; // Legacy URL API class Url { public protocol: string | null; public slashes: boolean | null; public auth: string | null; public host: string | null; public port: string | null; public hostname: string | null; public hash: string | null; public search: string | null; public query: string | ParsedUrlQuery | null; public pathname: string | null; public path: string | null; public href: string | null; [key: string]: unknown; constructor() { 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; } #parseHost() { let host = this.host || ""; let port: RegExpExecArray | null | string = RegExpPrototypeExec( portPattern, host, ); if (port) { port = port[0]; if (port !== ":") { this.port = StringPrototypeSlice(port, 1); } host = StringPrototypeSlice(host, 0, host.length - port.length); } if (host) this.hostname = host; } public resolve(relative: string) { return this.resolveObject(parse(relative, false, true)).format(); } public resolveObject(relative: string | Url) { if (typeof relative === "string") { const rel = new Url(); rel.urlParse(relative, false, true); relative = rel; } const result = new Url(); const tkeys = ObjectKeys(this); for (let tk = 0; tk < tkeys.length; tk++) { const tkey = tkeys[tk]; result[tkey] = this[tkey]; } // Hash is always overridden, no matter what. // even href="" will remove it. result.hash = relative.hash; // If the relative url is empty, then there's nothing left to do here. if (relative.href === "") { result.href = result.format(); return result; } // Hrefs like //foo/bar always cut to the protocol. if (relative.slashes && !relative.protocol) { // Take everything except the protocol from relative const rkeys = ObjectKeys(relative); for (let rk = 0; rk < rkeys.length; rk++) { const rkey = rkeys[rk]; if (rkey !== "protocol") result[rkey] = relative[rkey]; } // urlParse appends trailing / to urls like http://www.example.com if ( result.protocol && SetPrototypeHas(slashedProtocol, result.protocol) && result.hostname && !result.pathname ) { result.path = result.pathname = "/"; } result.href = result.format(); return result; } if (relative.protocol && relative.protocol !== result.protocol) { // If it's a known url protocol, then changing // the protocol does weird things // first, if it's not file:, then we MUST have a host, // and if there was a path // to begin with, then we MUST have a path. // if it is file:, then the host is dropped, // because that's known to be hostless. // anything else is assumed to be absolute. if (!SetPrototypeHas(slashedProtocol, relative.protocol)) { const keys = ObjectKeys(relative); for (let v = 0; v < keys.length; v++) { const k = keys[v]; result[k] = relative[k]; } result.href = result.format(); return result; } result.protocol = relative.protocol; if ( !relative.host && !RegExpPrototypeTest(fileProtocolPattern, relative.protocol) && !SetPrototypeHas(hostlessProtocol, relative.protocol) ) { const relPath = StringPrototypeSplit(relative.pathname || "", "/"); while ( relPath.length && !(relative.host = ArrayPrototypeShift(relPath) || null) ); if (!relative.host) relative.host = ""; if (!relative.hostname) relative.hostname = ""; if (relPath[0] !== "") ArrayPrototypeUnshift(relPath, ""); if (relPath.length < 2) ArrayPrototypeUnshift(relPath, ""); result.pathname = ArrayPrototypeJoin(relPath, "/"); } else { result.pathname = relative.pathname; } result.search = relative.search; result.query = relative.query; result.host = relative.host || ""; result.auth = relative.auth; result.hostname = relative.hostname || relative.host; result.port = relative.port; // To support http.request if (result.pathname || result.search) { const p = result.pathname || ""; const s = result.search || ""; result.path = p + s; } result.slashes = result.slashes || relative.slashes; result.href = result.format(); return result; } const isSourceAbs = result.pathname && StringPrototypeCharAt(result.pathname, 0) === "/"; const isRelAbs = relative.host || (relative.pathname && StringPrototypeCharAt(relative.pathname, 0) === "/"); let mustEndAbs: string | boolean | number | null = isRelAbs || isSourceAbs || (result.host && relative.pathname); const removeAllDots = mustEndAbs; let srcPath = (result.pathname && StringPrototypeSplit(result.pathname, "/")) || []; const relPath = (relative.pathname && StringPrototypeSplit(relative.pathname, "/")) || []; const noLeadingSlashes = result.protocol && !SetPrototypeHas(slashedProtocol, result.protocol); // If the url is a non-slashed url, then relative // links like ../.. should be able // to crawl up to the hostname, as well. This is strange. // result.protocol has already been set by now. // Later on, put the first path part into the host field. if (noLeadingSlashes) { result.hostname = ""; result.port = null; if (result.host) { if (srcPath[0] === "") srcPath[0] = result.host; else ArrayPrototypeUnshift(srcPath, result.host); } result.host = ""; if (relative.protocol) { relative.hostname = null; relative.port = null; result.auth = null; if (relative.host) { if (relPath[0] === "") relPath[0] = relative.host; else ArrayPrototypeUnshift(relPath, relative.host); } relative.host = null; } mustEndAbs = mustEndAbs && (relPath[0] === "" || srcPath[0] === ""); } if (isRelAbs) { // it's absolute. if (relative.host || relative.host === "") { if (result.host !== relative.host) result.auth = null; result.host = relative.host; result.port = relative.port; } if (relative.hostname || relative.hostname === "") { if (result.hostname !== relative.hostname) result.auth = null; result.hostname = relative.hostname; } result.search = relative.search; result.query = relative.query; srcPath = relPath; // Fall through to the dot-handling below. } else if (relPath.length) { // it's relative // throw away the existing file, and take the new path instead. if (!srcPath) srcPath = []; ArrayPrototypePop(srcPath); srcPath = ArrayPrototypeConcat(srcPath, relPath); result.search = relative.search; result.query = relative.query; } else if (relative.search !== null && relative.search !== undefined) { // Just pull out the search. // like href='?foo'. // Put this after the other two cases because it simplifies the booleans if (noLeadingSlashes) { result.hostname = result.host = ArrayPrototypeShift(srcPath) || null; // Occasionally the auth can get stuck only in host. // This especially happens in cases like // url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = result.host && StringPrototypeIndexOf(result.host, "@") > 0 && StringPrototypeSplit(result.host, "@"); if (authInHost) { result.auth = ArrayPrototypeShift(authInHost) || null; result.host = result.hostname = ArrayPrototypeShift(authInHost) || null; } } result.search = relative.search; result.query = relative.query; // To support http.request if (result.pathname !== null || result.search !== null) { result.path = (result.pathname ? result.pathname : "") + (result.search ? result.search : ""); } result.href = result.format(); return result; } if (!srcPath.length) { // No path at all. All other things were already handled above. result.pathname = null; // To support http.request if (result.search) { result.path = "/" + result.search; } else { result.path = null; } result.href = result.format(); return result; } // If a url ENDs in . or .., then it must get a trailing slash. // however, if it ends in anything else non-slashy, // then it must NOT get a trailing slash. let last = ArrayPrototypeSlice(srcPath, -1)[0]; const hasTrailingSlash = ((result.host || relative.host || srcPath.length > 1) && (last === "." || last === "..")) || last === ""; // Strip single dots, resolve double dots to parent dir // if the path tries to go above the root, `up` ends up > 0 let up = 0; for (let i = srcPath.length - 1; i >= 0; i--) { last = srcPath[i]; if (last === ".") { ArrayPrototypeSplice(srcPath, i, 1); } else if (last === "..") { ArrayPrototypeSplice(srcPath, i, 1); up++; } else if (up) { ArrayPrototypeSplice(srcPath, i, 1); up--; } } // If the path is allowed to go above the root, restore leading ..s if (!mustEndAbs && !removeAllDots) { while (up--) { ArrayPrototypeUnshift(srcPath, ".."); } } if ( mustEndAbs && srcPath[0] !== "" && (!srcPath[0] || StringPrototypeCharAt(srcPath[0], 0) !== "/") ) { ArrayPrototypeUnshift(srcPath, ""); } if ( hasTrailingSlash && StringPrototypeSlice(ArrayPrototypeJoin(srcPath, "/"), -1) !== "/" ) { ArrayPrototypePush(srcPath, ""); } const isAbsolute = srcPath[0] === "" || (srcPath[0] && StringPrototypeCharAt(srcPath[0], 0) === "/"); // put the host back if (noLeadingSlashes) { result.hostname = result.host = isAbsolute ? "" : srcPath.length ? ArrayPrototypeShift(srcPath) || null : ""; // Occasionally the auth can get stuck only in host. // This especially happens in cases like // url.resolveObject('mailto:local1@domain1', 'local2@domain2') const authInHost = result.host && StringPrototypeIndexOf(result.host, "@") > 0 ? StringPrototypeSplit(result.host, "@") : false; if (authInHost) { result.auth = ArrayPrototypeShift(authInHost) || null; result.host = result.hostname = ArrayPrototypeShift(authInHost) || null; } } mustEndAbs = mustEndAbs || (result.host && srcPath.length); if (mustEndAbs && !isAbsolute) { ArrayPrototypeUnshift(srcPath, ""); } if (!srcPath.length) { result.pathname = null; result.path = null; } else { result.pathname = ArrayPrototypeJoin(srcPath, "/"); } // To support request.http if (result.pathname !== null || result.search !== null) { result.path = (result.pathname ? result.pathname : "") + (result.search ? result.search : ""); } result.auth = relative.auth || result.auth; result.slashes = result.slashes || relative.slashes; result.href = result.format(); return result; } format() { let auth = this.auth || ""; if (auth) { auth = encodeStr(auth, noEscapeAuth, hexTable); auth += "@"; } 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 + (StringPrototypeIncludes(this.hostname, ":") && !isIpv6Hostname(this.hostname) ? "[" + this.hostname + "]" : this.hostname); if (this.port) { host += ":" + this.port; } } if (this.query !== null && typeof this.query === "object") { query = querystring.stringify(this.query); } let search = this.search || (query && "?" + query) || ""; if ( protocol && StringPrototypeCharCodeAt(protocol, protocol.length - 1) !== 58 /* : */ ) { protocol += ":"; } let newPathname = ""; let lastPos = 0; for (let i = 0; i < pathname.length; ++i) { switch (StringPrototypeCharCodeAt(pathname, i)) { case CHAR_HASH: if (i - lastPos > 0) { newPathname += StringPrototypeSlice(pathname, lastPos, i); } newPathname += "%23"; lastPos = i + 1; break; case CHAR_QUESTION_MARK: if (i - lastPos > 0) { newPathname += StringPrototypeSlice(pathname, lastPos, i); } newPathname += "%3F"; lastPos = i + 1; break; } } if (lastPos > 0) { if (lastPos !== pathname.length) { pathname = newPathname + StringPrototypeSlice(pathname, lastPos); } else pathname = newPathname; } // Only the slashedProtocols get the //. Not mailto:, xmpp:, etc. // unless they had them to begin with. if (this.slashes || SetPrototypeHas(slashedProtocol, protocol)) { if (this.slashes || host) { if ( pathname && StringPrototypeCharCodeAt(pathname, 0) !== CHAR_FORWARD_SLASH ) { pathname = "/" + pathname; } host = "//" + host; } else if ( protocol.length >= 4 && StringPrototypeCharCodeAt(protocol, 0) === 102 /* f */ && StringPrototypeCharCodeAt(protocol, 1) === 105 /* i */ && StringPrototypeCharCodeAt(protocol, 2) === 108 /* l */ && StringPrototypeCharCodeAt(protocol, 3) === 101 /* e */ ) { host = "//"; } } search = StringPrototypeReplace(search, hashRegEx, "%23"); if (hash && StringPrototypeCharCodeAt(hash, 0) !== CHAR_HASH) { hash = "#" + hash; } if ( search && StringPrototypeCharCodeAt(search, 0) !== CHAR_QUESTION_MARK ) { search = "?" + search; } return protocol + host + pathname + search + hash; } public urlParse( url: string, parseQueryString: boolean, slashesDenoteHost: boolean, ) { validateString(url, "url"); // Copy chrome, IE, opera backslash-handling behavior. // Back slashes before the query string get converted to forward slashes // See: https://code.google.com/p/chromium/issues/detail?id=25916 let hasHash = false; let start = -1; let end = -1; let rest = ""; let lastPos = 0; for (let i = 0, inWs = false, split = false; i < url.length; ++i) { const code = StringPrototypeCharCodeAt(url, i); // Find first and last non-whitespace characters for trimming const isWs = code === CHAR_SPACE || code === CHAR_TAB || code === CHAR_CARRIAGE_RETURN || code === CHAR_LINE_FEED || code === CHAR_FORM_FEED || code === CHAR_NO_BREAK_SPACE || code === CHAR_ZERO_WIDTH_NOBREAK_SPACE; if (start === -1) { if (isWs) continue; lastPos = start = i; } else if (inWs) { if (!isWs) { end = -1; inWs = false; } } else if (isWs) { end = i; inWs = true; } // Only convert backslashes while we haven't seen a split character if (!split) { switch (code) { case CHAR_HASH: hasHash = true; // Fall through case CHAR_QUESTION_MARK: split = true; break; case CHAR_BACKWARD_SLASH: if (i - lastPos > 0) { rest += StringPrototypeSlice(url, lastPos, i); } rest += "/"; lastPos = i + 1; break; } } else if (!hasHash && code === CHAR_HASH) { hasHash = true; } } // Check if string was non-empty (including strings with only whitespace) if (start !== -1) { if (lastPos === start) { // We didn't convert any backslashes if (end === -1) { if (start === 0) rest = url; else rest = StringPrototypeSlice(url, start); } else { rest = StringPrototypeSlice(url, start, end); } } else if (end === -1 && lastPos < url.length) { // We converted some backslashes and have only part of the entire string rest += StringPrototypeSlice(url, lastPos); } else if (end !== -1 && lastPos < end) { // We converted some backslashes and have only part of the entire string rest += StringPrototypeSlice(url, lastPos, end); } } if (!slashesDenoteHost && !hasHash) { // Try fast path regexp const simplePath = RegExpPrototypeExec(simplePathPattern, rest); if (simplePath) { this.path = rest; this.href = rest; this.pathname = simplePath[1]; if (simplePath[2]) { this.search = simplePath[2]; if (parseQueryString) { this.query = querystring.parse( StringPrototypeSlice(this.search, 1), ); } else { this.query = StringPrototypeSlice(this.search, 1); } } else if (parseQueryString) { this.search = null; this.query = ObjectCreate(null); } return this; } } let proto: RegExpExecArray | null | string = RegExpPrototypeExec( protocolPattern, rest, ); let lowerProto = ""; if (proto) { proto = proto[0]; lowerProto = StringPrototypeToLowerCase(proto); this.protocol = lowerProto; rest = StringPrototypeSlice(rest, proto.length); } // Figure out if it's got a host // user@server is *always* interpreted as a hostname, and url // resolution will treat //foo/bar as host=foo,path=bar because that's // how the browser resolves relative URLs. let slashes; if ( slashesDenoteHost || proto || RegExpPrototypeTest(hostPattern, rest) ) { slashes = StringPrototypeCharCodeAt(rest, 0) === CHAR_FORWARD_SLASH && StringPrototypeCharCodeAt(rest, 1) === CHAR_FORWARD_SLASH; if ( slashes && !(proto && SetPrototypeHas(hostlessProtocol, lowerProto)) ) { rest = StringPrototypeSlice(rest, 2); this.slashes = true; } } if ( !SetPrototypeHas(hostlessProtocol, lowerProto) && (slashes || (proto && !SetPrototypeHas(slashedProtocol, proto))) ) { // there's a hostname. // the first instance of /, ?, ;, or # ends the host. // // If there is an @ in the hostname, then non-host chars *are* allowed // to the left of the last @ sign, unless some host-ending character // comes *before* the @-sign. // URLs are obnoxious. // // ex: // http://a@b@c/ => user:a@b host:c // http://a@b?@c => user:a host:b path:/?@c let hostEnd = -1; let atSign = -1; let nonHost = -1; for (let i = 0; i < rest.length; ++i) { switch (StringPrototypeCharCodeAt(rest, i)) { case CHAR_TAB: case CHAR_LINE_FEED: case CHAR_CARRIAGE_RETURN: case CHAR_SPACE: case CHAR_DOUBLE_QUOTE: case CHAR_PERCENT: case CHAR_SINGLE_QUOTE: case CHAR_SEMICOLON: case CHAR_LEFT_ANGLE_BRACKET: case CHAR_RIGHT_ANGLE_BRACKET: case CHAR_BACKWARD_SLASH: case CHAR_CIRCUMFLEX_ACCENT: case CHAR_GRAVE_ACCENT: case CHAR_LEFT_CURLY_BRACKET: case CHAR_VERTICAL_LINE: case CHAR_RIGHT_CURLY_BRACKET: // Characters that are never ever allowed in a hostname from RFC 2396 if (nonHost === -1) nonHost = i; break; case CHAR_HASH: case CHAR_FORWARD_SLASH: case CHAR_QUESTION_MARK: // Find the first instance of any host-ending characters if (nonHost === -1) nonHost = i; hostEnd = i; break; case CHAR_AT: // At this point, either we have an explicit point where the // auth portion cannot go past, or the last @ char is the decider. atSign = i; nonHost = -1; break; } if (hostEnd !== -1) break; } start = 0; if (atSign !== -1) { this.auth = decodeURIComponent(StringPrototypeSlice(rest, 0, atSign)); start = atSign + 1; } if (nonHost === -1) { this.host = StringPrototypeSlice(rest, start); rest = ""; } else { this.host = StringPrototypeSlice(rest, start, nonHost); rest = StringPrototypeSlice(rest, nonHost); } // pull out port. this.#parseHost(); // We've indicated that there is a hostname, // so even if it's empty, it has to be present. if (typeof this.hostname !== "string") this.hostname = ""; const hostname = this.hostname; // If hostname begins with [ and ends with ] // assume that it's an IPv6 address. const ipv6Hostname = isIpv6Hostname(hostname); // validate a little. if (!ipv6Hostname) { rest = getHostname(this, rest, hostname); } if (this.hostname.length > hostnameMaxLen) { this.hostname = ""; } else { // Hostnames are always lower case. this.hostname = StringPrototypeToLowerCase(this.hostname); } if (this.hostname !== "") { if (ipv6Hostname) { if (RegExpPrototypeTest(forbiddenHostCharsIpv6, this.hostname)) { throw new ERR_INVALID_URL(url); } } else { // IDNA Support: Returns a punycoded representation of "domain". // It only converts parts of the domain name that // have non-ASCII characters, i.e. it doesn't matter if // you call it with a domain that already is ASCII-only. // Use lenient mode (`true`) to try to support even non-compliant // URLs. this.hostname = idnaToASCII(this.hostname); // Prevent two potential routes of hostname spoofing. // 1. If this.hostname is empty, it must have become empty due to toASCII // since we checked this.hostname above. // 2. If any of forbiddenHostChars appears in this.hostname, it must have // also gotten in due to toASCII. This is since getHostname would have // filtered them out otherwise. // Rather than trying to correct this by moving the non-host part into // the pathname as we've done in getHostname, throw an exception to // convey the severity of this issue. if ( this.hostname === "" || RegExpPrototypeTest(forbiddenHostChars, this.hostname) ) { throw new ERR_INVALID_URL(url); } } } const p = this.port ? ":" + this.port : ""; const h = this.hostname || ""; this.host = h + p; // strip [ and ] from the hostname // the host field still retains them, though if (ipv6Hostname) { this.hostname = StringPrototypeSlice(this.hostname, 1, -1); if (rest[0] !== "/") { rest = "/" + rest; } } } // Now rest is set to the post-host stuff. // Chop off any delim chars. if (!SetPrototypeHas(unsafeProtocol, lowerProto)) { // First, make 100% sure that any "autoEscape" chars get // escaped, even if encodeURIComponent doesn't think they // need to be. rest = autoEscapeStr(rest); } let questionIdx = -1; let hashIdx = -1; for (let i = 0; i < rest.length; ++i) { const code = StringPrototypeCharCodeAt(rest, i); if (code === CHAR_HASH) { this.hash = StringPrototypeSlice(rest, i); hashIdx = i; break; } else if (code === CHAR_QUESTION_MARK && questionIdx === -1) { questionIdx = i; } } if (questionIdx !== -1) { if (hashIdx === -1) { this.search = StringPrototypeSlice(rest, questionIdx); this.query = StringPrototypeSlice(rest, questionIdx + 1); } else { this.search = StringPrototypeSlice(rest, questionIdx, hashIdx); this.query = StringPrototypeSlice(rest, questionIdx + 1, hashIdx); } if (parseQueryString) { this.query = querystring.parse(this.query); } } else if (parseQueryString) { // No query string, but parseQueryString still requested this.search = null; this.query = ObjectCreate(null); } const useQuestionIdx = questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx); const firstIdx = useQuestionIdx ? questionIdx : hashIdx; if (firstIdx === -1) { if (rest.length > 0) this.pathname = rest; } else if (firstIdx > 0) { this.pathname = StringPrototypeSlice(rest, 0, firstIdx); } if ( SetPrototypeHas(slashedProtocol, lowerProto) && this.hostname && !this.pathname ) { this.pathname = "/"; } // To support http.request if (this.pathname || this.search) { const p = this.pathname || ""; const s = this.search || ""; this.path = p + s; } // Finally, reconstruct the href based on what has been validated. this.href = this.format(); return this; } } interface UrlObject { auth?: string | null | undefined; hash?: string | null | undefined; host?: string | null | undefined; hostname?: string | null | undefined; href?: string | null | undefined; pathname?: string | null | undefined; protocol?: string | null | undefined; search?: string | null | undefined; slashes?: boolean | null | undefined; port?: string | number | null | undefined; query?: string | null | ParsedUrlQueryInput | undefined; } function format( urlObject: string | URL | Url | UrlObject, options?: { auth: boolean; fragment: boolean; search: boolean; unicode: boolean; }, ): string { if (typeof urlObject === "string") { urlObject = parse(urlObject, true, false); } else if (typeof urlObject !== "object" || urlObject === null) { throw new ERR_INVALID_ARG_TYPE( "urlObject", ["Object", "string"], urlObject, ); } else if (ObjectPrototypeIsPrototypeOf(URL.prototype, urlObject)) { return formatWhatwg(urlObject, options); } return FunctionPrototypeCall(Url.prototype.format, urlObject); } /** * The URL object has both a `toString()` method and `href` property that return string serializations of the URL. * These are not, however, customizable in any way. * This method allows for basic customization of the output. * @see Tested in `parallel/test-url-format-whatwg.js`. * @param urlObject * @param options * @param options.auth `true` if the serialized URL string should include the username and password, `false` otherwise. **Default**: `true`. * @param options.fragment `true` if the serialized URL string should include the fragment, `false` otherwise. **Default**: `true`. * @param options.search `true` if the serialized URL string should include the search query, **Default**: `true`. * @param options.unicode `true` if Unicode characters appearing in the host component of the URL string should be encoded directly as opposed to being Punycode encoded. **Default**: `false`. * @returns a customizable serialization of a URL `String` representation of a `WHATWG URL` object. */ function formatWhatwg( urlObject: string | URL, options?: { auth: boolean; fragment: boolean; search: boolean; unicode: boolean; }, ): string { if (typeof urlObject === "string") { urlObject = new URL(urlObject); } if (options) { if (typeof options !== "object") { throw new ERR_INVALID_ARG_TYPE("options", "object", options); } } options = { auth: true, fragment: true, search: true, unicode: false, ...options, }; let ret = urlObject.protocol; if (urlObject.host !== null) { ret += "//"; const hasUsername = !!urlObject.username; const hasPassword = !!urlObject.password; if (options.auth && (hasUsername || hasPassword)) { if (hasUsername) { ret += urlObject.username; } if (hasPassword) { ret += `:${urlObject.password}`; } ret += "@"; } ret += options.unicode ? domainToUnicode(urlObject.hostname) : urlObject.hostname; if (urlObject.port) { ret += `:${urlObject.port}`; } } ret += urlObject.pathname; if (options.search && urlObject.search) { ret += urlObject.search; } if (options.fragment && urlObject.hash) { ret += urlObject.hash; } return ret; } function isIpv6Hostname(hostname: string) { return ( StringPrototypeCharCodeAt(hostname, 0) === CHAR_LEFT_SQUARE_BRACKET && StringPrototypeCharCodeAt(hostname, hostname.length - 1) === CHAR_RIGHT_SQUARE_BRACKET ); } function getHostname(self: Url, rest: string, hostname: string) { for (let i = 0; i < hostname.length; ++i) { const code = StringPrototypeCharCodeAt(hostname, i); const isValid = (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) || code === CHAR_DOT || (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || (code >= CHAR_0 && code <= CHAR_9) || code === CHAR_HYPHEN_MINUS || code === CHAR_PLUS || code === CHAR_UNDERSCORE || code > 127; // Invalid host character if (!isValid) { self.hostname = StringPrototypeSlice(hostname, 0, i); return `/${StringPrototypeSlice(hostname, i)}${rest}`; } } return rest; } // Escaped characters. Use empty strings to fill up unused entries. // Using Array is faster than Object/Map // deno-fmt-ignore const escapedCodes = [ /* 0 - 9 */ "", "", "", "", "", "", "", "", "", "%09", /* 10 - 19 */ "%0A", "", "", "%0D", "", "", "", "", "", "", /* 20 - 29 */ "", "", "", "", "", "", "", "", "", "", /* 30 - 39 */ "", "", "%20", "", "%22", "", "", "", "", "%27", /* 40 - 49 */ "", "", "", "", "", "", "", "", "", "", /* 50 - 59 */ "", "", "", "", "", "", "", "", "", "", /* 60 - 69 */ "%3C", "", "%3E", "", "", "", "", "", "", "", /* 70 - 79 */ "", "", "", "", "", "", "", "", "", "", /* 80 - 89 */ "", "", "", "", "", "", "", "", "", "", /* 90 - 99 */ "", "", "%5C", "", "%5E", "", "%60", "", "", "", /* 100 - 109 */ "", "", "", "", "", "", "", "", "", "", /* 110 - 119 */ "", "", "", "", "", "", "", "", "", "", /* 120 - 125 */ "", "", "", "%7B", "%7C", "%7D" ]; // Automatically escape all delimiters and unwise characters from RFC 2396. // Also escape single quotes in case of an XSS attack. // Return the escaped string. function autoEscapeStr(rest: string) { let escaped = ""; let lastEscapedPos = 0; for (let i = 0; i < rest.length; ++i) { // `escaped` contains substring up to the last escaped character. const escapedChar = escapedCodes[StringPrototypeCharCodeAt(rest, i)]; if (escapedChar) { // Concat if there are ordinary characters in the middle. if (i > lastEscapedPos) { escaped += StringPrototypeSlice(rest, lastEscapedPos, i); } escaped += escapedChar; lastEscapedPos = i + 1; } } if (lastEscapedPos === 0) { // Nothing has been escaped. return rest; } // There are ordinary characters at the end. if (lastEscapedPos < rest.length) { escaped += StringPrototypeSlice(rest, lastEscapedPos); } return escaped; } /** * The url.urlParse() method takes a URL string, parses it, and returns a URL object. * * @see Tested in `parallel/test-url-parse-format.js`. * @param url The URL string to parse. * @param parseQueryString If `true`, the query property will always be set to an object returned by the querystring module's parse() method. If false, * the query property on the returned URL object will be an unparsed, undecoded string. Default: false. * @param slashesDenoteHost If `true`, the first token after the literal string // and preceding the next / will be interpreted as the host */ function parse( url: string | Url, parseQueryString: boolean, slashesDenoteHost: boolean, ) { if (ObjectPrototypeIsPrototypeOf(Url.prototype, url)) return url; const urlObject = new Url(); urlObject.urlParse(url, parseQueryString, slashesDenoteHost); return urlObject; } /** The url.resolve() method resolves a target URL relative to a base URL in a manner similar to that of a Web browser resolving an anchor tag HREF. * @see https://nodejs.org/api/url.html#urlresolvefrom-to * @legacy */ function resolve(from: string, to: string) { return parse(from, false, true).resolve(to); } function resolveObject(source: string | Url, relative: string) { if (!source) return relative; return parse(source, false, true).resolveObject(relative); } /** * The url.domainToASCII() takes an arbitrary domain and attempts to convert it into an IDN * * @param domain The domain to convert to an IDN * @see https://www.rfc-editor.org/rfc/rfc3490#section-4 */ function domainToASCII(domain: string) { return idnaToASCII(domain); } /** * The url.domainToUnicode() takes an IDN and attempts to convert it into unicode * * @param domain The IDN to convert to Unicode * @see https://www.rfc-editor.org/rfc/rfc3490#section-4 */ function domainToUnicode(domain: string) { return idnaToUnicode(domain); } /** * This function ensures the correct decodings of percent-encoded characters as well as ensuring a cross-platform valid absolute path string. * @see Tested in `parallel/test-fileurltopath.js`. * @param path The file URL string or URL object to convert to a path. * @returns The fully-resolved platform-specific Node.js file path. */ function fileURLToPath(path: string | URL): string { if (typeof path === "string") path = new URL(path); else if (!ObjectPrototypeIsPrototypeOf(URL.prototype, path)) { throw new ERR_INVALID_ARG_TYPE("path", ["string", "URL"], path); } if (path.protocol !== "file:") { throw new ERR_INVALID_URL_SCHEME("file"); } return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path); } // https://url.spec.whatwg.org/#percent-decode function isHexCharByte(byte: number): boolean { // 0-9 A-F a-f return (byte >= 0x30 && byte <= 0x39) || (byte >= 0x41 && byte <= 0x46) || (byte >= 0x61 && byte <= 0x66); } function hexByteToNumber(byte: number): number { return ( // 0-9 byte >= 0x30 && byte <= 0x39 ? (byte - 48) // Convert to uppercase: ((byte & 0xDF) - 65) + 10 : ((byte & 0xDF) - 55) ); } // Decodes a percent-encoded byte sequence to raw bytes without interpreting // the result as UTF-8. https://url.spec.whatwg.org/#percent-decode function percentDecode(input: Uint8Array): Uint8Array { const length = input.length; const output = new Uint8Array(length); let j = 0; for (let i = 0; i < length; ++i) { const byte = input[i]; if (byte !== 0x25) { output[j++] = byte; } else if ( byte === 0x25 && !(isHexCharByte(input[i + 1]) && isHexCharByte(input[i + 2])) ) { output[j++] = 0x25; } else { output[j++] = (hexByteToNumber(input[i + 1]) << 4) | hexByteToNumber(input[i + 2]); i += 2; } } return length === j ? output : TypedArrayPrototypeSubarray(output, 0, j); } /** * Like `fileURLToPath`, but returns the path as a `Buffer` of raw bytes * obtained by percent-decoding the URL without interpreting the result as * UTF-8. This allows recovering paths that contain non-Unicode byte sequences. * @see Tested in `parallel/test-fileurltopathbuffer.js`. */ function fileURLToPathBuffer( path: string | URL, options: { windows?: boolean } = { __proto__: null }, ) { const windows = options?.windows; if (typeof path === "string") path = new URL(path); else if (!ObjectPrototypeIsPrototypeOf(URL.prototype, path)) { throw new ERR_INVALID_ARG_TYPE("path", ["string", "URL"], path); } if (path.protocol !== "file:") { throw new ERR_INVALID_URL_SCHEME("file"); } return (windows ?? isWindows) ? getPathBufferFromURLWin(path) : getPathBufferFromURLPosix(path); } function getPathBufferFromURLPosix(url: URL) { if (url.hostname !== "") { throw new ERR_INVALID_FILE_URL_HOST(osType); } const Buffer = lazyBuffer(); const u8 = percentDecode(Buffer.from(url.pathname, "utf8")); return Buffer.from( TypedArrayPrototypeGetBuffer(u8), TypedArrayPrototypeGetByteOffset(u8), TypedArrayPrototypeGetByteLength(u8), ); } function getPathBufferFromURLWin(url: URL) { const Buffer = lazyBuffer(); const hostname = url.hostname; const pathname = StringPrototypeReplace( url.pathname, forwardSlashRegEx, "\\", ); const u8 = percentDecode(Buffer.from(pathname, "utf8")); const decodedPathname = Buffer.from( TypedArrayPrototypeGetBuffer(u8), TypedArrayPrototypeGetByteOffset(u8), TypedArrayPrototypeGetByteLength(u8), ); if (hostname !== "") { const prefix = Buffer.from("\\\\", "ascii"); const domain = Buffer.from(domainToUnicode(hostname), "utf8"); // `concat` is a `node:buffer` static method on `Buffer` // deno-lint-ignore deno-internal/prefer-primordials return Buffer.concat([prefix, domain, decodedPathname]); } const letter = decodedPathname[1] | 0x20; const sep = decodedPathname[2]; if ( letter < CHAR_LOWERCASE_A || letter > CHAR_LOWERCASE_Z || // a..z A..Z sep !== 0x3a // : ) { throw new ERR_INVALID_FILE_URL_PATH("must be absolute", url); } return TypedArrayPrototypeSubarray(decodedPathname, 1); } function getPathFromURLWin(url: URL): string { const hostname = url.hostname; let pathname = url.pathname; for (let n = 0; n < pathname.length; n++) { if (pathname[n] === "%") { const third = StringPrototypeCodePointAt(pathname, n + 2)! | 0x20; if ( (pathname[n + 1] === "2" && third === 102) || // 2f 2F / (pathname[n + 1] === "5" && third === 99) // 5c 5C \ ) { throw new ERR_INVALID_FILE_URL_PATH( "must not include encoded \\ or / characters", url, ); } } } pathname = StringPrototypeReplace(pathname, forwardSlashRegEx, "\\"); pathname = decodeURIComponent(pathname); if (hostname !== "") { // TODO(bartlomieju): add support for punycode encodings return `\\\\${hostname}${pathname}`; } else { // Otherwise, it's a local path that requires a drive letter const letter = StringPrototypeCodePointAt(pathname, 1)! | 0x20; const sep = pathname[2]; if ( letter < CHAR_LOWERCASE_A || letter > CHAR_LOWERCASE_Z || // a..z A..Z sep !== ":" ) { throw new ERR_INVALID_FILE_URL_PATH("must be absolute", url); } return StringPrototypeSlice(pathname, 1); } } function getPathFromURLPosix(url: URL): string { if (url.hostname !== "") { throw new ERR_INVALID_FILE_URL_HOST(osType); } const pathname = url.pathname; for (let n = 0; n < pathname.length; n++) { if (pathname[n] === "%") { const third = StringPrototypeCodePointAt(pathname, n + 2)! | 0x20; if (pathname[n + 1] === "2" && third === 102) { throw new ERR_INVALID_FILE_URL_PATH( "must not include encoded / characters", url, ); } } } return decodeURIComponent(pathname); } /** * The following characters are percent-encoded when converting from file path * to URL: * - %: The percent character is the only character not encoded by the * `pathname` setter. * - \: Backslash is encoded on non-windows platforms since it's a valid * character but the `pathname` setters replaces it by a forward slash. * - LF: The newline character is stripped out by the `pathname` setter. * (See whatwg/url#419) * - CR: The carriage return character is also stripped out by the `pathname` * setter. * - TAB: The tab character is also stripped out by the `pathname` setter. */ function encodePathChars( filepath: string, options: { windows?: boolean }, ): string { const windows = options.windows; if (StringPrototypeIncludes(filepath, "%")) { filepath = StringPrototypeReplace(filepath, percentRegEx, "%25"); } // In posix, backslash is a valid character in paths: if (!(windows ?? isWindows) && StringPrototypeIncludes(filepath, "\\")) { filepath = StringPrototypeReplace(filepath, backslashRegEx, "%5C"); } if (StringPrototypeIncludes(filepath, "\n")) { filepath = StringPrototypeReplace(filepath, newlineRegEx, "%0A"); } if (StringPrototypeIncludes(filepath, "\r")) { filepath = StringPrototypeReplace(filepath, carriageReturnRegEx, "%0D"); } if (StringPrototypeIncludes(filepath, "\t")) { filepath = StringPrototypeReplace(filepath, tabRegEx, "%09"); } if (StringPrototypeIncludes(filepath, "^")) { filepath = StringPrototypeReplace(filepath, caretRegEx, "%5E"); } if (StringPrototypeIncludes(filepath, "[")) { filepath = StringPrototypeReplace(filepath, leftBracketRegEx, "%5B"); } if (StringPrototypeIncludes(filepath, "]")) { filepath = StringPrototypeReplace(filepath, rightBracketRegEx, "%5D"); } if (StringPrototypeIncludes(filepath, "|")) { filepath = StringPrototypeReplace(filepath, pipeRegEx, "%7C"); } if (StringPrototypeIncludes(filepath, "~")) { filepath = StringPrototypeReplace(filepath, tildeRegEx, "%7E"); } return filepath; } /** * This function ensures that `filepath` is resolved absolutely, and that the URL control characters are correctly encoded when converting into a File URL. * @see Tested in `parallel/test-url-pathtofileurl.js`. * @param filepath The file path string to convert to a file URL. * @param options The options. * @returns The file URL object. */ function pathToFileURL( filepath: string, options: { windows?: boolean } = { __proto__: null }, ): URL { validateString(filepath, "path"); const windows = options?.windows; const outURL = new URL("file://"); const isWin = windows ?? isWindows; // Extended Win32 path format (\\?\C:\...) is treated as a regular drive // path after stripping the \\?\ prefix. Extended UNC (\\?\UNC\server\...) // still goes through the UNC branch, with the \\?\UNC\ prefix stripped. if ( isWin && StringPrototypeStartsWith(filepath, "\\\\?\\") && !StringPrototypeStartsWith(filepath, "\\\\?\\UNC\\") ) { const stripped = StringPrototypeSlice(filepath, 4); outURL.pathname = encodePathChars(stripped, { windows }); return outURL; } if (isWin && StringPrototypeStartsWith(filepath, "\\\\")) { // UNC path format: \\server\share\resource // Extended UNC path format: \\?\UNC\server\share\resource const isExtendedUNC = StringPrototypeStartsWith(filepath, "\\\\?\\UNC\\"); const prefixLength = isExtendedUNC ? 8 : 2; const hostnameEndIndex = StringPrototypeIndexOf( filepath, "\\", prefixLength, ); if (hostnameEndIndex === -1) { throw new ERR_INVALID_ARG_VALUE( "filepath", filepath, "Missing UNC resource path", ); } if (hostnameEndIndex === prefixLength) { throw new ERR_INVALID_ARG_VALUE( "filepath", filepath, "Empty UNC servername", ); } const hostname = StringPrototypeSlice( filepath, prefixLength, hostnameEndIndex, ); const rest = StringPrototypeSlice(filepath, hostnameEndIndex + 1); outURL.hostname = idnaToASCII(hostname); if (outURL.hostname === "" && hostname !== "") { throw new ERR_INVALID_URL(filepath); } outURL.pathname = encodePathChars( StringPrototypeReplace(rest, backslashRegEx, "/"), { windows, }, ); } else { const path = lazyPath(); let resolved = (windows ?? isWindows) ? path.win32.resolve(filepath) : path.posix.resolve(filepath); // path.resolve strips trailing slashes so we must add them back const filePathLast = StringPrototypeCharCodeAt( filepath, filepath.length - 1, ); if ( (filePathLast === CHAR_FORWARD_SLASH || ((windows ?? isWindows) && filePathLast === CHAR_BACKWARD_SLASH)) && resolved[resolved.length - 1] !== path.sep ) { resolved += "/"; } outURL.pathname = encodePathChars(resolved, { windows }); } return outURL; } const URLSearchParams_ = URLSearchParams; return { URL: _url, URLSearchParams: URLSearchParams_, urlToHttpOptions, Url, parse, format, resolve, resolveObject, domainToASCII, domainToUnicode, fileURLToPath, fileURLToPathBuffer, pathToFileURL, }; })();