export interface URI { path: string // native URLSearchParams is not worth here // we can't use URLSearchParams.toString() because it encodes the spaces as `+` instead of `%20` // causing issues in some cases like odata queries // also not encoding the keys is good for dev readability (lime doesn't care much about it) query: Map } export const uri = (parts: TemplateStringsArray, ...vars: Array): URI => { let queryObject: Record | undefined const last = vars[vars.length - 1] if (last && last.constructor === Object) { queryObject = last as Record vars.splice(vars.length - 1, 1) } const stringify = (v: unknown): string => v === undefined || v === null ? '' : v instanceof Date ? v.toISOString() : String(v) const raw = parts.map((part, i) => part + encodeURIComponent(stringify(vars[i]))).join('') const [path, rawQuery = ''] = raw.split('?', 2) const query = new Map(new URLSearchParams(rawQuery)) if (queryObject) { for (const [key, value] of Object.entries(queryObject)) { if (value !== undefined && value !== null) { query.set(key, stringify(value)) } } } return { path, query } } export const uriToString = (uri: URI): string => { if (uri.query.size === 0) { return uri.path } const query = Array.from(uri.query) .map(([key, value]) => `${key}=${encodeURIComponent(value)}`) .join('&') return `${uri.path}?${query}` }