UNPKG

svelte-query-params

Version:

A lightweight, dead-simple, type-safe reactive query parameter store built for Svelte 5.

144 lines (140 loc) 6.31 kB
import { BaseSchema, InferOutput } from 'valibot'; import { z } from 'zod'; interface Adapter { /** * @returns Whether we are in the browser */ isBrowser(): boolean; browser: BrowserAdapter; server: ServerAdapter; } interface BrowserAdapter { /** * A function to retrieve the URL when in the browser */ read: () => URLLike; /** * A function to update the browser query params and hash. * * @param search The search string - includes the `?` prefix if there are query params, otherwise an empty string * @param hash The fragment - includes the `#` prefix if there is a hash, otherwise an empty string */ save: (search: string, hash: string) => void; } interface ServerAdapter { /** * A function to update the server query params. * * @param search The search string - includes the `?` prefix if there are query params, otherwise an empty string */ save: (search: string) => void; } type FunctionValidator<TOut extends object = any> = (value: Query) => TOut; type FunctionValueValidator<TOut = any> = (value?: QueryValue) => TOut; type ValibotValidator = BaseSchema<any, any, any>; type ZodValidator = z.ZodType; type ValueValidator = FunctionValueValidator | ZodValidator | ValibotValidator; type Validator = FunctionValidator | ZodValidator | ValibotValidator; type inferFromValidator<TValidator extends Validator> = TValidator extends ZodValidator ? z.infer<TValidator> : TValidator extends ValibotValidator ? InferOutput<TValidator> : TValidator extends FunctionValidator ? ReturnType<TValidator> : TValidator extends FunctionValueValidator ? ReturnType<TValidator> : never; type QuerySchema = Validator | Record<string, ValueValidator>; type Empty = Record<string, never>; type QueryValue = string | string[]; type Query = Record<string, QueryValue>; type inferShape<TShape extends QuerySchema> = TShape extends Validator ? inferFromValidator<TShape> : TShape extends Empty ? Empty : TShape extends Record<string, Validator> ? { [K in keyof TShape]: inferFromValidator<TShape[K]>; } & {} : never; type Serializer = (value: unknown) => string; type URLLike = Pick<URL, "search" | "hash">; type WindowLike = Pick<typeof window, "location" | "history" | "addEventListener" | "removeEventListener">; interface QueryParamsOptions { /** * Provide a custom implementation of {@linkcode window}. It requires * {@linkcode window.location}, {@linkcode window.history}, * {@linkcode window.addEventListener} and {@linkcode window.removeEventListener} * * @default window */ windowObj?: WindowLike; /** * Add a delay (in ms) before updating the browser URL. This is useful in * situations where URL updates happen frequently, e.g., on every keystroke. * * Note this does not affect the reactive query params object - this will * always be updated immediately. * * @default 0 */ debounce?: number; /** * Control how query params are serialised to the browser query params * * **Note**: this is NOT for encoding values into URI components - it is * for serialising values into strings, which will then be encoded * internally. */ serialise?: Serializer; /** * Adapter to control URL peristence. Defaults to the `browser` adapter. You * **must** pass the `sveltekit` adapter when using SvelteKit with SSR, * otherwise your app may break! */ adapter?: Adapter; } type QueryHelpers<TShape extends Record<string, unknown>> = { /** * The raw query params, extracted from {@linkcode windowObj.location.href} * * Note: this may include query params not defined in your schema. Values will * not have been parsed even if you have specified so in your validators. * * Also note that if you've defined an optional property in your validators * with a default value, it will `undefined` here - without applying the * default - if the value isn't set in the URL. It will, however, be * available (with the default) in {@linkcode QueryHelpers.all} */ readonly raw: Query; /** * Similar to {@linkcode raw}, but any params specified in your validators * will have been parsed - all other values are passed through as-is. * * Note: this may include query params not defined in your schema. */ readonly all: Query & TShape; /** * The query string, generated from the {@linkcode QueryHelpers.raw} query * which may contain query params not defined in your schema. * * If there are query params, this will always start with `?`; if there * are no query params, this will be the empty string. */ readonly search: string; /** Replace _ALL_ query params, triggering a reactive and browser update */ set(params: TShape): void; /** Update a subset of the query params, triggering a reactive and browser update */ update(params: Partial<TShape>): void; /** Remove query params. Note that this may cause your validation check to fail */ remove(...params: (keyof TShape)[]): void; /** Manually unset unregister all event listeners */ unsubscribe(): void; /** * Return the parsed query keys. This only includes the keys from your * validators. Unlike {@linkcode Object.keys}, this is type-safe. */ keys(): Array<keyof TShape>; /** * Type-safe version of {@linkcode Object.entries}. Like * {@linkcode QueryHelpers.keys}, this only contains entries from from your * validators. */ entries(): Array<[keyof TShape, TShape[keyof TShape]]>; }; type QueryHook<TShape extends Record<string, unknown>> = [ TShape, QueryHelpers<TShape> ]; type UseQueryHook<TShape extends Record<string, unknown>> = /** * @param url The current URL */ (url: URLLike) => QueryHook<TShape>; export type { Adapter as A, BrowserAdapter as B, FunctionValidator as F, QuerySchema as Q, Serializer as S, UseQueryHook as U, ValibotValidator as V, WindowLike as W, ZodValidator as Z, QueryParamsOptions as a, Query as b, QueryValue as c, QueryHelpers as d, QueryHook as e, FunctionValueValidator as f, Validator as g, inferFromValidator as h, inferShape as i, URLLike as j, ServerAdapter as k };