diff --git a/packages/libsql-core/src/uri.ts b/packages/libsql-core/src/uri.ts index bd361cb..61bd2a8 100644 --- a/packages/libsql-core/src/uri.ts +++ b/packages/libsql-core/src/uri.ts @@ -39,87 +39,21 @@ export interface KeyValue { } export function parseUri(text: string): Uri { - const match = URI_RE.exec(text); - if (match === null) { - throw new LibsqlError("The URL is not in a valid format", "URL_INVALID"); - } - - const groups = match.groups!; - const scheme = groups["scheme"]!; - const authority = groups["authority"] !== undefined - ? parseAuthority(groups["authority"]) : undefined; - const path = percentDecode(groups["path"]!); - const query = groups["query"] !== undefined - ? parseQuery(groups["query"]) : undefined; - const fragment = groups["fragment"] !== undefined - ? percentDecode(groups["fragment"]) : undefined; - return {scheme, authority, path, query, fragment}; -} - -const URI_RE = (() => { - const SCHEME = '(?[A-Za-z][A-Za-z.+-]*)'; - const AUTHORITY = '(?[^/?#]*)'; - const PATH = '(?[^?#]*)'; - const QUERY = '(?[^#]*)'; - const FRAGMENT = '(?.*)' - return new RegExp(`^${SCHEME}:(//${AUTHORITY})?${PATH}(\\?${QUERY})?(#${FRAGMENT})?$`, "su"); -})(); - -function parseAuthority(text: string): Authority { - const match = AUTHORITY_RE.exec(text); - if (match === null) { - throw new LibsqlError("The authority part of the URL is not in a valid format", "URL_INVALID"); - } - - const groups = match.groups!; - const host = percentDecode(groups["host_br"] ?? groups["host"]); - const port = groups["port"] - ? parseInt(groups["port"], 10) - : undefined; - const userinfo = groups["username"] !== undefined - ? { - username: percentDecode(groups["username"]), - password: groups["password"] !== undefined - ? percentDecode(groups["password"]) : undefined, - } - : undefined; - return {host, port, userinfo}; -} - -const AUTHORITY_RE = (() => { - const USERINFO = '(?[^:]*)(:(?.*))?'; - const HOST = '((?[^:\\[\\]]*)|(\\[(?[^\\[\\]]*)\\]))'; - const PORT = '(?[0-9]*)'; - return new RegExp(`^(${USERINFO}@)?${HOST}(:${PORT})?$`, "su"); -})(); - -// Query string is parsed as application/x-www-form-urlencoded according to the Web URL standard: -// https://url.spec.whatwg.org/#urlencoded-parsing -function parseQuery(text: string): Query { - const sequences = text.split("&"); - const pairs = []; - for (const sequence of sequences) { - if (sequence === "") { - continue; - } - - let key: string; - let value: string; - const splitIdx = sequence.indexOf("="); - if (splitIdx < 0) { - key = sequence; - value = ""; - } else { - key = sequence.substring(0, splitIdx); - value = sequence.substring(splitIdx+1); - } - - pairs.push({ - key: percentDecode(key.replaceAll("+", " ")), - value: percentDecode(value.replaceAll("+", " ")), - }); - } - return {pairs}; + const url = new URL(text); + return { + scheme: url.protocol.slice(0, -1), + authority: { + userinfo: { + username: url.username, + password: url.password, + }, + host: url.hostname, + port: url.port ? parseInt(url.port) : undefined, + }, + path: url.pathname, + query: { pairs: [...url.searchParams.entries()].map(([key, value]) => ({ key, value })) }, + fragment: url.hash || undefined ? percentDecode(url.hash) : undefined, + }; } function percentDecode(text: string): string {