vuerl 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/useQueryState.ts","../src/internal/rate-limiter.ts","../src/internal/utils.ts","../src/useQueryStates.ts","../src/parsers.ts"],"sourcesContent":["import { onBeforeUnmount, ref, watch } from 'vue'\nimport type { Ref } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport type { Parser, QueryStateOptions, StateSetter, StateValue } from './types'\nimport { getSafeBrowserDelay } from './internal/rate-limiter'\nimport { buildQueryString, cloneValue, normalizeQueryValue } from './internal/utils'\n\n/**\n * Composable for syncing a single query parameter with component state\n * Provides bidirectional synchronization between URL and component state\n *\n * @param key - Query parameter name\n * @param parser - Parser for type conversion\n * @param options - Configuration options\n * @returns Tuple of [state, setState]\n *\n * @example\n * ```typescript\n * const [search] = useQueryState('q', parseAsString)\n * const [count] = useQueryState('count', parseAsInteger.withDefault(0))\n *\n * // State updates immediately\n * count.value = 42\n *\n * // URL updates after debounce (browser-safe)\n * // ?count=42\n * ```\n */\nexport function useQueryState<P extends Parser<any>>(\n key: string,\n parser: P,\n options: QueryStateOptions = {}\n): [state: Ref<StateValue<P>>, setState: (value: StateSetter<P>) => void] {\n const route = useRoute()\n const router = useRouter()\n\n const parserOptions = parser.options ?? {}\n\n // Configuration\n const historyMode = options.history ?? parserOptions.history ?? 'replace'\n const clearOnDefault = options.clearOnDefault ?? parserOptions.clearOnDefault ?? true\n const shallowRouting = options.shallow ?? parserOptions.shallow ?? true\n const shouldScroll = options.scroll ?? parserOptions.scroll ?? false\n\n const browserSafeDelay = getSafeBrowserDelay()\n const customDebounce = options.debounce ?? parserOptions.debounce ?? null\n const customThrottle = options.throttle ?? parserOptions.throttle ?? null\n const resolvedDebounce = customDebounce !== null\n ? Math.max(customDebounce, browserSafeDelay)\n : null\n const resolvedThrottle = resolvedDebounce === null && customThrottle !== null\n ? Math.max(customThrottle, browserSafeDelay)\n : null\n const fallbackDebounce = resolvedDebounce ?? browserSafeDelay\n\n // State\n type State = StateValue<P>\n\n const state = ref<State>(getStateFromQueryValue(route.query[key])) as Ref<State>\n let updateTimeout: ReturnType<typeof setTimeout> | null = null\n let isNavigating = false\n let lastThrottleTimestamp = 0\n\n function resolveDefaultValue(): State {\n if (parser.default !== undefined) {\n return cloneValue(parser.default) as State\n }\n return null as State\n }\n\n function normalizeValue(value: State | ReturnType<P['parse']> | null | undefined): State {\n if (value === undefined || value === null) {\n return resolveDefaultValue()\n }\n return value as State\n }\n\n function getStateFromQueryValue(rawValue: unknown): State {\n const normalized = normalizeQueryValue(rawValue)\n const parsed = parser.parse(normalized)\n return normalizeValue(parsed ?? null)\n }\n\n /**\n * Check if value equals default (for clearing)\n */\n function equalsDefault(value: State): boolean {\n if (parser.default === undefined) {\n return value === null\n }\n if (parser.eq) {\n return parser.eq(value as any, parser.default)\n }\n return value === parser.default\n }\n\n /**\n * Update URL with current state value\n * Called after debounce delay\n */\n async function updateUrl(): Promise<void> {\n updateTimeout = null\n\n // Don't update if we're in an external navigation\n if (isNavigating) {\n return\n }\n\n const serialized = parser.serialize(state.value === null ? null : (state.value as any))\n const newQuery: Record<string, string | string[] | undefined> = {}\n\n for (const [queryKey, queryValue] of Object.entries(route.query)) {\n if (queryValue === null) {\n continue\n }\n newQuery[queryKey] = queryValue as string | string[]\n }\n\n // Determine if we should remove the parameter\n if (serialized === null || (clearOnDefault && equalsDefault(state.value))) {\n delete newQuery[key]\n } else {\n newQuery[key] = serialized\n }\n\n // Check URL length\n const urlString = buildQueryString(newQuery)\n\n if (urlString.length > 2000) {\n console.warn(\n `URL length (${urlString.length}) exceeds recommended 2000 characters. ` +\n `Consider simplifying or moving state to session storage.`\n )\n }\n\n // Update router\n const navigationTarget = shallowRouting\n ? { query: newQuery }\n : { path: route.path, hash: route.hash, query: newQuery }\n\n await router[historyMode](navigationTarget)\n\n if (shouldScroll && typeof window !== 'undefined' && typeof window.scrollTo === 'function') {\n window.scrollTo({ top: 0, left: 0, behavior: 'auto' })\n }\n }\n\n /**\n * Queue a URL update with debounce\n */\n function queueUrlUpdate(): void {\n if (resolvedThrottle !== null) {\n const now = Date.now()\n const remaining = resolvedThrottle - (now - lastThrottleTimestamp)\n\n if (remaining <= 0) {\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n updateTimeout = null\n }\n lastThrottleTimestamp = now\n void updateUrl()\n return\n }\n\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n }\n updateTimeout = setTimeout(() => {\n lastThrottleTimestamp = Date.now()\n updateTimeout = null\n void updateUrl()\n }, remaining)\n return\n }\n\n const delay = resolvedDebounce ?? fallbackDebounce\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n }\n updateTimeout = setTimeout(() => {\n updateTimeout = null\n void updateUrl()\n }, delay)\n }\n\n /**\n * Set state and queue URL update\n */\n function setState(value: StateSetter<P>): void {\n const computedValue = typeof value === 'function'\n ? (value as (prev: State) => State)(state.value)\n : value\n const newValue = normalizeValue(computedValue as State | null | undefined)\n\n // Use custom equality if provided, otherwise use reference equality\n const hasChanged = parser.eq ? !parser.eq(newValue as any, state.value as any) : newValue !== state.value\n\n if (hasChanged) {\n state.value = newValue\n queueUrlUpdate()\n }\n }\n\n /**\n * Watch for external URL changes (back button, direct navigation)\n */\n const urlWatcher = watch(\n () => route.query[key],\n (newValue) => {\n // Mark that we're navigating to prevent URL update race condition\n isNavigating = true\n\n // Cancel any pending URL update\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n updateTimeout = null\n }\n\n // Parse and update state from new URL\n state.value = getStateFromQueryValue(newValue)\n\n // Re-enable URL updates after next tick\n setTimeout(() => {\n isNavigating = false\n }, 0)\n }\n )\n\n // Cleanup on unmount\n onBeforeUnmount(() => {\n urlWatcher()\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n }\n })\n\n return [state, setState]\n}\n","/**\n * Browser-aware rate limiting for URL updates\n *\n * Different browsers have different performance characteristics:\n * - Safari: Limited to ~120ms between updates without crashes\n * - Chrome/Firefox/Edge: Can handle ~50ms between updates\n *\n * This detection happens once at module load and is cached\n */\n\nlet cachedDelay: number | null = null\n\n/**\n * Detects the current browser and returns the safe minimum delay\n * for URL updates to prevent browser crashes\n */\nfunction detectBrowserDelay(): number {\n if (typeof window === 'undefined') {\n // SSR environment\n return 50\n }\n\n const userAgent = navigator.userAgent.toLowerCase()\n const isSafari =\n /safari/i.test(userAgent) &&\n !/chrome/i.test(userAgent) &&\n !/chromium/i.test(userAgent) &&\n !/firefox/i.test(userAgent)\n\n return isSafari ? 120 : 50\n}\n\n/**\n * Get the safe browser delay, cached after first call\n * @returns Minimum delay in milliseconds\n */\nexport function getSafeBrowserDelay(): number {\n if (cachedDelay === null) {\n cachedDelay = detectBrowserDelay()\n }\n return cachedDelay\n}\n\n/**\n * For testing: reset the cached delay so detection runs again\n * @internal\n */\nexport function __resetCachedDelay(): void {\n cachedDelay = null\n}\n\n/**\n * For testing: override the detected delay\n * @internal\n */\nexport function __setOverrideDelay(delay: number | null): void {\n cachedDelay = delay\n}\n","import type { QueryValue } from '../types'\n\nexport function normalizeQueryValue(value: unknown): QueryValue {\n if (value === undefined || value === null) {\n return null\n }\n\n if (Array.isArray(value)) {\n return (value.filter((item): item is string => typeof item === 'string'))\n }\n\n return typeof value === 'string' ? value : null\n}\n\nexport function buildQueryString(query: Record<string, string | string[] | undefined>): string {\n const params = new URLSearchParams()\n\n for (const [key, value] of Object.entries(query)) {\n if (typeof value === 'string') {\n params.append(key, value)\n continue\n }\n\n if (Array.isArray(value)) {\n for (const entry of value) {\n if (typeof entry === 'string') {\n params.append(key, entry)\n }\n }\n }\n }\n\n return params.toString()\n}\n\nexport function cloneValue<T>(value: T): T {\n if (Array.isArray(value)) {\n return value.slice() as unknown as T\n }\n\n if (value instanceof Date) {\n return new Date(value) as unknown as T\n }\n\n if (value && typeof value === 'object') {\n return { ...(value as Record<string, unknown>) } as T\n }\n\n return value\n}\n","import { onBeforeUnmount, reactive, watch } from 'vue'\nimport type { UnwrapRef } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport type { Parser, QueryStateOptions, StateRecord, StateRecordUpdate } from './types'\nimport { getSafeBrowserDelay } from './internal/rate-limiter'\nimport { buildQueryString, cloneValue, normalizeQueryValue } from './internal/utils'\n\n/**\n * Composable for syncing multiple related query parameters with component state\n * Provides automatic batching - multiple setState calls within the same event loop tick\n * result in a single router.push()\n *\n * @param parsers - Mapping of query parameter names to their parsers\n * @param options - Configuration options (applies to all parameters)\n * @returns Tuple of [state, setState] where state is a reactive object\n *\n * @example\n * ```typescript\n * const [filters, setFilters] = useQueryStates({\n * search: parseAsString.withDefault(''),\n * status: parseAsStringLiteral(['active', 'inactive']).withDefault('active'),\n * limit: parseAsInteger.withDefault(20)\n * })\n *\n * // Single updates\n * setFilters({ search: 'vue' })\n *\n * // Batch updates (single URL push)\n * setFilters({\n * search: 'vue',\n * status: 'active',\n * limit: 50\n * })\n *\n * // Clear all\n * setFilters(null)\n * ```\n */\nexport function useQueryStates<TParsers extends Record<string, Parser<any>>>(\n parsers: TParsers,\n options: QueryStateOptions = {}\n): [state: UnwrapRef<StateRecord<TParsers>>, setState: (values: StateRecordUpdate<TParsers> | null) => void] {\n const route = useRoute()\n const router = useRouter()\n\n const keys = Object.keys(parsers) as Array<keyof TParsers>\n\n function findParserOption<K extends keyof QueryStateOptions>(option: K): QueryStateOptions[K] | undefined {\n for (const key of keys) {\n const parserOption = parsers[key].options?.[option]\n if (parserOption !== undefined) {\n return parserOption\n }\n }\n return undefined\n }\n\n // Configuration\n const historyMode = options.history ?? findParserOption('history') ?? 'replace'\n const shallowRouting = options.shallow ?? findParserOption('shallow') ?? true\n const shouldScroll = options.scroll ?? findParserOption('scroll') ?? false\n const globalClearOnDefault = options.clearOnDefault ?? true\n\n const browserSafeDelay = getSafeBrowserDelay()\n const parserDebounce = findParserOption('debounce')\n const parserThrottle = findParserOption('throttle')\n const customDebounce = options.debounce ?? (parserDebounce ?? null)\n const customThrottle = options.throttle ?? (parserThrottle ?? null)\n const resolvedDebounce = customDebounce !== null\n ? Math.max(customDebounce, browserSafeDelay)\n : null\n const resolvedThrottle = resolvedDebounce === null && customThrottle !== null\n ? Math.max(customThrottle, browserSafeDelay)\n : null\n const fallbackDebounce = resolvedDebounce ?? browserSafeDelay\n\n // State - initialize all fields\n const state = reactive<Record<string, any>>({}) as StateRecord<TParsers>\n\n for (const key of keys) {\n state[key] = getStateFromQueryValue(key, route.query[key as string])\n }\n\n // Batching state\n let updateTimeout: ReturnType<typeof setTimeout> | null = null\n let batchMicrotaskId: ReturnType<typeof queueMicrotask> | null = null\n let isNavigating = false\n let lastThrottleTimestamp = 0\n\n function shouldClearKey(key: keyof TParsers): boolean {\n const parserOption = parsers[key].options?.clearOnDefault\n return parserOption ?? globalClearOnDefault\n }\n\n function resolveDefaultValue(key: keyof TParsers): any {\n const parser = parsers[key]\n if (parser.default !== undefined) {\n return cloneValue(parser.default)\n }\n return null\n }\n\n function normalizeValue(key: keyof TParsers, value: any): any {\n if (value === undefined || value === null) {\n return resolveDefaultValue(key)\n }\n return value\n }\n\n function getStateFromQueryValue(key: keyof TParsers, rawValue: unknown): any {\n const normalized = normalizeQueryValue(rawValue)\n const parser = parsers[key]\n const parsed = parser.parse(normalized)\n return normalizeValue(key, parsed ?? null)\n }\n\n /**\n * Check if value equals its parser's default\n */\n function equalsDefault(key: keyof TParsers, value: any): boolean {\n const parser = parsers[key]\n if (parser.default === undefined) {\n return value === null\n }\n if (parser.eq) {\n return parser.eq(value, parser.default)\n }\n return value === parser.default\n }\n\n /**\n * Update URL with current state\n * Called after debounce/throttle delay\n */\n async function updateUrl(): Promise<void> {\n updateTimeout = null\n\n // Don't update if we're in an external navigation\n if (isNavigating) {\n return\n }\n\n const newQuery: Record<string, string | string[] | undefined> = {}\n\n for (const [queryKey, queryValue] of Object.entries(route.query)) {\n if (queryValue === null) {\n continue\n }\n newQuery[queryKey] = queryValue as string | string[]\n }\n\n // Process each key\n for (const key of keys) {\n const parser = parsers[key]\n const serialized = parser.serialize(state[key] === null ? null : state[key])\n\n // Determine if we should remove the parameter\n if (serialized === null || (shouldClearKey(key) && equalsDefault(key, state[key]))) {\n delete newQuery[key as string]\n } else {\n newQuery[key as string] = serialized\n }\n }\n\n // Check URL length\n const urlString = buildQueryString(newQuery)\n\n if (urlString.length > 2000) {\n console.warn(\n `URL length (${urlString.length}) exceeds recommended 2000 characters. ` +\n `Consider simplifying or moving state to session storage.`\n )\n }\n\n // Update router\n const navigationTarget = shallowRouting\n ? { query: newQuery }\n : { path: route.path, hash: route.hash, query: newQuery }\n\n await router[historyMode](navigationTarget)\n\n if (shouldScroll && typeof window !== 'undefined' && typeof window.scrollTo === 'function') {\n window.scrollTo({ top: 0, left: 0, behavior: 'auto' })\n }\n }\n\n /**\n * Queue a URL update with debounce\n */\n function queueUrlUpdate(): void {\n if (resolvedThrottle !== null) {\n const now = Date.now()\n const remaining = resolvedThrottle - (now - lastThrottleTimestamp)\n\n if (remaining <= 0) {\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n updateTimeout = null\n }\n lastThrottleTimestamp = now\n void updateUrl()\n return\n }\n\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n }\n updateTimeout = setTimeout(() => {\n lastThrottleTimestamp = Date.now()\n updateTimeout = null\n void updateUrl()\n }, remaining)\n return\n }\n\n const delay = resolvedDebounce ?? fallbackDebounce\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n }\n updateTimeout = setTimeout(() => {\n updateTimeout = null\n void updateUrl()\n }, delay)\n }\n\n /**\n * Queue URL update batching for the same event loop tick\n */\n function queueBatchUrlUpdate(): void {\n // If already queued, don't queue again\n if (batchMicrotaskId !== null) {\n return\n }\n\n // Queue a microtask to batch URL updates\n batchMicrotaskId = queueMicrotask(() => {\n batchMicrotaskId = null\n queueUrlUpdate()\n })\n }\n\n /**\n * Set state for one or more parameters\n * Updates state immediately, batches URL updates\n */\n function setState(values: StateRecordUpdate<TParsers> | null): void {\n let hasChanges = false\n\n if (values === null) {\n // Clear all to defaults\n for (const key of keys) {\n const parser = parsers[key]\n const newValue = resolveDefaultValue(key)\n const hasChanged = parser.eq ? !parser.eq(newValue, state[key]) : newValue !== state[key]\n\n if (hasChanged) {\n state[key] = newValue\n hasChanges = true\n }\n }\n } else {\n // Update each provided key\n for (const key of Object.keys(values) as Array<keyof TParsers>) {\n const providedValue = values[key]\n if (providedValue === undefined) {\n continue\n }\n\n const parser = parsers[key]\n const newValue = normalizeValue(key, providedValue)\n const hasChanged = parser.eq ? !parser.eq(newValue, state[key]) : newValue !== state[key]\n\n if (hasChanged) {\n state[key] = newValue\n hasChanges = true\n }\n }\n }\n\n // Batch URL updates\n if (hasChanges) {\n queueBatchUrlUpdate()\n }\n }\n\n /**\n * Watch for external URL changes (back button, direct navigation)\n */\n const watchers = keys.map((key) => {\n return watch(\n () => route.query[key as string],\n (newValue) => {\n // Mark that we're navigating to prevent URL update race condition\n isNavigating = true\n\n // Cancel any pending URL update\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n updateTimeout = null\n }\n\n // Parse and update state from new URL\n state[key] = getStateFromQueryValue(key, newValue)\n\n // Re-enable URL updates after next tick\n setTimeout(() => {\n isNavigating = false\n }, 0)\n }\n )\n })\n\n // Cleanup on unmount\n onBeforeUnmount(() => {\n watchers.forEach((unwatch) => unwatch())\n if (updateTimeout) {\n clearTimeout(updateTimeout)\n }\n })\n\n return [state as UnwrapRef<StateRecord<TParsers>>, setState]\n}\n","import type { BuildableParser, Parser, QueryStateOptions, QueryValue } from './types'\n\nfunction getSingleValue(value: QueryValue): string | null {\n if (value === null) {\n return null\n }\n if (Array.isArray(value)) {\n return value[0] ?? null\n }\n return value\n}\n\nfunction getArrayValues(value: QueryValue): string[] {\n if (value === null) {\n return []\n }\n if (Array.isArray(value)) {\n return value.filter((item): item is string => typeof item === 'string')\n }\n return value.length === 0 ? [] : [value]\n}\n\n/**\n * Creates a parser from a base parser with optional default value and options\n */\nfunction createBuildable<T>(base: Parser<T>): BuildableParser<T> {\n const buildable: BuildableParser<T> = {\n ...base,\n withDefault<D extends T>(defaultValue: D): BuildableParser<T> & { default: D } {\n return createBuildable({\n ...base,\n default: defaultValue\n }) as BuildableParser<T> & { default: D }\n },\n withOptions(options: Partial<QueryStateOptions>): BuildableParser<T> {\n return createBuildable({\n ...base,\n options: {\n ...(base.options ?? {}),\n ...options\n }\n })\n }\n }\n return buildable\n}\n\n/**\n * Parse raw string values\n * Returns null for null/undefined input\n *\n * @example\n * ```typescript\n * const [search] = useQueryState('q', parseAsString)\n * const [search] = useQueryState('q', parseAsString.withDefault(''))\n * ```\n */\nexport const parseAsString: BuildableParser<string> = createBuildable({\n parse: (value) => getSingleValue(value),\n serialize: (value) => (value ?? null)\n})\n\n/**\n * Parse integer values\n * Returns null for non-numeric input or NaN\n *\n * @example\n * ```typescript\n * const [count] = useQueryState('count', parseAsInteger)\n * const [count] = useQueryState('count', parseAsInteger.withDefault(0))\n * ```\n */\nexport const parseAsInteger: BuildableParser<number> = createBuildable({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n const normalized = rawValue.trim()\n if (!/^-?\\d+$/.test(normalized)) {\n return null\n }\n const num = parseInt(normalized, 10)\n return Number.isNaN(num) ? null : num\n },\n serialize: (value) => {\n if (value === null || value === undefined) return null\n return String(Math.floor(value))\n }\n})\n\n/**\n * Parse floating point values\n * Returns null for non-numeric input or NaN/Infinity\n *\n * @example\n * ```typescript\n * const [price] = useQueryState('price', parseAsFloat)\n * const [price] = useQueryState('price', parseAsFloat.withDefault(0.0))\n * ```\n */\nexport const parseAsFloat: BuildableParser<number> = createBuildable({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n const normalized = rawValue.trim()\n if (!/^[-+]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)$/.test(normalized)) {\n return null\n }\n const num = parseFloat(normalized)\n return Number.isFinite(num) ? num : null\n },\n serialize: (value) => {\n if (value === null || value === undefined) return null\n return String(value)\n }\n})\n\n/**\n * Parse boolean values\n * Accepts 'true'/'false' strings (case-insensitive)\n * Returns null for invalid input\n *\n * @example\n * ```typescript\n * const [isActive] = useQueryState('active', parseAsBoolean)\n * const [isActive] = useQueryState('active', parseAsBoolean.withDefault(false))\n * ```\n */\nexport const parseAsBoolean: BuildableParser<boolean> = createBuildable({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n const lower = rawValue.toLowerCase()\n if (lower === 'true') return true\n if (lower === 'false') return false\n return null\n },\n serialize: (value) => {\n if (value === null || value === undefined) return null\n return String(value)\n }\n})\n\n/**\n * Parse string literal (enum-like) values\n * Provides type-safe string enum handling with compile-time validation\n * Returns null for values not in the allowed set\n *\n * @example\n * ```typescript\n * const [sort] = useQueryState(\n * 'sort',\n * parseAsStringLiteral(['asc', 'desc']).withDefault('asc')\n * )\n * // Type is now Ref<'asc' | 'desc'>, not string\n * ```\n */\nexport function parseAsStringLiteral<const T extends readonly string[]>(\n values: T\n): BuildableParser<T[number]> {\n const valueSet = new Set(values)\n\n return createBuildable<T[number]>({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n return valueSet.has(rawValue) ? (rawValue as T[number]) : null\n },\n serialize: (value) => {\n if (value === null || value === undefined) return null\n return String(value)\n }\n })\n}\n\n/**\n * Parse number literal (enum-like) values\n * Provides type-safe number enum handling with compile-time validation\n * Returns null for values not in the allowed set\n *\n * @example\n * ```typescript\n * const [limit] = useQueryState(\n * 'limit',\n * parseAsNumberLiteral([10, 20, 50]).withDefault(20)\n * )\n * // Type is now Ref<10 | 20 | 50>, not number\n * ```\n */\nexport function parseAsNumberLiteral<const T extends readonly number[]>(\n values: T\n): BuildableParser<T[number]> {\n const valueSet = new Set(values)\n\n return createBuildable<T[number]>({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n const num = parseInt(rawValue, 10)\n return !isNaN(num) && valueSet.has(num) ? (num as T[number]) : null\n },\n serialize: (value) => {\n if (value === null || value === undefined) return null\n return String(Math.floor(value))\n }\n })\n}\n\n/**\n * Parse string enums into typed values\n */\nexport function parseAsStringEnum<TEnum extends Record<string, string | number>>(\n enumObject: TEnum\n): BuildableParser<TEnum[keyof TEnum]> {\n const enumValues = new Set(\n Object.values(enumObject).filter((entry): entry is TEnum[keyof TEnum] => typeof entry === 'string')\n )\n\n return createBuildable<TEnum[keyof TEnum]>({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n return enumValues.has(rawValue as TEnum[keyof TEnum]) ? (rawValue as TEnum[keyof TEnum]) : null\n },\n serialize: (value) => {\n if (value === null || value === undefined) return null\n return String(value)\n }\n })\n}\n\n/**\n * Parse comma-separated or custom-separated values into arrays\n * Each item is parsed using the provided itemParser\n * Returns null for null/undefined input\n *\n * @example\n * ```typescript\n * const [ids] = useQueryState('ids', parseAsArrayOf(parseAsInteger))\n * // ?ids=1,2,3 → [1, 2, 3]\n *\n * const [tags] = useQueryState('tags', parseAsArrayOf(parseAsString, '|'))\n * // ?tags=a|b|c → ['a', 'b', 'c']\n * ```\n */\nexport function parseAsArrayOf<T>(\n itemParser: Parser<T>,\n separator: string = ','\n): BuildableParser<T[]> {\n return createBuildable<T[]>({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return []\n return rawValue\n .split(separator)\n .map((item) => itemParser.parse(item.trim()))\n .filter((item): item is T => item !== null)\n },\n serialize: (value) => {\n if (!value || value.length === 0) return null\n const serialized = value\n .map((item) => itemParser.serialize(item))\n .flatMap((result) => {\n if (result === null) {\n return []\n }\n return Array.isArray(result) ? result : [result]\n })\n .filter((s): s is string => typeof s === 'string')\n return serialized.length > 0 ? serialized.join(separator) : null\n },\n eq: (a, b) => {\n if (!a && !b) return true\n if (!a || !b) return false\n if (a.length !== b.length) return false\n return a.every((v, i) => {\n const itemEq = itemParser.eq\n return itemEq ? itemEq(v, b[i]) : v === b[i]\n })\n },\n default: [] as unknown as T[]\n })\n}\n\n/**\n * Parse repeated query parameters (native arrays) into typed arrays\n */\nexport function parseAsNativeArrayOf<T>(itemParser: Parser<T>): BuildableParser<T[]> {\n return createBuildable<T[]>({\n parse: (value) => {\n const inputs = getArrayValues(value)\n if (inputs.length === 0) return []\n return inputs\n .map((entry) => itemParser.parse(entry))\n .filter((item): item is T => item !== null)\n },\n serialize: (value) => {\n if (!value || value.length === 0) return null\n const serialized = value\n .map((item) => itemParser.serialize(item))\n .flatMap((result) => {\n if (result === null) {\n return []\n }\n return Array.isArray(result) ? result : [result]\n })\n .filter((entry): entry is string => typeof entry === 'string')\n return serialized.length > 0 ? serialized : null\n },\n eq: (a, b) => {\n if (!a && !b) return true\n if (!a || !b) return false\n if (a.length !== b.length) return false\n return a.every((v, i) => {\n const itemEq = itemParser.eq\n return itemEq ? itemEq(v, b[i]) : v === b[i]\n })\n },\n default: [] as unknown as T[]\n })\n}\n\n/**\n * Parse ISO 8601 date strings (YYYY-MM-DD)\n * Returns null for invalid date strings\n *\n * @example\n * ```typescript\n * const [date] = useQueryState('date', parseAsIsoDate)\n * // ?date=2025-11-22 → Date(2025-11-22T00:00:00Z)\n * ```\n */\nexport const parseAsIsoDate: BuildableParser<Date> = createBuildable({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n // Match ISO date format: YYYY-MM-DD\n const dateMatch = /^(\\d{4})-(\\d{2})-(\\d{2})$/.test(rawValue)\n if (!dateMatch) return null\n const date = new Date(rawValue + 'T00:00:00Z')\n return isNaN(date.getTime()) ? null : date\n },\n serialize: (value) => {\n if (!value) return null\n // Use toISOString but extract just the date part\n const iso = value.toISOString()\n return iso.split('T')[0]\n },\n eq: (a, b) => {\n if (!a && !b) return true\n if (!a || !b) return false\n return a.getTime() === b.getTime()\n }\n})\n\n/**\n * Parse ISO 8601 datetime strings with timezone\n * Supports formats: 2025-11-22T10:30:00Z and 2025-11-22T10:30:00+01:00\n * Returns null for invalid datetime strings\n *\n * @example\n * ```typescript\n * const [datetime] = useQueryState('created', parseAsIsoDateTime)\n * // ?created=2025-11-22T10:30:00Z → Date(2025-11-22T10:30:00Z)\n * ```\n */\nexport const parseAsIsoDateTime: BuildableParser<Date> = createBuildable({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n // Match ISO datetime with optional milliseconds and timezone: YYYY-MM-DDTHH:MM:SS(.sss)?(Z or ±HH:MM)\n const dateTimeMatch =\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$/.test(rawValue)\n if (!dateTimeMatch) return null\n const date = new Date(rawValue)\n return isNaN(date.getTime()) ? null : date\n },\n serialize: (value) => {\n if (!value) return null\n return value.toISOString()\n },\n eq: (a, b) => {\n if (!a && !b) return true\n if (!a || !b) return false\n return a.getTime() === b.getTime()\n }\n})\n\n/**\n * Parse JSON values with optional Zod schema validation\n * Returns null for invalid JSON or validation failures\n *\n * @example\n * ```typescript\n * const [filters] = useQueryState('f', parseAsJson())\n * // ?f={\"status\":\"active\"} → {status: \"active\"}\n *\n * // With Zod validation:\n * const filterSchema = z.object({ status: z.enum(['active', 'inactive']) })\n * const [filters] = useQueryState('f', parseAsJson(filterSchema))\n * ```\n */\nexport function parseAsJson<T = unknown>(schema?: any): BuildableParser<T> {\n return createBuildable<T>({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n try {\n const parsed = JSON.parse(rawValue)\n // If schema provided, validate it\n if (schema) {\n const result = schema.safeParse ? schema.safeParse(parsed) : schema.parse?.(parsed)\n if (result && !result.success) return null\n return result?.data ?? parsed\n }\n return parsed\n } catch {\n return null\n }\n },\n serialize: (value) => {\n if (value === null || value === undefined) return null\n try {\n return JSON.stringify(value)\n } catch {\n return null\n }\n }\n })\n}\n\n/**\n * Parse hex color values (6 digit hex codes)\n * Accepts both with and without # prefix\n * Returns null for invalid hex values\n *\n * @example\n * ```typescript\n * const [color] = useQueryState('color', parseAsHex)\n * // ?color=ff00ff → 'ff00ff'\n * // ?color=%23ff00ff → 'ff00ff' (URL encoded #)\n * ```\n */\nexport const parseAsHex: BuildableParser<string> = createBuildable({\n parse: (value) => {\n const rawValue = getSingleValue(value)\n if (!rawValue) return null\n // Remove # if present\n const hex = rawValue.startsWith('#') ? rawValue.slice(1) : rawValue\n // Validate hex format (6 characters)\n if (!/^[0-9a-fA-F]{6}$/.test(hex)) return null\n return hex.toLowerCase()\n },\n serialize: (value) => {\n if (!value) return null\n return value\n }\n})\n\nexport function withDefault<T, D extends T>(parser: Parser<T>, defaultValue: D): BuildableParser<T> & { default: D } {\n return createBuildable({\n ...parser,\n default: defaultValue\n }) as BuildableParser<T> & { default: D }\n}\n"],"mappings":"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","names":["onBeforeUnmount","watch","useRoute","useRouter","useRoute","useRouter","watch","onBeforeUnmount"]}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Browser-aware rate limiting for URL updates
3
+ *
4
+ * Different browsers have different performance characteristics:
5
+ * - Safari: Limited to ~120ms between updates without crashes
6
+ * - Chrome/Firefox/Edge: Can handle ~50ms between updates
7
+ *
8
+ * This detection happens once at module load and is cached
9
+ */
10
+ /**
11
+ * Get the safe browser delay, cached after first call
12
+ * @returns Minimum delay in milliseconds
13
+ */
14
+ export declare function getSafeBrowserDelay(): number;
15
+ /**
16
+ * For testing: reset the cached delay so detection runs again
17
+ * @internal
18
+ */
19
+ export declare function __resetCachedDelay(): void;
20
+ /**
21
+ * For testing: override the detected delay
22
+ * @internal
23
+ */
24
+ export declare function __setOverrideDelay(delay: number | null): void;
25
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/internal/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAK5C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE7D"}
@@ -0,0 +1,5 @@
1
+ import type { QueryValue } from '../types';
2
+ export declare function normalizeQueryValue(value: unknown): QueryValue;
3
+ export declare function buildQueryString(query: Record<string, string | string[] | undefined>): string;
4
+ export declare function cloneValue<T>(value: T): T;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAU9D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,MAAM,CAmB7F;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAczC"}
@@ -0,0 +1,154 @@
1
+ import type { BuildableParser, Parser } from './types';
2
+ /**
3
+ * Parse raw string values
4
+ * Returns null for null/undefined input
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * const [search] = useQueryState('q', parseAsString)
9
+ * const [search] = useQueryState('q', parseAsString.withDefault(''))
10
+ * ```
11
+ */
12
+ export declare const parseAsString: BuildableParser<string>;
13
+ /**
14
+ * Parse integer values
15
+ * Returns null for non-numeric input or NaN
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const [count] = useQueryState('count', parseAsInteger)
20
+ * const [count] = useQueryState('count', parseAsInteger.withDefault(0))
21
+ * ```
22
+ */
23
+ export declare const parseAsInteger: BuildableParser<number>;
24
+ /**
25
+ * Parse floating point values
26
+ * Returns null for non-numeric input or NaN/Infinity
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const [price] = useQueryState('price', parseAsFloat)
31
+ * const [price] = useQueryState('price', parseAsFloat.withDefault(0.0))
32
+ * ```
33
+ */
34
+ export declare const parseAsFloat: BuildableParser<number>;
35
+ /**
36
+ * Parse boolean values
37
+ * Accepts 'true'/'false' strings (case-insensitive)
38
+ * Returns null for invalid input
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const [isActive] = useQueryState('active', parseAsBoolean)
43
+ * const [isActive] = useQueryState('active', parseAsBoolean.withDefault(false))
44
+ * ```
45
+ */
46
+ export declare const parseAsBoolean: BuildableParser<boolean>;
47
+ /**
48
+ * Parse string literal (enum-like) values
49
+ * Provides type-safe string enum handling with compile-time validation
50
+ * Returns null for values not in the allowed set
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const [sort] = useQueryState(
55
+ * 'sort',
56
+ * parseAsStringLiteral(['asc', 'desc']).withDefault('asc')
57
+ * )
58
+ * // Type is now Ref<'asc' | 'desc'>, not string
59
+ * ```
60
+ */
61
+ export declare function parseAsStringLiteral<const T extends readonly string[]>(values: T): BuildableParser<T[number]>;
62
+ /**
63
+ * Parse number literal (enum-like) values
64
+ * Provides type-safe number enum handling with compile-time validation
65
+ * Returns null for values not in the allowed set
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * const [limit] = useQueryState(
70
+ * 'limit',
71
+ * parseAsNumberLiteral([10, 20, 50]).withDefault(20)
72
+ * )
73
+ * // Type is now Ref<10 | 20 | 50>, not number
74
+ * ```
75
+ */
76
+ export declare function parseAsNumberLiteral<const T extends readonly number[]>(values: T): BuildableParser<T[number]>;
77
+ /**
78
+ * Parse string enums into typed values
79
+ */
80
+ export declare function parseAsStringEnum<TEnum extends Record<string, string | number>>(enumObject: TEnum): BuildableParser<TEnum[keyof TEnum]>;
81
+ /**
82
+ * Parse comma-separated or custom-separated values into arrays
83
+ * Each item is parsed using the provided itemParser
84
+ * Returns null for null/undefined input
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const [ids] = useQueryState('ids', parseAsArrayOf(parseAsInteger))
89
+ * // ?ids=1,2,3 → [1, 2, 3]
90
+ *
91
+ * const [tags] = useQueryState('tags', parseAsArrayOf(parseAsString, '|'))
92
+ * // ?tags=a|b|c → ['a', 'b', 'c']
93
+ * ```
94
+ */
95
+ export declare function parseAsArrayOf<T>(itemParser: Parser<T>, separator?: string): BuildableParser<T[]>;
96
+ /**
97
+ * Parse repeated query parameters (native arrays) into typed arrays
98
+ */
99
+ export declare function parseAsNativeArrayOf<T>(itemParser: Parser<T>): BuildableParser<T[]>;
100
+ /**
101
+ * Parse ISO 8601 date strings (YYYY-MM-DD)
102
+ * Returns null for invalid date strings
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const [date] = useQueryState('date', parseAsIsoDate)
107
+ * // ?date=2025-11-22 → Date(2025-11-22T00:00:00Z)
108
+ * ```
109
+ */
110
+ export declare const parseAsIsoDate: BuildableParser<Date>;
111
+ /**
112
+ * Parse ISO 8601 datetime strings with timezone
113
+ * Supports formats: 2025-11-22T10:30:00Z and 2025-11-22T10:30:00+01:00
114
+ * Returns null for invalid datetime strings
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const [datetime] = useQueryState('created', parseAsIsoDateTime)
119
+ * // ?created=2025-11-22T10:30:00Z → Date(2025-11-22T10:30:00Z)
120
+ * ```
121
+ */
122
+ export declare const parseAsIsoDateTime: BuildableParser<Date>;
123
+ /**
124
+ * Parse JSON values with optional Zod schema validation
125
+ * Returns null for invalid JSON or validation failures
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * const [filters] = useQueryState('f', parseAsJson())
130
+ * // ?f={"status":"active"} → {status: "active"}
131
+ *
132
+ * // With Zod validation:
133
+ * const filterSchema = z.object({ status: z.enum(['active', 'inactive']) })
134
+ * const [filters] = useQueryState('f', parseAsJson(filterSchema))
135
+ * ```
136
+ */
137
+ export declare function parseAsJson<T = unknown>(schema?: any): BuildableParser<T>;
138
+ /**
139
+ * Parse hex color values (6 digit hex codes)
140
+ * Accepts both with and without # prefix
141
+ * Returns null for invalid hex values
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * const [color] = useQueryState('color', parseAsHex)
146
+ * // ?color=ff00ff → 'ff00ff'
147
+ * // ?color=%23ff00ff → 'ff00ff' (URL encoded #)
148
+ * ```
149
+ */
150
+ export declare const parseAsHex: BuildableParser<string>;
151
+ export declare function withDefault<T, D extends T>(parser: Parser<T>, defaultValue: D): BuildableParser<T> & {
152
+ default: D;
153
+ };
154
+ //# sourceMappingURL=parsers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsers.d.ts","sourceRoot":"","sources":["../src/parsers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAiC,MAAM,SAAS,CAAA;AA+CrF;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,EAAE,eAAe,CAAC,MAAM,CAGhD,CAAA;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,eAAe,CAAC,MAAM,CAejD,CAAA;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,EAAE,eAAe,CAAC,MAAM,CAe/C,CAAA;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,EAAE,eAAe,CAAC,OAAO,CAalD,CAAA;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,MAAM,EAAE,EACpE,MAAM,EAAE,CAAC,GACR,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAc5B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,MAAM,EAAE,EACpE,MAAM,EAAE,CAAC,GACR,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAe5B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAC7E,UAAU,EAAE,KAAK,GAChB,eAAe,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAgBrC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,EACrB,SAAS,GAAE,MAAY,GACtB,eAAe,CAAC,CAAC,EAAE,CAAC,CAkCtB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,CAiCnF;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,EAAE,eAAe,CAAC,IAAI,CAqB/C,CAAA;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,eAAe,CAAC,IAAI,CAoBnD,CAAA;AAEF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,CA2BzE;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,UAAU,EAAE,eAAe,CAAC,MAAM,CAc7C,CAAA;AAEF,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAKnH"}
@@ -0,0 +1,135 @@
1
+ import type { Ref } from 'vue';
2
+ export type QueryValue = string | string[] | null;
3
+ export type SerializedQueryValue = string | string[] | null;
4
+ /**
5
+ * A parser that converts between URL string values and typed values
6
+ */
7
+ export interface Parser<T> {
8
+ /**
9
+ * Parse a URL query string value to a typed value
10
+ * Returns null for invalid input (never throws)
11
+ */
12
+ parse(value: QueryValue): T | null;
13
+ /**
14
+ * Serialize a typed value back to a URL query string
15
+ * Returns null to remove the parameter from URL
16
+ */
17
+ serialize(value: T | null): SerializedQueryValue;
18
+ /**
19
+ * Default value when URL parameter is missing or invalid
20
+ */
21
+ default?: T;
22
+ /**
23
+ * Custom equality check for types that use reference equality
24
+ * Prevents infinite update loops for arrays/objects
25
+ */
26
+ eq?(a: T | null, b: T | null): boolean;
27
+ /**
28
+ * Optional parser-level options that act as defaults
29
+ */
30
+ options?: Partial<QueryStateOptions>;
31
+ }
32
+ /**
33
+ * Options for useQueryState behavior
34
+ */
35
+ export interface QueryStateOptions {
36
+ /**
37
+ * Browser history mode: 'push' adds new entry, 'replace' overwrites current
38
+ * @default 'replace'
39
+ */
40
+ history?: 'push' | 'replace';
41
+ /**
42
+ * Milliseconds to debounce URL updates from state changes
43
+ * If undefined, auto-detects based on browser (Safari: 120ms, others: 50ms)
44
+ * @default auto
45
+ */
46
+ debounce?: number;
47
+ /**
48
+ * Milliseconds to throttle URL updates from state changes
49
+ * Only used if debounce is not set
50
+ * @default auto
51
+ */
52
+ throttle?: number;
53
+ /**
54
+ * Remove parameter from URL when value equals default
55
+ * @default true
56
+ */
57
+ clearOnDefault?: boolean;
58
+ /**
59
+ * SPA mode (true) vs SSR mode (false)
60
+ * In SPA mode, doesn't scroll on navigation
61
+ * @default true
62
+ */
63
+ shallow?: boolean;
64
+ /**
65
+ * Whether to scroll to top when URL changes
66
+ * @default false
67
+ */
68
+ scroll?: boolean;
69
+ }
70
+ export type ParserWithDefault<T> = Parser<T> & {
71
+ default: T;
72
+ };
73
+ type InferParserValue<P> = P extends Parser<infer T> ? T : never;
74
+ export type StateValue<P extends Parser<any>> = P extends ParserWithDefault<infer T> ? T : InferParserValue<P> | null;
75
+ export type StateSetter<P extends Parser<any>> = P extends ParserWithDefault<infer T> ? T | null | ((prev: T) => T | null) : InferParserValue<P> | null | ((prev: InferParserValue<P> | null) => InferParserValue<P> | null);
76
+ export type StateRecord<TParsers extends Record<string, Parser<any>>> = {
77
+ [K in keyof TParsers]: StateValue<TParsers[K]>;
78
+ };
79
+ export type StateRecordUpdate<TParsers extends Record<string, Parser<any>>> = Partial<{
80
+ [K in keyof TParsers]: TParsers[K] extends ParserWithDefault<infer T> ? T | null : InferParserValue<TParsers[K]> | null;
81
+ }>;
82
+ /**
83
+ * Stateful hook for syncing a single query parameter with component state
84
+ * Returns a tuple of [state, setState] like React's useState
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * const [search] = useQueryState('q', parseAsString)
89
+ * const [count] = useQueryState('count', parseAsInteger.withDefault(0))
90
+ * ```
91
+ */
92
+ export type UseQueryState = <P extends Parser<any>>(key: string, parser: P, options?: QueryStateOptions) => [state: Ref<StateValue<P>>, setState: (value: StateSetter<P>) => void];
93
+ /**
94
+ * Stateful hook for syncing multiple related query parameters with component state
95
+ * Automatically batches multiple setState calls within the same event loop tick
96
+ * into a single router.push() call
97
+ *
98
+ * Returns a tuple of [state, setState] where state is a reactive object
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const [filters, setFilters] = useQueryStates({
103
+ * search: parseAsString.withDefault(''),
104
+ * status: parseAsStringLiteral(['active', 'inactive']).withDefault('active')
105
+ * })
106
+ *
107
+ * // Single update
108
+ * setFilters({ search: 'vue' })
109
+ *
110
+ * // Batch updates (single URL push)
111
+ * setFilters({ search: 'vue', status: 'inactive' })
112
+ *
113
+ * // Clear all to defaults
114
+ * setFilters(null)
115
+ * ```
116
+ */
117
+ export type UseQueryStates = <TParsers extends Record<string, Parser<any>>>(parsers: TParsers, options?: QueryStateOptions) => [state: StateRecord<TParsers>, setState: (values: StateRecordUpdate<TParsers> | null) => void];
118
+ /**
119
+ * Extended parser with chainable builder methods
120
+ */
121
+ export interface BuildableParser<T> extends Parser<T> {
122
+ /**
123
+ * Set a default value for this parser
124
+ * Also affects TypeScript inference - returns T instead of T | null
125
+ */
126
+ withDefault<D extends T>(defaultValue: D): BuildableParser<T> & {
127
+ default: D;
128
+ };
129
+ /**
130
+ * Set options for this parser
131
+ */
132
+ withOptions(options: Partial<QueryStateOptions>): BuildableParser<T>;
133
+ }
134
+ export {};
135
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Ref } from 'vue';
2
+ import type { Parser, QueryStateOptions, StateSetter, StateValue } from './types';
3
+ /**
4
+ * Composable for syncing a single query parameter with component state
5
+ * Provides bidirectional synchronization between URL and component state
6
+ *
7
+ * @param key - Query parameter name
8
+ * @param parser - Parser for type conversion
9
+ * @param options - Configuration options
10
+ * @returns Tuple of [state, setState]
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const [search] = useQueryState('q', parseAsString)
15
+ * const [count] = useQueryState('count', parseAsInteger.withDefault(0))
16
+ *
17
+ * // State updates immediately
18
+ * count.value = 42
19
+ *
20
+ * // URL updates after debounce (browser-safe)
21
+ * // ?count=42
22
+ * ```
23
+ */
24
+ export declare function useQueryState<P extends Parser<any>>(key: string, parser: P, options?: QueryStateOptions): [state: Ref<StateValue<P>>, setState: (value: StateSetter<P>) => void];
25
+ //# sourceMappingURL=useQueryState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQueryState.d.ts","sourceRoot":"","sources":["../src/useQueryState.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAIjF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,EACjD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,CAAC,EACT,OAAO,GAAE,iBAAsB,GAC9B,CAAC,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CA8MxE"}
@@ -0,0 +1,35 @@
1
+ import type { UnwrapRef } from 'vue';
2
+ import type { Parser, QueryStateOptions, StateRecord, StateRecordUpdate } from './types';
3
+ /**
4
+ * Composable for syncing multiple related query parameters with component state
5
+ * Provides automatic batching - multiple setState calls within the same event loop tick
6
+ * result in a single router.push()
7
+ *
8
+ * @param parsers - Mapping of query parameter names to their parsers
9
+ * @param options - Configuration options (applies to all parameters)
10
+ * @returns Tuple of [state, setState] where state is a reactive object
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const [filters, setFilters] = useQueryStates({
15
+ * search: parseAsString.withDefault(''),
16
+ * status: parseAsStringLiteral(['active', 'inactive']).withDefault('active'),
17
+ * limit: parseAsInteger.withDefault(20)
18
+ * })
19
+ *
20
+ * // Single updates
21
+ * setFilters({ search: 'vue' })
22
+ *
23
+ * // Batch updates (single URL push)
24
+ * setFilters({
25
+ * search: 'vue',
26
+ * status: 'active',
27
+ * limit: 50
28
+ * })
29
+ *
30
+ * // Clear all
31
+ * setFilters(null)
32
+ * ```
33
+ */
34
+ export declare function useQueryStates<TParsers extends Record<string, Parser<any>>>(parsers: TParsers, options?: QueryStateOptions): [state: UnwrapRef<StateRecord<TParsers>>, setState: (values: StateRecordUpdate<TParsers> | null) => void];
35
+ //# sourceMappingURL=useQueryStates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQueryStates.d.ts","sourceRoot":"","sources":["../src/useQueryStates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAA;AAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAIxF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,cAAc,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EACzE,OAAO,EAAE,QAAQ,EACjB,OAAO,GAAE,iBAAsB,GAC9B,CAAC,KAAK,EAAE,SAAS,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC,CAwR3G"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "vuerl",
3
+ "version": "1.0.0",
4
+ "description": "Type-safe URL query state management for Vue 3",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
20
+ "peerDependencies": {
21
+ "vue": "^3.3.0",
22
+ "vue-router": "^4.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@vue/test-utils": "^2.4.1",
26
+ "jsdom": "^23.0.0",
27
+ "tsup": "^8.0.0",
28
+ "typescript": "^5.3.0",
29
+ "vitest": "^1.0.0",
30
+ "vue": "^3.3.0",
31
+ "vue-router": "^4.0.0"
32
+ },
33
+ "keywords": [
34
+ "vue",
35
+ "vue3",
36
+ "url",
37
+ "query",
38
+ "state",
39
+ "router",
40
+ "composable",
41
+ "type-safe",
42
+ "url-state",
43
+ "query-string"
44
+ ],
45
+ "author": "Marius Oseth",
46
+ "license": "MIT",
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/mariusoseth/vuerl.git"
50
+ },
51
+ "homepage": "https://github.com/mariusoseth/vuerl#readme",
52
+ "bugs": {
53
+ "url": "https://github.com/mariusoseth/vuerl/issues"
54
+ },
55
+ "scripts": {
56
+ "build": "tsup && tsc --emitDeclarationOnly --declaration --declarationMap --outDir dist",
57
+ "type-check": "tsc --noEmit",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest",
60
+ "test:ui": "vitest --ui"
61
+ }
62
+ }