ts-search-params
Version:
Type Safe Search Params
1 lines • 6.11 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/ts-search-params.ts"],"sourcesContent":["export { TSSearchParams } from './ts-search-params'\n","type Options<T extends Record<string, unknown>, Q extends boolean> = {\n validate?: (searchParamsObject: Record<string, unknown>) => T\n questionMark?: Q\n}\n\nexport class TSSearchParams<\n T extends Record<string, unknown>,\n Q extends boolean = false,\n> {\n #value: T\n #options: Required<Options<T, Q>>\n\n constructor(\n init?: string | URLSearchParams | undefined,\n options: Options<T, Q> = {},\n ) {\n const searchParams =\n !init || typeof init === 'string' ? new URLSearchParams(init) : init\n const value = {} as T\n for (let [_k, val] of searchParams.entries()) {\n value[_k as keyof T] = serialize(val) as any\n }\n this.#value = value\n this.#options = {\n validate: options.validate || (() => this.#value),\n questionMark: options.questionMark || (false as Q),\n }\n }\n\n #validated() {\n return this.#options.validate(this.#value)\n }\n\n assign(partial: Partial<T>) {\n Object.assign(this.#value, partial)\n return this\n }\n\n toString(): Q extends true ? `?${string}` : string {\n const qs = Object.entries(this.#validated()).reduce((qs, [_k, _v]) => {\n // remove undefined at the top level\n if (_v === undefined) return qs\n const prefix = qs.length ? '&' : ''\n const key = encodeURIComponent(_k)\n const value = deserialize(_v)\n return qs + `${prefix}${key}=${value}`\n }, '')\n return (this.#options.questionMark ? `?${qs}` : qs) as Q extends true\n ? `?${string}`\n : string\n }\n\n toObject() {\n return this.#validated()\n }\n\n get<K extends keyof T>(key: K): T[K] {\n return this.#validated()[key]\n }\n}\n\nfunction serialize(val: string): any {\n if (isSurroundedBy(val, '{', '}') || isSurroundedBy(val, '[', ']')) {\n return JSON.parse(val, (_, val) => {\n return typeof val === 'string' ? serialize(val) : val\n })\n }\n if (isSurroundedBy(val, '\"')) {\n return val.slice(1, -1)\n }\n if (val === 'true' || val === 'false') {\n return val === 'true'\n }\n if (val === 'null') {\n return null\n }\n if (val === 'NaN') {\n return NaN\n }\n if (val === 'undefined') {\n return undefined\n }\n if (!isNaN(+val) && (+val).toString() === val) {\n return +val\n }\n return val\n}\n\nfunction deserialize(val: any) {\n if (isObjectOrArray(val)) {\n let scope: any\n return encodeURIComponent(\n JSON.stringify(val, (_, value) => {\n if (isObjectOrArray(value)) {\n scope = value\n return value\n }\n if (typeof value === 'string') {\n return `\"${value}\"`\n // remove undefined from objects\n } else if (typeof value === 'undefined' && !Array.isArray(scope)) {\n return undefined\n } else {\n return String(value)\n }\n }),\n )\n }\n // top level empty string values result in `value=` as they go through this case\n if (val && typeof val === 'string') {\n return encodeURIComponent(`\"${val}\"`)\n }\n return String(val)\n}\n\nfunction isSurroundedBy(str: string, start: string, end: string = start) {\n return str.startsWith(start) && str.endsWith(end)\n}\n\nfunction isObjectOrArray(val: any): val is Record<string, unknown> | unknown[] {\n return typeof val === 'object' && val !== null\n}\n"],"mappings":"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","names":["src_exports","__export","TSSearchParams","__toCommonJS","_value","_options","_TSSearchParams_instances","validated_fn","TSSearchParams","init","options","__privateAdd","searchParams","value","_k","val","serialize","__privateSet","__privateGet","partial","qs","__privateMethod","_v","prefix","key","deserialize","isSurroundedBy","_","isObjectOrArray","scope","str","start","end"]}