import { ObjectCreate, ObjectDefineProperty, TypeError, WeakMapPrototypeSet as weakSet, } from "ext:core/primordials"; import { required, state, usv } from "ext:web/idl.js"; import { op_url_parse, op_url_set } from "ext:core/ops"; import { URLSearchParams } from "./search_params.js"; import { params, queryList, urls } from "./state.js"; import type { URLComponent, URLParts } from "ext:core/ops"; function parse(rawInput: unknown, rawBase: unknown, count: number): URLParts | null { required(count, 1); const input = usv(rawInput); const base = rawBase === undefined ? null : usv(rawBase); return op_url_parse([input, base]); } function initializeURL(receiver: URL, parts: URLParts): URL { const searchParams: URLSearchParams = ObjectCreate(URLSearchParams.prototype); const url = { parts, searchParams }; weakSet(params, searchParams, { list: queryList(parts), url }); weakSet(urls, receiver, url); return receiver; } // Component accessors are installed from the table below, without instance fields. interface URL extends Record { readonly origin: string; } class URL { constructor(input: unknown, base: unknown = undefined) { const parts = parse(input, base, arguments.length); if (parts === null) throw new TypeError("Invalid URL"); initializeURL(this, parts); } static canParse(input: unknown, base: unknown = undefined): boolean { return parse(input, base, arguments.length) !== null; } static parse(input: unknown, base: unknown = undefined): URL | null { const parts = parse(input, base, arguments.length); return parts === null ? null : initializeURL(ObjectCreate(URL.prototype), parts); } get searchParams(): URLSearchParams { return state(urls, this).searchParams; } toString(): string { return state(urls, this).parts[0]; } toJSON(): string { return state(urls, this).parts[0]; } } const components = [ "href", "origin", "protocol", "username", "password", "host", "hostname", "port", "pathname", "search", "hash", ] as const; for (let index = 0; index < components.length; index++) { const name = components[index]; const descriptor: { get(this: URL): string; set?: (this: URL, value: unknown) => void; enumerable: boolean; configurable: boolean; } = { get(this: URL) { return state(urls, this).parts[index]; }, enumerable: true, configurable: true, }; if (name !== "origin") { descriptor.set = { set(this: URL, rawValue: unknown) { const data = state(urls, this); const value = usv(rawValue); const parts = op_url_set([data.parts[0], name, value]); if (parts === null) throw new TypeError("Invalid URL"); data.parts = parts; if (name === "href" || name === "search") { state(params, data.searchParams).list = queryList(parts); } }, }.set; } ObjectDefineProperty(descriptor.get, "name", { value: `get ${name}`, configurable: true, }); if (descriptor.set) { ObjectDefineProperty(descriptor.set, "name", { value: `set ${name}`, configurable: true, }); } ObjectDefineProperty(URL.prototype, name, descriptor); } export { URL };