UNPKG

vue-qs

Version:

Type‑safe, reactive URL query params for Vue

452 lines (441 loc) 16.3 kB
import { Ref, App } from 'vue'; import { Router } from 'vue-router'; /** * Function that parses a raw query string value into a typed value * @template T The expected output type * @param rawValue The raw string value from the URL query parameter * @returns The parsed typed value */ type QueryParser<T> = (rawValue: string | null) => T; /** * Function that serializes a typed value into a string for the URL query * @template T The input type to serialize * @param typedValue The typed value to serialize * @returns The serialized string value or null if the value should be omitted */ type QuerySerializer<T> = (typedValue: T) => string | null; /** * A codec that combines both parse and serialize functions for a given type * @template T The type this codec handles */ type QueryCodec<T> = { /** Function to parse string values from URL into typed values */ parse: QueryParser<T>; /** Function to serialize typed values back to URL strings */ serialize: QuerySerializer<T>; }; /** * Configuration options for a single query parameter * @template T The type of the parameter value */ type QueryParameterOptions<T> = { /** Default value to use when parameter is missing or invalid */ defaultValue?: T; /** Combined codec with both parse and serialize functions */ codec?: QueryCodec<T>; /** Custom parser function (overrides codec.parse if provided) */ parseFunction?: QueryParser<T>; /** Custom serializer function (overrides codec.serialize if provided) */ serializeFunction?: QuerySerializer<T>; /** Custom equality function to compare values (defaults to Object.is) */ isEqual?: (valueA: T, valueB: T) => boolean; /** Whether to omit the parameter from URL when value equals default (default: true) */ shouldOmitDefault?: boolean; /** Optional batch key for grouping parameter updates */ batchKey?: string; }; /** * Schema defining multiple query parameters with their configurations */ type QueryParameterSchema = Record<string, QueryParameterOptions<any>>; /** * Options for useQueryRef composable * @template T The type of the query parameter value */ type UseQueryRefOptions<T> = QueryParameterOptions<T> & { /** History strategy when updating the URL ('replace' | 'push') */ historyStrategy?: 'replace' | 'push'; /** Optional custom query adapter to use */ queryAdapter?: QueryAdapter; /** Enable two-way synchronization with URL changes */ enableTwoWaySync?: boolean; }; /** * Return type from useQueryRef composable * @template T The type of the query parameter value */ type QueryRefReturn<T> = Ref<T> & { /** Manually sync the current value to the URL */ syncToUrl(): void; }; /** * Reactive state object for useQueryReactive * @template TSchema The parameter schema type */ type ReactiveQueryState<TSchema extends QueryParameterSchema> = { [K in keyof TSchema]: TSchema[K] extends QueryParameterOptions<infer T> ? T : never; }; /** * Options for batch updates in useQueryReactive */ type QueryBatchUpdateOptions = { /** History strategy for the batch update */ historyStrategy?: 'replace' | 'push'; }; /** * Return type from useQueryReactive composable * @template TSchema The parameter schema type */ type QueryReactiveReturn<TSchema extends QueryParameterSchema> = { /** Reactive state object with typed parameter values */ queryState: ReactiveQueryState<TSchema>; /** Update multiple parameters in a single operation */ updateBatch(updates: Partial<ReactiveQueryState<TSchema>>, options?: QueryBatchUpdateOptions): void; /** Manually sync all current values to the URL */ syncAllToUrl(): void; }; /** * Options for useQueryReactive composable */ type UseQueryReactiveOptions = { /** History strategy when updating the URL */ historyStrategy?: 'replace' | 'push'; /** Optional custom query adapter to use */ queryAdapter?: QueryAdapter; /** Enable two-way synchronization with URL changes */ enableTwoWaySync?: boolean; }; /** * Abstraction for reading and writing query parameters */ type QueryAdapter = { /** Read current query parameters as a plain object */ getCurrentQuery(): Record<string, string | undefined>; /** Update query parameters in the URL */ updateQuery(queryUpdates: Record<string, string | undefined>, options?: { historyStrategy?: 'replace' | 'push'; }): void; /** Subscribe to external query changes (returns unsubscribe function) */ onQueryChange?(callback: () => void): () => void; }; /** * Runtime environment information */ type RuntimeEnvironment = { /** Whether we're running in a browser environment */ isBrowser: boolean; /** Window object if available */ windowObject: Window | null; }; /** Environment flags used by the default History API adapter. */ type RuntimeEnv = { isClient: boolean; /** Safe access to window if on client */ win: Window | null; }; /** * Manages a single query parameter as a Vue Ref with URL synchronization * * @template T The type of the parameter value * @param parameterName The name of the URL query parameter * @param options Configuration options for the parameter * @returns Reactive ref that stays in sync with the URL parameter * * @example * ```typescript * import { useQueryRef, numberCodec } from 'vue-qs'; * * // Simple string parameter with default * const searchQuery = useQueryRef('q', { * defaultValue: '', * enableTwoWaySync: true * }); * * // Number parameter with custom codec * const currentPage = useQueryRef('page', { * defaultValue: 1, * codec: numberCodec, * shouldOmitDefault: true * }); * * // Update the URL by changing the ref value * searchQuery.value = 'hello world'; * currentPage.value = 2; * * // Manually sync to URL * searchQuery.syncToUrl(); * ``` */ declare function useQueryRef<T>(parameterName: string, options?: UseQueryRefOptions<T>): QueryRefReturn<T>; /** * Manages multiple query parameters as a single reactive object with URL synchronization * * @template TSchema The schema type defining all parameters * @param parameterSchema Schema defining configuration for each parameter * @param options Global options for the reactive query state * @returns Reactive state object with batch update and sync capabilities * * @example * ```typescript * import { useQueryReactive, numberCodec, booleanCodec } from 'vue-qs'; * * const querySchema = { * search: { * defaultValue: '', * shouldOmitDefault: true * }, * page: { * defaultValue: 1, * codec: numberCodec * }, * showDetails: { * defaultValue: false, * codec: booleanCodec * }, * } as const; * * const { queryState, updateBatch, syncAllToUrl } = useQueryReactive(querySchema, { * enableTwoWaySync: true, * historyStrategy: 'replace' * }); * * // Access reactive values * console.log(queryState.search, queryState.page, queryState.showDetails); * * // Update single values * queryState.search = 'hello'; * queryState.page = 2; * * // Batch update multiple values * updateBatch({ * search: 'world', * page: 1 * }); * * // Manual sync * syncAllToUrl(); * ``` */ declare function useQueryReactive<TSchema extends QueryParameterSchema>(parameterSchema: TSchema, options?: UseQueryReactiveOptions): QueryReactiveReturn<TSchema>; /** * Configuration options for the history adapter */ interface HistoryAdapterOptions { /** Whether to suppress custom history events (default: false) */ suppressHistoryEvents?: boolean; } /** * Result of creating a history-based query adapter */ interface HistoryAdapterResult { /** The query adapter instance */ queryAdapter: QueryAdapter; /** Runtime environment information */ runtimeEnvironment: RuntimeEnvironment; } /** * Creates a query adapter that uses the browser's History API * This adapter is SSR-safe and maintains an in-memory cache on the server * * @param options Configuration options for the adapter * @returns History adapter result with the adapter and runtime info * * @example * ```typescript * import { createHistoryAdapter } from 'vue-qs'; * * const { queryAdapter } = createHistoryAdapter(); * * // Use with the plugin * app.use(createVueQsPlugin({ queryAdapter })); * ``` */ declare function createHistoryAdapter(options?: HistoryAdapterOptions): HistoryAdapterResult; /** * Configuration options for the Vue Router adapter */ interface VueRouterAdapterOptions { /** Whether to log warnings for array query parameters (default: true) */ warnOnArrayParams?: boolean; } /** * Creates a query adapter that integrates with Vue Router * This adapter reads and writes query parameters through Vue Router's API * * @param vueRouter The Vue Router instance to integrate with * @param options Configuration options for the adapter * @returns QueryAdapter that works with Vue Router * * @example * ```typescript * import { createRouter } from 'vue-router'; * import { createVueRouterAdapter } from 'vue-qs'; * * const router = createRouter({ ... }); * const routerAdapter = createVueRouterAdapter(router); * * // Use with the plugin * app.use(createVueQsPlugin({ queryAdapter: routerAdapter })); * ``` */ declare function createVueRouterAdapter(vueRouter: Router, options?: VueRouterAdapterOptions): QueryAdapter; /** * Provides a query adapter to the component tree using dependency injection * This makes the adapter available to all child components * * @param queryAdapter The query adapter instance to provide * * @example * ```typescript * import { provideQueryAdapter, createHistoryAdapter } from 'vue-qs'; * * // In a parent component * const historyAdapter = createHistoryAdapter(); * provideQueryAdapter(historyAdapter); * ``` */ declare function provideQueryAdapter(queryAdapter: QueryAdapter): void; /** * Retrieves the nearest provided query adapter from the component tree * Returns undefined if no adapter has been provided * * @returns The injected query adapter or undefined * * @example * ```typescript * import { useQueryAdapter } from 'vue-qs'; * * // In a child component * const queryAdapter = useQueryAdapter(); * if (queryAdapter) { * // Use the adapter * } * ``` */ declare function useQueryAdapter(): QueryAdapter | undefined; /** * Configuration options for the Vue.js plugin */ interface VueQueryPluginOptions { /** The query adapter to use throughout the application */ queryAdapter: QueryAdapter; } /** * Creates a Vue.js plugin for vue-qs that automatically provides the query adapter * * @param options Plugin configuration options * @returns Vue plugin object * * @example * ```typescript * import { createApp } from 'vue'; * import { createVueQsPlugin, createHistoryAdapter } from 'vue-qs'; * * const app = createApp(); * const historyAdapter = createHistoryAdapter(); * const vueQueryPlugin = createVueQsPlugin({ queryAdapter: historyAdapter }); * * app.use(vueQueryPlugin); * ``` */ declare function createVueQsPlugin(options: VueQueryPluginOptions): { install: (app: App) => void; }; /** * String codec for handling string values * Treats null/undefined as empty string */ declare const stringCodec: QueryCodec<string>; /** * Number codec for handling numeric values * Returns NaN for invalid numbers */ declare const numberCodec: QueryCodec<number>; /** * Boolean codec for handling boolean values * Treats 'true' and '1' as true, everything else as false */ declare const booleanCodec: QueryCodec<boolean>; /** * Date codec using ISO string format * Returns invalid Date for unparseable values */ declare const dateISOCodec: QueryCodec<Date>; /** * JSON codec factory for handling complex objects * Returns null for invalid JSON * @template T The type of object to handle * @returns QueryCodec for the specified type */ declare function createJsonCodec<T>(): QueryCodec<T | null>; /** * Array codec factory for handling arrays with a specific element type * @template T The type of array elements * @param elementCodec Codec for individual array elements * @param delimiter String to use for separating array elements (default: ',') * @returns QueryCodec for arrays of the specified type */ declare function createArrayCodec<T>(elementCodec: QueryCodec<T>, delimiter?: string): QueryCodec<T[]>; /** * Enum codec factory for handling string enum values * Falls back to first enum value for invalid inputs * @template T String literal union type * @param allowedValues Array of allowed enum values * @returns QueryCodec for the enum type */ declare function createEnumCodec<T extends string>(allowedValues: readonly T[]): QueryCodec<T>; type serializers_QueryCodec<T> = QueryCodec<T>; declare const serializers_booleanCodec: typeof booleanCodec; declare const serializers_createArrayCodec: typeof createArrayCodec; declare const serializers_createEnumCodec: typeof createEnumCodec; declare const serializers_createJsonCodec: typeof createJsonCodec; declare const serializers_dateISOCodec: typeof dateISOCodec; declare const serializers_numberCodec: typeof numberCodec; declare const serializers_stringCodec: typeof stringCodec; declare namespace serializers { export { type serializers_QueryCodec as QueryCodec, serializers_booleanCodec as booleanCodec, serializers_createArrayCodec as createArrayCodec, serializers_createEnumCodec as createEnumCodec, serializers_createJsonCodec as createJsonCodec, serializers_dateISOCodec as dateISOCodec, serializers_numberCodec as numberCodec, serializers_stringCodec as stringCodec }; } /** * Safely checks if we're running in a browser environment * @returns true if running in a browser, false otherwise */ declare function isBrowserEnvironment(): boolean; /** * Creates a runtime environment object with safe property access * @returns Runtime environment information */ declare function createRuntimeEnvironment(): RuntimeEnvironment; /** * Safely converts a URL search string to a plain object * @param searchString The URL search string (with or without leading '?') * @returns Object with key-value pairs from the search string */ declare function parseSearchString(searchString: string): Record<string, string>; /** * Safely converts a query object to a URL search string * @param queryObject Object with key-value pairs * @returns URL search string with leading '?' or empty string */ declare function buildSearchString(queryObject: Record<string, string | undefined>): string; /** * Safely compares two values for equality * @param valueA First value to compare * @param valueB Second value to compare * @param customEquals Optional custom equality function * @returns true if values are equal */ declare function areValuesEqual<T>(valueA: T, valueB: T, customEquals?: (a: T, b: T) => boolean): boolean; /** * Safely merges two objects * @param baseObject Base object to merge into * @param updateObject Object with updates to apply * @returns New merged object */ declare function mergeObjects<T extends Record<string, unknown>>(baseObject: T, updateObject: Partial<T>): T; /** * Safely removes undefined values from an object * @param sourceObject Object to clean * @returns New object without undefined values */ declare function removeUndefinedValues<T extends Record<string, unknown>>(sourceObject: T): Partial<T>; export { type HistoryAdapterOptions, type HistoryAdapterResult, type QueryAdapter, type QueryBatchUpdateOptions, type QueryCodec, type QueryParameterOptions, type QueryParameterSchema, type QueryParser, type QueryReactiveReturn, type QueryRefReturn, type QuerySerializer, type ReactiveQueryState, type RuntimeEnv, type RuntimeEnvironment, type UseQueryReactiveOptions, type UseQueryRefOptions, type VueQueryPluginOptions, type VueRouterAdapterOptions, areValuesEqual, booleanCodec, buildSearchString, createArrayCodec, createEnumCodec, createHistoryAdapter, createJsonCodec, createRuntimeEnvironment, createVueQsPlugin, createVueRouterAdapter, dateISOCodec, isBrowserEnvironment, mergeObjects, numberCodec, parseSearchString, provideQueryAdapter, removeUndefinedValues, serializers, stringCodec, useQueryAdapter, useQueryReactive, useQueryRef };