UNPKG

nuqs

Version:

Type-safe search params state manager for React - Like useState, but stored in the URL query string

721 lines (720 loc) 26.3 kB
"use client"; import { c as debounce, i as createEmitter, l as defaultRateLimit, n as useQueuedQueries, o as isAbsentFromUrl, r as globalThrottleQueue, s as write, t as debounceController, u as throttle } from "./debounce-DPoC4s1g.js"; import { i as warn, t as debug } from "./debug-CH5E3t0A.js"; import { a as useAdapterProcessUrlSearchParams, c as renderQueryString, i as useAdapterDefaultOptions, o as globalSingleton, r as useAdapter } from "./context-Mu913OAK.js"; import { t as compareQuery } from "./compare-DGz2TUZl.js"; import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react"; //#region src/lib/url-keys.ts function getOwn(object, key) { return Object.hasOwn(object, key) ? object[key] : void 0; } function getUrlKey(urlKeys, key) { return getOwn(urlKeys, key) ?? key; } //#endregion //#region src/loader.ts function createLoader(parsers, { urlKeys = {} } = {}) { function loadSearchParams(input, { strict = false } = {}) { if (input instanceof Promise) return input.then((i) => loadSearchParams(i, { strict })); const searchParams = extractSearchParams(input); const result = {}; for (const [key, parser] of Object.entries(parsers)) { const urlKey = getUrlKey(urlKeys, key); const query = parser.type === "multi" ? searchParams.getAll(urlKey) : searchParams.get(urlKey); if (isAbsentFromUrl(query)) { result[key] = parser.defaultValue ?? null; continue; } let parsedValue; try { parsedValue = parser.parse(query); } catch (error) { if (strict) throw new Error(`[nuqs] Error while parsing query \`${query}\` for key \`${key}\`: ${error}`); parsedValue = null; } if (strict && parsedValue === null) throw new Error(`[nuqs] Failed to parse query \`${query}\` for key \`${key}\` (got null)`); result[key] = parsedValue ?? parser.defaultValue ?? null; } return result; } return loadSearchParams; } function extractSearchParams(input) { try { if (input instanceof Request) return input.url ? new URL(input.url).searchParams : new URLSearchParams(); if (input instanceof URL) return input.searchParams; if (input instanceof URLSearchParams) return input; if (typeof input === "object") { const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(input)) if (Array.isArray(value)) for (const v of value) searchParams.append(key, v); else if (value !== void 0) searchParams.set(key, value); return searchParams; } if (typeof input === "string") { if (URL.canParse(input)) return new URL(input).searchParams; return new URLSearchParams(input); } } catch {} return new URLSearchParams(); } //#endregion //#region src/lib/safe-parse.ts function safeParse(parser, value, key) { try { return parser(value); } catch (error) { if (key) warn(25, value, error, key); else warn(24, value, error); return null; } } //#endregion //#region src/parsers.ts /** * Wrap a set of parse/serialize functions into a builder pattern parser * you can pass to one of the hooks, making its default value type safe. */ function createParser(parser) { function parseServerSideNullable(value) { if (typeof value === "undefined") return null; let str = ""; if (Array.isArray(value)) { if (value[0] === void 0) return null; str = value[0]; } if (typeof value === "string") str = value; return safeParse(parser.parse, str); } return { type: "single", eq: (a, b) => a === b, ...parser, parseServerSide: parseServerSideNullable, withDefault(defaultValue) { return { ...this, defaultValue, parseServerSide(value) { return parseServerSideNullable(value) ?? defaultValue; } }; }, withOptions(options) { return { ...this, ...options }; } }; } function createMultiParser(parser) { function parseServerSideNullable(value) { if (typeof value === "undefined") return null; return safeParse(parser.parse, Array.isArray(value) ? value : [value]); } return { type: "multi", eq: (a, b) => a === b, ...parser, parseServerSide: parseServerSideNullable, withDefault(defaultValue) { return { ...this, defaultValue, parseServerSide(value) { return parseServerSideNullable(value) ?? defaultValue; } }; }, withOptions(options) { return { ...this, ...options }; } }; } const parseAsString = createParser({ parse: (v) => v, serialize: String }); const parseAsInteger = createParser({ parse: (v) => { const int = parseInt(v); return int == int ? int : null; }, serialize: (v) => "" + Math.round(v) }); const parseAsIndex = createParser({ parse: (v) => { const int = parseInt(v); return int == int ? int - 1 : null; }, serialize: (v) => "" + Math.round(v + 1) }); const parseAsHex = createParser({ parse: (v) => { const int = parseInt(v, 16); return int == int ? int : null; }, serialize: (v) => { const hex = Math.round(v).toString(16); return hex < "0" || !(hex.length & 1) ? hex : "0" + hex; } }); const parseAsFloat = createParser({ parse: (v) => { const float = parseFloat(v); return float == float ? float : null; }, serialize: String }); const parseAsBoolean = createParser({ parse: (v) => v.toLowerCase() === "true", serialize: String }); function compareDates(a, b) { return a.valueOf() === b.valueOf(); } /** * Querystring encoded as the number of milliseconds since epoch, * and returned as a Date object. */ const parseAsTimestamp = createParser({ parse: (v) => { const date = new Date(parseInt(v)); return date.valueOf() == date.valueOf() ? date : null; }, serialize: (v) => "" + v.valueOf(), eq: compareDates }); /** * Querystring encoded as an ISO-8601 string (UTC), * and returned as a Date object. */ const parseAsIsoDateTime = createParser({ parse: (v) => { const date = new Date(v); return date.valueOf() == date.valueOf() ? date : null; }, serialize: (v) => v.toISOString(), eq: compareDates }); /** * Querystring encoded as an ISO-8601 string (UTC) * without the time zone offset, and returned as * a Date object. * * The Date is parsed without the time zone offset, * making it at 00:00:00 UTC. */ const parseAsIsoDate = createParser({ parse: (v) => { const date = new Date(v.slice(0, 10)); return date.valueOf() == date.valueOf() ? date : null; }, serialize: (v) => v.toISOString().slice(0, 10), eq: compareDates }); /** * String-based enums provide better type-safety for known sets of values. * You will need to pass the parseAsStringEnum function a list of your enum values * in order to validate the query string. Anything else will return `null`, * or your default value if specified. * * Example: * ```ts * enum Direction { * up = 'UP', * down = 'DOWN', * left = 'LEFT', * right = 'RIGHT' * } * * const [direction, setDirection] = useQueryState( * 'direction', * parseAsStringEnum<Direction>(Object.values(Direction)) // pass a list of allowed values * .withDefault(Direction.up) * ) * ``` * * Note: the query string value will be the value of the enum, not its name * (example above: `direction=UP`). * * @param validValues The values you want to accept */ function parseAsStringEnum(validValues) { return parseAsStringLiteral(validValues); } /** * String-based literals provide better type-safety for known sets of values. * You will need to pass the parseAsStringLiteral function a list of your string values * in order to validate the query string. Anything else will return `null`, * or your default value if specified. * * Example: * ```ts * const colors = ["red", "green", "blue"] as const * * const [color, setColor] = useQueryState( * 'color', * parseAsStringLiteral(colors) // pass a readonly list of allowed values * .withDefault("red") * ) * ``` * * @param validValues The values you want to accept */ function parseAsStringLiteral(validValues) { return createParser({ parse: (query) => { const asConst = query; return validValues.includes(asConst) ? asConst : null; }, serialize: String }); } /** * Number-based literals provide better type-safety for known sets of values. * You will need to pass the parseAsNumberLiteral function a list of your number values * in order to validate the query string. Anything else will return `null`, * or your default value if specified. * * Example: * ```ts * const diceSides = [1, 2, 3, 4, 5, 6] as const * * const [side, setSide] = useQueryState( * 'side', * parseAsNumberLiteral(diceSides) // pass a readonly list of allowed values * .withDefault(4) * ) * ``` * * @param validValues The values you want to accept */ function parseAsNumberLiteral(validValues) { return createParser({ parse: (query) => { const asConst = parseFloat(query); if (validValues.includes(asConst)) return asConst; return null; }, serialize: String }); } /** * Encode any object shape into the querystring value as JSON. * Note: you may want to use `useQueryStates` for finer control over * multiple related query keys. * * @param runtimeParser Runtime parser (eg: Zod schema or Standard Schema) to validate after JSON.parse */ function parseAsJson(validator) { return createParser({ parse: (query) => { try { const obj = JSON.parse(query); if ("~standard" in validator) { const result = validator["~standard"].validate(obj); if (result instanceof Promise) throw new Error("[nuqs] Only synchronous Standard Schemas are supported in parseAsJson."); return result.issues ? null : result.value; } return validator(obj); } catch { return null; } }, serialize: (value) => JSON.stringify(value), eq(a, b) { return a === b || JSON.stringify(a) === JSON.stringify(b); } }); } /** * A comma-separated list of items. * Items are URI-encoded for safety, so they may not look nice in the URL. * * @param itemParser Parser for each individual item in the array * @param separator The character to use to separate items (default ',') */ function parseAsArrayOf(itemParser, separator = ",") { const itemEq = itemParser.eq ?? ((a, b) => a === b); const encodedSeparator = encodeURIComponent(separator); return createParser({ parse: (query) => { if (query === "") return []; return query.split(separator).map((item, index) => safeParse(itemParser.parse, item.replaceAll(encodedSeparator, separator), `[${index}]`)).filter((value) => value !== null && value !== void 0); }, serialize: (values) => values.map((value) => { return (itemParser.serialize ? itemParser.serialize(value) : String(value)).replaceAll(separator, encodedSeparator); }).join(separator), eq(a, b) { if (a === b) return true; if (a.length !== b.length) return false; return a.every((value, index) => itemEq(value, b[index])); } }); } function parseAsNativeArrayOf(itemParser) { const itemEq = itemParser.eq ?? ((a, b) => a === b); return createMultiParser({ parse: (query) => { const parsed = query.map((item, index) => safeParse(itemParser.parse, item, `[${index}]`)).filter((value) => value !== null && value !== void 0); if (parsed.length > 0) return parsed; return query.length === 1 && query[0] === "" ? [] : null; }, serialize: (values) => { return (Array.isArray(values) ? values : [values]).flatMap((value) => { const serialized = itemParser.serialize?.(value) ?? String(value); return typeof serialized === "string" ? [serialized] : [...serialized]; }); }, eq(a, b) { if (a === b) return true; if (a.length !== b.length) return false; return a.every((value, index) => itemEq(value, b[index])); } }).withDefault([]); } //#endregion //#region src/serializer.ts function createSerializer(parsers, { clearOnDefault = true, urlKeys = {}, processUrlSearchParams } = {}) { function serialize(arg1BaseOrValues, arg2values = {}) { let [base, search, hash] = isBase(arg1BaseOrValues) ? splitBase(arg1BaseOrValues) : [ "", new URLSearchParams(), "" ]; const values = isBase(arg1BaseOrValues) ? arg2values : arg1BaseOrValues; if (values === null) { for (const key of Object.keys(parsers)) { const urlKey = getUrlKey(urlKeys, key); search.delete(urlKey); } if (processUrlSearchParams) search = processUrlSearchParams(search); return base + renderQueryString(search) + hash; } for (const key of Object.keys(parsers)) { const parser = parsers[key]; const value = getOwn(values, key); if (value === void 0) continue; const urlKey = getUrlKey(urlKeys, key); const isMatchingDefault = parser.defaultValue !== void 0 && value !== null && (parser.eq ?? ((a, b) => a === b))(value, parser.defaultValue); if (value === null || (parser.clearOnDefault ?? clearOnDefault ?? true) && isMatchingDefault) search.delete(urlKey); else { const serialized = parser.serialize(value); search = write(search, urlKey, serialized); } } if (processUrlSearchParams) search = processUrlSearchParams(search); return base + renderQueryString(search) + hash; } return serialize; } function isBase(base) { return typeof base === "string" || base instanceof URLSearchParams || base instanceof URL; } function splitBase(base) { if (typeof base === "string") { const [pathAndSearch = "", ...hashParts] = base.split("#"); const hash = hashParts.length ? "#" + hashParts.join("#") : ""; const [path = "", ...search] = pathAndSearch.split("?"); return [ path, new URLSearchParams(search.join("?")), hash ]; } else if (base instanceof URLSearchParams) return [ "", new URLSearchParams(base), "" ]; else { const baseLength = base.href.length - base.search.length - base.hash.length; const path = base.href.slice(0, baseLength).replace(/[?#]+$/, ""); const hash = base.hash || (base.href.endsWith("#") ? "#" : ""); return [ path, new URLSearchParams(base.searchParams), hash ]; } } //#endregion //#region src/standard-schema.ts function createStandardSchemaV1(parsers, { urlKeys, partialOutput = false } = {}) { const serialize = createSerializer(parsers, { urlKeys }); const load = createLoader(parsers, { urlKeys }); return { "~standard": { version: 1, vendor: "nuqs", validate(input) { try { const url = serialize(input); const value = load(url, { strict: true }); if (partialOutput) { for (const key in value) if (!(key in input)) delete value[key]; } return { value }; } catch (error) { return { issues: [{ message: error instanceof Error ? error.message : String(error) }] }; } } } }; } //#endregion //#region src/lib/sync.ts const emitter = globalSingleton("sync-emitter", () => createEmitter()); //#endregion //#region src/useQueryStates.ts const defaultUrlKeys = {}; const omitDefaultValue = (key, value) => key === "defaultValue" ? void 0 : value; /** * Synchronise multiple query string arguments to React state in Next.js * * @param keys - An object describing the keys to synchronise and how to * serialise and parse them. * Use `parseAs(String|Integer|Float|...)` for quick shorthands. * @param options - Optional history mode, shallow routing and scroll restoration options. */ function useQueryStates(keyMap, options = {}) { const hookId = useId(); const defaultOptions = useAdapterDefaultOptions(); const processUrlSearchParams = useAdapterProcessUrlSearchParams(); const { history = defaultOptions?.history ?? "replace", scroll = defaultOptions?.scroll ?? false, shallow = defaultOptions?.shallow ?? true, throttleMs = defaultRateLimit.timeMs, limitUrlUpdates = defaultOptions?.limitUrlUpdates, clearOnDefault = defaultOptions?.clearOnDefault ?? true, startTransition, urlKeys = defaultUrlKeys } = options; const stateKeys = Object.keys(keyMap).join(","); const cachedKeyMapRef = useRef(keyMap); const cachedKeyMap = cachedKeyMapRef.current; const stableKeyMap = JSON.stringify(Object.entries(cachedKeyMap), omitDefaultValue) === JSON.stringify(Object.entries(keyMap), omitDefaultValue) && Object.entries(keyMap).every(([key, parser]) => { if (cachedKeyMap[key]?.startTransition !== parser.startTransition) return false; const previousDefault = cachedKeyMap[key]?.defaultValue; const currentDefault = parser.defaultValue; if (Object.is(previousDefault, currentDefault)) return true; return previousDefault !== void 0 && currentDefault !== void 0 && parser.eq?.(previousDefault, currentDefault) === true; }) ? cachedKeyMap : keyMap; cachedKeyMapRef.current = stableKeyMap; const resolvedUrlKeys = useMemo(() => Object.fromEntries(Object.keys(keyMap).map((key) => [key, getUrlKey(urlKeys, key)])), [stateKeys, JSON.stringify(urlKeys)]); const adapter = useAdapter(Object.values(resolvedUrlKeys)); const initialSearchParams = adapter.searchParams; const queryRef = useRef({}); const lastSyncKeyRef = useRef(null); const committedPathnameRef = useRef(null); const queuedQueries = useQueuedQueries(Object.values(resolvedUrlKeys)); const [internalState, setInternalState] = useState(() => parseMap(keyMap, resolvedUrlKeys, initialSearchParams, queuedQueries).state); const stateRef = useRef(internalState); const searchParamsSyncKey = JSON.stringify([Object.values(resolvedUrlKeys).map((key) => [key, initialSearchParams.getAll(key)]), queuedQueries]); const reconcile = () => { const { state, hasChanged } = parseMap(keyMap, resolvedUrlKeys, initialSearchParams, queuedQueries, queryRef.current, stateRef.current); if (hasChanged) { debug(1, hookId, stateKeys, state); stateRef.current = state; setInternalState(state); } return hasChanged; }; const keysChanged = Object.keys(queryRef.current).join("&") !== Object.values(resolvedUrlKeys).join("&"); const onCommittedPathname = committedPathnameRef.current === null || committedPathnameRef.current === (adapter.pathname ?? location.pathname); let didReconcileState = false; if (keysChanged || onCommittedPathname && lastSyncKeyRef.current !== searchParamsSyncKey) { lastSyncKeyRef.current = searchParamsSyncKey; didReconcileState = reconcile(); if (keysChanged) queryRef.current = Object.fromEntries(Object.entries(resolvedUrlKeys).map(([key, urlKey]) => { return [urlKey, keyMap[key]?.type === "multi" ? initialSearchParams.getAll(urlKey) : initialSearchParams.get(urlKey) ?? null]; })); } if (!keysChanged && !didReconcileState && onCommittedPathname && internalState !== stateRef.current) setInternalState(stateRef.current); useEffect(() => { committedPathnameRef.current = adapter.pathname ?? location.pathname; reconcile(); }, [searchParamsSyncKey, adapter.pathname]); useEffect(() => { const handlers = Object.keys(keyMap).reduce((handlers, stateKey) => { handlers[stateKey] = ({ state, query }) => { setInternalState((currentState) => { const urlKey = resolvedUrlKeys[stateKey]; if (Object.is(currentState[stateKey] ?? null, state)) { debug(2, hookId, stateKeys, urlKey, state, keyMap[stateKey]?.defaultValue, stateRef.current); return currentState; } stateRef.current = { ...stateRef.current, [stateKey]: state }; queryRef.current[urlKey] = query; debug(3, hookId, stateKeys, urlKey, state, keyMap[stateKey]?.defaultValue, stateRef.current); return stateRef.current; }); }; return handlers; }, {}); for (const stateKey of Object.keys(keyMap)) { const urlKey = resolvedUrlKeys[stateKey]; debug(4, hookId, urlKey, stateKeys); emitter.on(urlKey, handlers[stateKey]); } return () => { for (const stateKey of Object.keys(keyMap)) { const urlKey = resolvedUrlKeys[stateKey]; debug(5, hookId, urlKey, stateKeys); emitter.off(urlKey, handlers[stateKey]); } }; }, [stateKeys, resolvedUrlKeys]); const update = useCallback((stateUpdater, callOptions = {}) => { const nullMap = Object.fromEntries(Object.keys(stableKeyMap).map((key) => [key, null])); const newState = typeof stateUpdater === "function" ? stateUpdater(applyDefaultValues(stateRef.current, stableKeyMap)) ?? nullMap : stateUpdater ?? nullMap; debug(6, hookId, stateKeys, newState); let returnedPromise = void 0; let maxDebounceTime = 0; let doFlush = false; const debounceAborts = []; for (let [stateKey, value] of Object.entries(newState)) { const parser = getOwn(stableKeyMap, stateKey); const urlKey = resolvedUrlKeys[stateKey]; if (!parser || urlKey === void 0 || value === void 0) continue; if ((callOptions.clearOnDefault ?? parser.clearOnDefault ?? clearOnDefault) && value !== null && parser.defaultValue !== void 0 && (parser.eq ?? ((a, b) => a === b))(value, parser.defaultValue)) value = null; const query = value === null ? null : (parser.serialize ?? String)(value); emitter.emit(urlKey, { state: value, query }); const update = { key: urlKey, query, options: { history: callOptions.history ?? parser.history ?? history, shallow: callOptions.shallow ?? parser.shallow ?? shallow, scroll: callOptions.scroll ?? parser.scroll ?? scroll, startTransition: callOptions.startTransition ?? parser.startTransition ?? startTransition } }; const resolvedLimitUrlUpdates = callOptions.limitUrlUpdates ?? parser.limitUrlUpdates ?? limitUrlUpdates; if (resolvedLimitUrlUpdates?.method === "debounce") { const timeMs = resolvedLimitUrlUpdates.timeMs ?? defaultRateLimit.timeMs; const debouncedPromise = debounceController.push(update, timeMs, adapter, processUrlSearchParams); if (maxDebounceTime < timeMs) { returnedPromise = debouncedPromise; maxDebounceTime = timeMs; } } else { const timeMs = resolvedLimitUrlUpdates?.timeMs ?? callOptions.throttleMs ?? parser.throttleMs ?? throttleMs; debounceAborts.push(debounceController.abort(urlKey)); globalThrottleQueue.push(update, timeMs); doFlush = true; } } const globalPromise = debounceAborts.reduce((previous, fn) => fn(previous), doFlush ? globalThrottleQueue.flush(adapter, processUrlSearchParams) : globalThrottleQueue.getPendingPromise(adapter)); return returnedPromise ?? globalPromise; }, [ stateKeys, history, shallow, scroll, throttleMs, limitUrlUpdates?.method, limitUrlUpdates?.timeMs, startTransition, clearOnDefault, stableKeyMap, resolvedUrlKeys, adapter.updateUrl, adapter.getSearchParamsSnapshot, adapter.rateLimitFactor, processUrlSearchParams ]); return [useMemo(() => applyDefaultValues(internalState, stableKeyMap), [internalState, stableKeyMap]), update]; } function parseMap(keyMap, resolvedUrlKeys, searchParams, queuedQueries, cachedQuery, cachedState) { let hasChanged = false; const state = Object.entries(keyMap).reduce((out, [stateKey, parser]) => { const urlKey = resolvedUrlKeys[stateKey]; const queuedQuery = getOwn(queuedQueries, urlKey); const fallbackValue = parser.type === "multi" ? [] : null; const query = queuedQuery === void 0 ? (parser.type === "multi" ? searchParams.getAll(urlKey) : searchParams.get(urlKey)) ?? fallbackValue : queuedQuery; const cachedStateValue = cachedState && getOwn(cachedState, stateKey); if (cachedQuery && cachedStateValue !== void 0 && compareQuery(getOwn(cachedQuery, urlKey) ?? fallbackValue, query)) { out[stateKey] = cachedStateValue; return out; } hasChanged = true; out[stateKey] = (isAbsentFromUrl(query) ? null : safeParse(parser.parse, query, urlKey)) ?? null; if (cachedQuery) cachedQuery[urlKey] = query; return out; }, {}); if (!hasChanged) { const keyMapKeys = Object.keys(keyMap); const cachedStateKeys = Object.keys(cachedState ?? {}); hasChanged = keyMapKeys.length !== cachedStateKeys.length || keyMapKeys.some((key) => !cachedStateKeys.includes(key)); } return { state, hasChanged }; } function applyDefaultValues(state, keyMap) { return Object.fromEntries(Object.keys(state).map((key) => [key, state[key] ?? keyMap[key]?.defaultValue ?? null])); } //#endregion //#region src/useQueryState.ts /** * React state hook synchronized with a URL query string in Next.js * * If used without a `defaultValue` supplied in the options, and the query is * missing in the URL, the state will be `null`. * * ### Behaviour with default values: * * _Note: the URL will **not** be updated with the default value if the query * is missing._ * * Setting the value to `null` will clear the query in the URL, and return * the default value as state. * * Example usage: * ```ts * // Blog posts filtering by tag * const [tag, selectTag] = useQueryState('tag') * const filteredPosts = posts.filter(post => tag ? post.tag === tag : true) * const clearTag = () => selectTag(null) * * // With default values * * const [count, setCount] = useQueryState( * 'count', * parseAsInteger.defaultValue(0) * ) * * const increment = () => setCount(oldCount => oldCount + 1) * const decrement = () => setCount(oldCount => oldCount - 1) * const clearCountQuery = () => setCount(null) * * // -- * * const [date, setDate] = useQueryState( * 'date', * parseAsIsoDateTime.withDefault(new Date('2021-01-01')) * ) * * const setToNow = () => setDate(new Date()) * const addOneHour = () => { * setDate(oldDate => new Date(oldDate.valueOf() + 3600_000)) * } * ``` * @param key The URL query string key to bind to * @param options - Parser (defines the state data type), optional default value and history mode. */ function useQueryState(key, options = {}) { const { parse, type, serialize, eq, defaultValue, ...hookOptions } = options; const [{ [key]: state }, setState] = useQueryStates({ [key]: { parse: parse ?? ((x) => x), type, serialize, eq, defaultValue } }, hookOptions); return [state, useCallback((stateUpdater, callOptions = {}) => setState((old) => ({ [key]: typeof stateUpdater === "function" ? stateUpdater(old[key]) : stateUpdater }), callOptions), [key, setState])]; } //#endregion export { createLoader, createMultiParser, createParser, createSerializer, createStandardSchemaV1, debounce, defaultRateLimit, parseAsArrayOf, parseAsBoolean, parseAsFloat, parseAsHex, parseAsIndex, parseAsInteger, parseAsIsoDate, parseAsIsoDateTime, parseAsJson, parseAsNativeArrayOf, parseAsNumberLiteral, parseAsString, parseAsStringEnum, parseAsStringLiteral, parseAsTimestamp, throttle, useQueryState, useQueryStates }; //# sourceMappingURL=index.js.map