UNPKG

nuqs

Version:

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

112 lines (111 loc) 4.8 kB
import { createContext, createElement, useContext } from "react"; //#region src/lib/errors.ts const errors = { 303: "Multiple adapter contexts detected. This might happen in monorepos.", 404: "nuqs requires an adapter to work with your framework.", 409: "Multiple versions of the library are loaded. This may lead to unexpected behavior. Currently using `%s`, but `%s` (via the %s adapter) was about to load on top.", 414: "Max safe URL length exceeded. Some browsers may not be able to accept this URL. Consider limiting the amount of state stored in the URL.", 429: "URL update rate-limited by the browser. Consider increasing `throttleMs` for key(s) `%s`. %O", 500: "Empty search params cache. Search params can't be accessed in Layouts.", 501: "Search params cache already populated. Have you called `parse` twice?", 502: "`processUrlSearchParams` threw while processing key(s) `%s`. %O" }; function error(code) { return `[nuqs] ${errors[code]} See https://nuqs.dev/NUQS-${code}`; } //#endregion //#region src/lib/url-encoding.ts function renderQueryString(search) { if (search.size === 0) return ""; const query = []; for (const [key, value] of search.entries()) { const safeKey = key.replace(/#/g, "%23").replace(/&/g, "%26").replace(/\+/g, "%2B").replace(/=/g, "%3D").replace(/\?/g, "%3F"); query.push(`${safeKey}=${encodeQueryValue(value)}`); } const queryString = "?" + query.join("&"); warnIfURLIsTooLong(queryString); return queryString; } function encodeQueryValue(input) { return input.replace(/%/g, "%25").replace(/\+/g, "%2B").replace(/ /g, "+").replace(/#/g, "%23").replace(/&/g, "%26").replace(/"/g, "%22").replace(/'/g, "%27").replace(/`/g, "%60").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/[\x00-\x1F]/g, (char) => encodeURIComponent(char)); } const URL_MAX_LENGTH = 2e3; function warnIfURLIsTooLong(queryString) { if (typeof location === "undefined") return; if (process.env.NODE_ENV === "production") return; const url = new URL(location.href); url.search = queryString; if (url.href.length > URL_MAX_LENGTH) console.warn(error(414)); } //#endregion //#region src/lib/version.ts const version = "2.9.5"; //#endregion //#region src/lib/global-singleton.ts const fallbackRegistry = {}; /** * Store a module-level singleton on globalThis, keyed by library version, * so that duplicate copies of the library loaded side by side (e.g. in * monorepos, see issue #798) share the same instance instead of each * creating their own. Copies of different versions deliberately keep * separate instances, as internal state shapes may differ across versions. */ function globalSingleton(scope, create) { const key = Symbol.for(`nuqs.${version}.${scope}`); const registry = globalThis; if (registry[key] != null) return registry[key]; const target = Object.isExtensible(registry) ? registry : fallbackRegistry; return target[key] ??= create(); } /** * Like globalSingleton, but additionally keyed by the identity of a * runtime object, for singletons that must not be shared across distinct * runtimes (e.g. one React context per React instance). */ function globalWeakSingleton(scope, key, create) { const instances = globalSingleton(scope, () => /* @__PURE__ */ new WeakMap()); if (!instances.has(key)) instances.set(key, create()); return instances.get(key); } //#endregion //#region src/adapters/lib/context.ts const context = globalWeakSingleton("adapter-context", createContext, () => { const ctx = createContext({ useAdapter() { throw new Error(error(404)); } }); ctx.displayName = "NuqsAdapterContext"; return ctx; }); if (typeof window !== "undefined") { if (window.__NuqsAdapterContext && window.__NuqsAdapterContext !== context) console.error(error(303)); window.__NuqsAdapterContext = context; } /** * Create a custom adapter (context provider) for nuqs to work with your framework / router. * * Adapters are based on React Context, * * @param useAdapter * @returns */ function createAdapterProvider(useAdapter) { return ({ children, defaultOptions, processUrlSearchParams, ...props }) => createElement(context.Provider, { ...props, value: { useAdapter, defaultOptions, processUrlSearchParams } }, children); } function useAdapter(watchKeys) { const value = useContext(context); if (!("useAdapter" in value)) throw new Error(error(404)); return value.useAdapter(watchKeys); } const useAdapterDefaultOptions = () => useContext(context).defaultOptions; const useAdapterProcessUrlSearchParams = () => useContext(context).processUrlSearchParams; //#endregion export { useAdapterProcessUrlSearchParams as a, renderQueryString as c, useAdapterDefaultOptions as i, error as l, createAdapterProvider as n, globalSingleton as o, useAdapter as r, version as s, context as t }; //# sourceMappingURL=context-Mu913OAK.js.map