@real-router/persistent-params-plugin 0.1.38 → 0.1.39

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.
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var t=require("@real-router/core");function r(t){const r={};for(const e in t)Object.hasOwn(t,e)&&(r[e]=t[e]);return r}function e(t){return"number"==typeof t?Number.isFinite(t):"string"==typeof t||"boolean"==typeof t}var a=/[\s#%&/=?\\]/,s=String.raw`Cannot contain: = & ? # % / \ or whitespace`;function n(t){if(a.test(t))throw new TypeError(`[@real-router/persistent-params-plugin] Invalid parameter name "${t}". ${s}`)}function i(t,r){if(null===r)throw new TypeError(`[@real-router/persistent-params-plugin] Parameter "${t}" cannot be null. Use undefined to remove the parameter from persistence.`);if(void 0!==r&&!e(r)){const e=Array.isArray(r)?"array":typeof r;throw new TypeError(`[@real-router/persistent-params-plugin] Parameter "${t}" must be a primitive value (string, number, or boolean), got ${e}. Objects and arrays are not supported in URL parameters.`)}}var o=class{#t;#r;#e;#a;#s;#n;constructor(t,r,e,a){this.#t=t,this.#a=r,this.#r=e,this.#e=a}getPlugin(){try{const t=[...this.#r].join("&");this.#t.setRootPath(`${this.#e}?${t}`)}catch(t){throw new Error(`[@real-router/persistent-params-plugin] Failed to update root path: ${t instanceof Error?t.message:String(t)}`,{cause:t})}return this.#s=this.#t.addInterceptor("buildPath",(t,r,e)=>t(r,this.#i(e??{}))),this.#n=this.#t.addInterceptor("forwardState",(t,r,e)=>{const a=t(r,e);return{...a,params:this.#i(a.params)}}),{onTransitionSuccess:t=>{this.#o(t)},teardown:()=>{this.#p()}}}#i(t){const e=r(t);let a;for(const t of Object.keys(e)){const r=e[t];void 0===r&&this.#r.has(t)?(this.#r.delete(t),a??={...this.#a},delete a[t]):i(t,r)}return a&&(this.#a=Object.freeze(a)),function(t,e){const a=r(e),s={};for(const r in t)Object.hasOwn(t,r)&&void 0!==t[r]&&(s[r]=t[r]);for(const t of Object.keys(a)){const r=a[t];void 0===r?delete s[t]:s[t]=r}return s}(this.#a,e)}#o(t){let r;for(const e of this.#r){const a=t.params[e];Object.hasOwn(t.params,e)&&void 0!==a?(i(e,a),this.#a[e]!==a&&(r??={...this.#a},r[e]=a)):Object.hasOwn(this.#a,e)&&void 0!==this.#a[e]&&(r??={...this.#a},delete r[e])}r&&(this.#a=Object.freeze(r))}#p(){try{this.#s?.(),this.#n?.(),this.#t.setRootPath(this.#e)}catch(t){console.error("persistent-params-plugin","Error during teardown:",t)}}},p=()=>({});exports.persistentParamsPluginFactory=function(r={}){return function(t){if(null==(r=t)||!(Array.isArray(r)?r.every(t=>{if("string"!=typeof t||0===t.length)return!1;try{return n(t),!0}catch{return!1}}):"object"==typeof r&&Object.getPrototypeOf(r)===Object.prototype&&Object.entries(r).every(([t,r])=>{if("string"!=typeof t||0===t.length)return!1;try{n(t)}catch{return!1}return e(r)}))){let r;throw r=null===t?"null":Array.isArray(t)?"array with invalid items":typeof t,new TypeError(`[@real-router/persistent-params-plugin] Invalid params configuration. Expected array of non-empty strings or object with primitive values, got ${r}.`)}var r}(r),Array.isArray(r)&&0===r.length?p:Array.isArray(r)||0!==Object.keys(r).length?e=>{let a;if(Array.isArray(r)){const t={};for(const e of r)t[e]=void 0;a=Object.freeze(t)}else a=Object.freeze({...r});const s=new Set(Array.isArray(r)?[...r]:Object.keys(r)),n=t.getPluginApi(e),i=n.getRootPath();return new o(n,a,s,i).getPlugin()}:p};//# sourceMappingURL=index.js.map
1
+ var t=require("@real-router/core"),e="[@real-router/persistent-params-plugin]";function r(t){return"number"==typeof t?Number.isFinite(t):"string"==typeof t||"boolean"==typeof t}var s=/[\s#%&/=?\\]/,a=String.raw`Cannot contain: = & ? # % / \ or whitespace`;function n(t){if(s.test(t))throw new TypeError(`${e} Invalid parameter name "${t}". ${a}`)}function o(t,s){if(null===s)throw new TypeError(`${e} Parameter "${t}" cannot be null. Use undefined to remove the parameter from persistence.`);if(void 0!==s&&!r(s)){const r=Array.isArray(s)?"array":typeof s;throw new TypeError(`${e} Parameter "${t}" must be a primitive value (string, number, or boolean), got ${r}. Objects and arrays are not supported in URL parameters.`)}}var i=class{#t;#e;#r;#s;#a;#n;constructor(t,r,s,a){let n,o;this.#t=t,this.#n=r,this.#e=s,this.#r=a;try{t.setRootPath(`${a}?${[...s].join("&")}`),n=t.addInterceptor("buildPath",(t,e,r)=>t(e,this.#o(r??{}))),o=t.addInterceptor("forwardState",(t,e,r)=>{const s=t(e,r);return{...s,params:this.#o(s.params)}})}catch(r){throw n?.(),o?.(),t.setRootPath(a),new Error(`${e} Failed to initialize: ${r instanceof Error?r.message:String(r)}`,{cause:r})}this.#s=n,this.#a=o}getPlugin(){return{onTransitionSuccess:t=>{this.#i(t)},teardown:()=>{this.#c()}}}#o(t){const e=function(t){const e={};for(const r in t)Object.hasOwn(t,r)&&(e[r]=t[r]);return e}(t);let r;for(const t of Object.keys(e)){const s=e[t];void 0===s&&this.#e.has(t)?(this.#e.delete(t),r??={...this.#n},delete r[t]):o(t,s)}return r&&(this.#n=Object.freeze(r)),function(t,e){const r={};for(const e in t)Object.hasOwn(t,e)&&void 0!==t[e]&&(r[e]=t[e]);for(const t of Object.keys(e)){const s=e[t];void 0===s?delete r[t]:r[t]=s}return r}(this.#n,e)}#i(t){let e;for(const r of this.#e){const s=t.params[r];Object.hasOwn(t.params,r)&&void 0!==s?(o(r,s),this.#n[r]!==s&&(e??={...this.#n},e[r]=s)):Object.hasOwn(this.#n,r)&&void 0!==this.#n[r]&&(e??={...this.#n},delete e[r])}e&&(this.#n=Object.freeze(e))}#c(){this.#s(),this.#a();try{this.#t.setRootPath(this.#r)}catch{}}},c={},p=()=>c;exports.persistentParamsPluginFactory=function(s={}){!function(t){if(null==(s=t)||!(Array.isArray(s)?s.every(t=>{if("string"!=typeof t||0===t.length)return!1;try{return n(t),!0}catch{return!1}}):"object"==typeof s&&Object.getPrototypeOf(s)===Object.prototype&&Object.entries(s).every(([t,e])=>{if("string"!=typeof t||0===t.length)return!1;try{n(t)}catch{return!1}return r(e)}))){let r;throw r=null===t?"null":Array.isArray(t)?"array with invalid items":typeof t,new TypeError(`${e} Invalid params configuration. Expected array of non-empty strings or object with primitive values, got ${r}.`)}var s}(s);const a=Array.isArray(s)?s:Object.keys(s);if(0===a.length)return p;const o={};if(Array.isArray(s))for(const t of s)o[t]=void 0;else Object.assign(o,s);Object.freeze(o);const c=new Set(a);return e=>{const r=t.getPluginApi(e);return new i(r,o,new Set(c),r.getRootPath()).getPlugin()}};//# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/param-utils.ts","../../src/validation.ts","../../src/plugin.ts","../../src/factory.ts"],"names":["getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/persistent-params-plugin/modules/param-utils.ts\n\nimport type { Params } from \"@real-router/core\";\n\n/**\n * Safely extracts own properties from params object.\n * Uses Object.hasOwn to prevent prototype pollution attacks.\n *\n * @param params - Parameters object (may contain inherited properties)\n * @returns New object with only own properties\n *\n * @example\n * const malicious = Object.create({ __proto__: { admin: true } });\n * malicious.mode = 'dev';\n * const safe = extractOwnParams(malicious); // { mode: 'dev' } (no __proto__)\n */\nexport function extractOwnParams(params: Params): Params {\n const result: Params = {};\n\n for (const key in params) {\n /* v8 ignore next -- @preserve: core validates params prototype — inherited keys never reach here */\n if (Object.hasOwn(params, key)) {\n result[key] = params[key];\n }\n }\n\n return result;\n}\n\n/**\n * Merges persistent and current parameters into a single Params object.\n * Keys explicitly set to `undefined` in current params are removed from result.\n *\n * Creates a new immutable object - does not mutate input parameters.\n *\n * @param persistent - Frozen persistent parameters\n * @param current - Current parameters from navigation\n * @returns New Params object with merged values\n *\n * @example\n * const persistent = { lang: 'en', theme: 'dark' };\n * const current = { theme: 'light', mode: 'dev' };\n * mergeParams(persistent, current); // { lang: 'en', theme: 'light', mode: 'dev' }\n *\n * @example\n * // Removing parameters with undefined\n * const persistent = { lang: 'en', theme: 'dark' };\n * const current = { theme: undefined };\n * mergeParams(persistent, current); // { lang: 'en' } (theme removed)\n */\nexport function mergeParams(\n persistent: Readonly<Params>,\n current: Params,\n): Params {\n // Safely extract own properties from current params\n const safeCurrentParams = extractOwnParams(current);\n\n // Start with persistent params, but EXCLUDE undefined values\n // (undefined values don't appear in URLs, so we shouldn't include them)\n const result: Params = {};\n\n for (const key in persistent) {\n if (Object.hasOwn(persistent, key) && persistent[key] !== undefined) {\n result[key] = persistent[key];\n }\n }\n\n // Apply current params\n for (const key of Object.keys(safeCurrentParams)) {\n const value = safeCurrentParams[key];\n\n if (value === undefined) {\n // Remove param if explicitly set to undefined\n delete result[key];\n } else {\n // Add or update param\n result[key] = value;\n }\n }\n\n return result;\n}\n","// packages/persistent-params-plugin/src/validation.ts\n\nimport { isPrimitiveValue } from \"type-guards\";\n\nimport type { PersistentParamsConfig } from \"./types\";\n\nconst INVALID_PARAM_KEY_REGEX = /[\\s#%&/=?\\\\]/;\nconst INVALID_CHARS_MESSAGE = String.raw`Cannot contain: = & ? # % / \\ or whitespace`;\n\nexport function validateParamKey(key: string): void {\n if (INVALID_PARAM_KEY_REGEX.test(key)) {\n throw new TypeError(\n `[@real-router/persistent-params-plugin] Invalid parameter name \"${key}\". ${INVALID_CHARS_MESSAGE}`,\n );\n }\n}\n\n/**\n * Validates params configuration structure and values.\n * Ensures all parameter names are non-empty strings and all default values are primitives.\n *\n * @param config - Configuration to validate\n * @returns true if configuration is valid\n */\nexport function isValidParamsConfig(\n config: unknown,\n): config is PersistentParamsConfig {\n if (config === null || config === undefined) {\n return false;\n }\n\n // Array configuration: all items must be non-empty strings\n if (Array.isArray(config)) {\n return config.every((item) => {\n if (typeof item !== \"string\" || item.length === 0) {\n return false;\n }\n\n try {\n validateParamKey(item);\n\n return true;\n } catch {\n return false;\n }\n });\n }\n\n // Object configuration: must be plain object with primitive values\n if (typeof config === \"object\") {\n // Reject non-plain objects (Date, Map, etc.)\n if (Object.getPrototypeOf(config) !== Object.prototype) {\n return false;\n }\n\n // All keys must be non-empty strings, all values must be primitives\n return Object.entries(config).every(([key, value]) => {\n // Check key is non-empty string\n if (typeof key !== \"string\" || key.length === 0) {\n return false;\n }\n\n // Validate key doesn't contain special characters\n try {\n validateParamKey(key);\n } catch {\n return false;\n }\n\n // Validate value is primitive (NaN/Infinity already rejected by isPrimitiveValue)\n return isPrimitiveValue(value);\n });\n }\n\n return false;\n}\n\n/**\n * Validates parameter value before persisting.\n * Throws descriptive TypeError if value is not valid for URL parameters.\n *\n * @param key - Parameter name for error messages\n * @param value - Value to validate\n * @throws {TypeError} If value is null, array, object, or other non-primitive type\n */\nexport function validateParamValue(key: string, value: unknown): void {\n if (value === null) {\n throw new TypeError(\n `[@real-router/persistent-params-plugin] Parameter \"${key}\" cannot be null. ` +\n `Use undefined to remove the parameter from persistence.`,\n );\n }\n\n if (value !== undefined && !isPrimitiveValue(value)) {\n const actualType = Array.isArray(value) ? \"array\" : typeof value;\n\n throw new TypeError(\n `[@real-router/persistent-params-plugin] Parameter \"${key}\" must be a primitive value ` +\n `(string, number, or boolean), got ${actualType}. ` +\n `Objects and arrays are not supported in URL parameters.`,\n );\n }\n}\n\n/**\n * Validates the params configuration and throws a descriptive error if invalid.\n *\n * @param params - Configuration to validate\n * @throws {TypeError} If params is not a valid configuration\n */\nexport function validateConfig(params: unknown): void {\n if (!isValidParamsConfig(params)) {\n let actualType: string;\n\n if (params === null) {\n actualType = \"null\";\n } else if (Array.isArray(params)) {\n actualType = \"array with invalid items\";\n } else {\n actualType = typeof params;\n }\n\n throw new TypeError(\n `[@real-router/persistent-params-plugin] Invalid params configuration. ` +\n `Expected array of non-empty strings or object with primitive values, got ${actualType}.`,\n );\n }\n}\n","// packages/persistent-params-plugin/src/plugin.ts\n\nimport { extractOwnParams, mergeParams } from \"./param-utils\";\nimport { validateParamValue } from \"./validation\";\n\nimport type { Params, PluginApi, State, Plugin } from \"@real-router/core\";\n\nexport class PersistentParamsPlugin {\n readonly #api: PluginApi;\n readonly #paramNamesSet: Set<string>;\n readonly #originalRootPath: string;\n\n #persistentParams: Readonly<Params>;\n #removeBuildPathInterceptor: (() => void) | undefined;\n #removeForwardStateInterceptor: (() => void) | undefined;\n\n constructor(\n api: PluginApi,\n persistentParams: Readonly<Params>,\n paramNamesSet: Set<string>,\n originalRootPath: string,\n ) {\n this.#api = api;\n this.#persistentParams = persistentParams;\n this.#paramNamesSet = paramNamesSet;\n this.#originalRootPath = originalRootPath;\n }\n\n getPlugin(): Plugin {\n try {\n const queryString = [...this.#paramNamesSet].join(\"&\");\n\n this.#api.setRootPath(`${this.#originalRootPath}?${queryString}`);\n } /* v8 ignore start -- @preserve: defensive error wrapping for setRootPath failure */ catch (error) {\n throw new Error(\n `[@real-router/persistent-params-plugin] Failed to update root path: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n } /* v8 ignore stop */\n\n this.#removeBuildPathInterceptor = this.#api.addInterceptor(\n \"buildPath\",\n (next, route, navParams) =>\n next(route, this.#withPersistentParams(navParams ?? {})),\n );\n\n this.#removeForwardStateInterceptor = this.#api.addInterceptor(\n \"forwardState\",\n (next, routeName, routeParams) => {\n const result = next(routeName, routeParams);\n\n return {\n ...result,\n params: this.#withPersistentParams(result.params),\n };\n },\n );\n\n return {\n onTransitionSuccess: (toState) => {\n this.#onTransitionSuccess(toState);\n },\n teardown: () => {\n this.#teardown();\n },\n };\n }\n\n #withPersistentParams(additionalParams: Params): Params {\n const safeParams = extractOwnParams(additionalParams);\n let newParams: Params | undefined;\n\n for (const key of Object.keys(safeParams)) {\n const value = safeParams[key];\n\n if (value === undefined && this.#paramNamesSet.has(key)) {\n this.#paramNamesSet.delete(key);\n newParams ??= { ...this.#persistentParams };\n delete newParams[key];\n } else {\n validateParamValue(key, value);\n }\n }\n\n if (newParams) {\n this.#persistentParams = Object.freeze(newParams);\n }\n\n return mergeParams(this.#persistentParams, safeParams);\n }\n\n #onTransitionSuccess(toState: State): void {\n let newParams: Params | undefined;\n\n for (const key of this.#paramNamesSet) {\n const value = toState.params[key];\n\n if (!Object.hasOwn(toState.params, key) || value === undefined) {\n /* v8 ignore next 4 -- @preserve: defensive removal for states committed via navigateToState bypassing forwardState */\n if (\n Object.hasOwn(this.#persistentParams, key) &&\n this.#persistentParams[key] !== undefined\n ) {\n newParams ??= { ...this.#persistentParams };\n delete newParams[key];\n }\n\n continue;\n }\n\n validateParamValue(key, value);\n\n if (this.#persistentParams[key] !== value) {\n newParams ??= { ...this.#persistentParams };\n newParams[key] = value;\n }\n }\n\n if (newParams) {\n this.#persistentParams = Object.freeze(newParams);\n }\n }\n\n #teardown(): void {\n try {\n this.#removeBuildPathInterceptor?.();\n this.#removeForwardStateInterceptor?.();\n this.#api.setRootPath(this.#originalRootPath);\n } /* v8 ignore start -- @preserve: defensive error logging for teardown failure */ catch (error) {\n console.error(\n \"persistent-params-plugin\",\n \"Error during teardown:\",\n error,\n );\n } /* v8 ignore stop */\n }\n}\n","// packages/persistent-params-plugin/modules/factory.ts\n\nimport { getPluginApi } from \"@real-router/core\";\n\nimport { PersistentParamsPlugin } from \"./plugin\";\nimport { validateConfig } from \"./validation\";\n\nimport type { PersistentParamsConfig } from \"./types\";\nimport type { Params, PluginFactory, Plugin } from \"@real-router/core\";\n\nconst noop = () => ({});\n\n/**\n * Factory for the persistent parameters' plugin.\n *\n * This plugin allows you to specify certain route parameters to be persisted across\n * all navigation transitions. Persisted parameters are automatically merged into\n * route parameters when building paths or states.\n *\n * Key features:\n * - Automatic persistence of query parameters across navigations\n * - Support for default values\n * - Type-safe (only primitives: string, number, boolean)\n * - Immutable internal state\n * - Protection against prototype pollution\n * - Full teardown support (can be safely unsubscribed)\n *\n * If a persisted parameter is explicitly set to `undefined` during navigation,\n * it will be removed from the persisted state and omitted from subsequent URLs.\n *\n * The plugin also adjusts the router's root path to include query parameters for\n * all persistent params, ensuring correct URL construction.\n *\n * @param params - Either an array of parameter names (strings) to persist,\n * or an object mapping parameter names to initial values.\n * If an array, initial values will be `undefined`.\n *\n * @returns A PluginFactory that creates the persistent params plugin instance.\n *\n * @example\n * // Persist parameters without default values\n * router.usePlugin(persistentParamsPlugin(['mode', 'lang']));\n *\n * @example\n * // Persist parameters with default values\n * router.usePlugin(persistentParamsPlugin({ mode: 'dev', lang: 'en' }));\n *\n * @example\n * // Removing a persisted parameter\n * router.navigate('route', { mode: undefined }); // mode will be removed\n *\n * @example\n * // Unsubscribing (full cleanup)\n * const unsubscribe = router.usePlugin(persistentParamsPlugin(['mode']));\n * unsubscribe(); // Restores original router state\n *\n * @throws {TypeError} If params is not a valid array of strings or object with primitives\n * @throws {Error} If plugin is already initialized on this router instance\n */\nexport function persistentParamsPluginFactory(\n params: PersistentParamsConfig = {},\n): PluginFactory {\n validateConfig(params);\n\n // Empty configuration - valid but does nothing\n if (Array.isArray(params) && params.length === 0) {\n return noop;\n }\n\n if (!Array.isArray(params) && Object.keys(params).length === 0) {\n return noop;\n }\n\n return (router): Plugin => {\n // Initialize frozen persistent parameters\n let persistentParams: Readonly<Params>;\n\n if (Array.isArray(params)) {\n const initial: Params = {};\n\n for (const param of params) {\n initial[param] = undefined;\n }\n\n persistentParams = Object.freeze(initial);\n } else {\n persistentParams = Object.freeze({ ...params });\n }\n\n const paramNamesSet = new Set<string>(\n Array.isArray(params) ? [...params] : Object.keys(params),\n );\n\n const api = getPluginApi(router);\n const originalRootPath = api.getRootPath();\n\n const plugin = new PersistentParamsPlugin(\n api,\n persistentParams,\n paramNamesSet,\n originalRootPath,\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/param-utils.ts","../../src/validation.ts","../../src/plugin.ts","../../src/factory.ts"],"names":["getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/persistent-params-plugin/src/constants.ts\n\nexport const LOGGER_CONTEXT = \"persistent-params-plugin\";\n\nexport const ERROR_PREFIX = `[@real-router/${LOGGER_CONTEXT}]`;\n","// packages/persistent-params-plugin/src/param-utils.ts\n\nimport type { Params } from \"@real-router/core\";\n\n/**\n * Safely extracts own properties from params object.\n * Uses Object.hasOwn to prevent prototype pollution attacks.\n *\n * @param params - Parameters object (may contain inherited properties)\n * @returns New object with only own properties\n *\n * @example\n * const malicious = Object.create({ __proto__: { admin: true } });\n * malicious.mode = 'dev';\n * const safe = extractOwnParams(malicious); // { mode: 'dev' } (no __proto__)\n */\nexport function extractOwnParams(params: Params): Params {\n const result: Params = {};\n\n for (const key in params) {\n /* v8 ignore next -- @preserve: core validates params prototype — inherited keys never reach here */\n if (Object.hasOwn(params, key)) {\n result[key] = params[key];\n }\n }\n\n return result;\n}\n\n/**\n * Merges persistent and current parameters into a single Params object.\n *\n * IMPORTANT: `current` must be pre-sanitized via `extractOwnParams()` by the caller.\n * This function does NOT perform prototype pollution protection on its own.\n *\n * @param persistent - Frozen persistent parameters\n * @param current - Pre-sanitized current parameters (own properties only)\n */\nexport function mergeParams(\n persistent: Readonly<Params>,\n current: Params,\n): Params {\n const result: Params = {};\n\n for (const key in persistent) {\n if (Object.hasOwn(persistent, key) && persistent[key] !== undefined) {\n result[key] = persistent[key];\n }\n }\n\n for (const key of Object.keys(current)) {\n const value = current[key];\n\n if (value === undefined) {\n delete result[key];\n } else {\n result[key] = value;\n }\n }\n\n return result;\n}\n","// packages/persistent-params-plugin/src/validation.ts\n\nimport { isPrimitiveValue } from \"type-guards\";\n\nimport { ERROR_PREFIX } from \"./constants\";\n\nimport type { PersistentParamsConfig } from \"./types\";\n\nconst INVALID_PARAM_KEY_REGEX = /[\\s#%&/=?\\\\]/;\nconst INVALID_CHARS_MESSAGE = String.raw`Cannot contain: = & ? # % / \\ or whitespace`;\n\nexport function validateParamKey(key: string): void {\n if (INVALID_PARAM_KEY_REGEX.test(key)) {\n throw new TypeError(\n `${ERROR_PREFIX} Invalid parameter name \"${key}\". ${INVALID_CHARS_MESSAGE}`,\n );\n }\n}\n\n/**\n * Validates params configuration structure and values.\n * Ensures all parameter names are non-empty strings and all default values are primitives.\n *\n * @param config - Configuration to validate\n * @returns true if configuration is valid\n */\nexport function isValidParamsConfig(\n config: unknown,\n): config is PersistentParamsConfig {\n if (config === null || config === undefined) {\n return false;\n }\n\n // Array configuration: all items must be non-empty strings\n if (Array.isArray(config)) {\n return config.every((item) => {\n if (typeof item !== \"string\" || item.length === 0) {\n return false;\n }\n\n try {\n validateParamKey(item);\n\n return true;\n } catch {\n return false;\n }\n });\n }\n\n // Object configuration: must be plain object with primitive values\n if (typeof config === \"object\") {\n // Reject non-plain objects (Date, Map, etc.)\n if (Object.getPrototypeOf(config) !== Object.prototype) {\n return false;\n }\n\n // All keys must be non-empty strings, all values must be primitives\n return Object.entries(config).every(([key, value]) => {\n // Check key is non-empty string\n if (typeof key !== \"string\" || key.length === 0) {\n return false;\n }\n\n // Validate key doesn't contain special characters\n try {\n validateParamKey(key);\n } catch {\n return false;\n }\n\n // Validate value is primitive (NaN/Infinity already rejected by isPrimitiveValue)\n return isPrimitiveValue(value);\n });\n }\n\n return false;\n}\n\n/**\n * Validates parameter value before persisting.\n * Throws descriptive TypeError if value is not valid for URL parameters.\n *\n * @param key - Parameter name for error messages\n * @param value - Value to validate\n * @throws {TypeError} If value is null, array, object, or other non-primitive type\n */\nexport function validateParamValue(key: string, value: unknown): void {\n if (value === null) {\n throw new TypeError(\n `${ERROR_PREFIX} Parameter \"${key}\" cannot be null. ` +\n `Use undefined to remove the parameter from persistence.`,\n );\n }\n\n if (value !== undefined && !isPrimitiveValue(value)) {\n const actualType = Array.isArray(value) ? \"array\" : typeof value;\n\n throw new TypeError(\n `${ERROR_PREFIX} Parameter \"${key}\" must be a primitive value ` +\n `(string, number, or boolean), got ${actualType}. ` +\n `Objects and arrays are not supported in URL parameters.`,\n );\n }\n}\n\n/**\n * Validates the params configuration and throws a descriptive error if invalid.\n *\n * @param params - Configuration to validate\n * @throws {TypeError} If params is not a valid configuration\n */\nexport function validateConfig(params: unknown): void {\n if (!isValidParamsConfig(params)) {\n let actualType: string;\n\n if (params === null) {\n actualType = \"null\";\n } else if (Array.isArray(params)) {\n actualType = \"array with invalid items\";\n } else {\n actualType = typeof params;\n }\n\n throw new TypeError(\n `${ERROR_PREFIX} Invalid params configuration. ` +\n `Expected array of non-empty strings or object with primitive values, got ${actualType}.`,\n );\n }\n}\n","// packages/persistent-params-plugin/src/plugin.ts\n\nimport { ERROR_PREFIX } from \"./constants\";\nimport { extractOwnParams, mergeParams } from \"./param-utils\";\nimport { validateParamValue } from \"./validation\";\n\nimport type { Params, PluginApi, State, Plugin } from \"@real-router/core\";\n\nexport class PersistentParamsPlugin {\n readonly #api: PluginApi;\n readonly #paramNamesSet: Set<string>;\n readonly #originalRootPath: string;\n readonly #removeBuildPathInterceptor: () => void;\n readonly #removeForwardStateInterceptor: () => void;\n\n #persistentParams: Readonly<Params>;\n\n constructor(\n api: PluginApi,\n persistentParams: Readonly<Params>,\n paramNamesSet: Set<string>,\n originalRootPath: string,\n ) {\n this.#api = api;\n this.#persistentParams = persistentParams;\n this.#paramNamesSet = paramNamesSet;\n this.#originalRootPath = originalRootPath;\n\n let removeBuildPath: (() => void) | undefined;\n let removeForwardState: (() => void) | undefined;\n\n try {\n api.setRootPath(`${originalRootPath}?${[...paramNamesSet].join(\"&\")}`);\n\n removeBuildPath = api.addInterceptor(\n \"buildPath\",\n (next, route, navParams) =>\n next(route, this.#withPersistentParams(navParams ?? {})),\n );\n\n removeForwardState = api.addInterceptor(\n \"forwardState\",\n (next, routeName, routeParams) => {\n const result = next(routeName, routeParams);\n\n return {\n ...result,\n params: this.#withPersistentParams(result.params),\n };\n },\n );\n } /* v8 ignore start -- @preserve: rollback on partial initialization failure */ catch (error) {\n removeBuildPath?.();\n removeForwardState?.();\n api.setRootPath(originalRootPath);\n\n throw new Error(\n `${ERROR_PREFIX} Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n } /* v8 ignore stop */\n\n this.#removeBuildPathInterceptor = removeBuildPath;\n this.#removeForwardStateInterceptor = removeForwardState;\n }\n\n getPlugin(): Plugin {\n return {\n onTransitionSuccess: (toState) => {\n this.#onTransitionSuccess(toState);\n },\n teardown: () => {\n this.#teardown();\n },\n };\n }\n\n #withPersistentParams(additionalParams: Params): Params {\n const safeParams = extractOwnParams(additionalParams);\n let newParams: Params | undefined;\n\n for (const key of Object.keys(safeParams)) {\n const value = safeParams[key];\n\n if (value === undefined && this.#paramNamesSet.has(key)) {\n this.#paramNamesSet.delete(key);\n newParams ??= { ...this.#persistentParams };\n delete newParams[key];\n } else {\n validateParamValue(key, value);\n }\n }\n\n if (newParams) {\n this.#persistentParams = Object.freeze(newParams);\n }\n\n return mergeParams(this.#persistentParams, safeParams);\n }\n\n #onTransitionSuccess(toState: State): void {\n let newParams: Params | undefined;\n\n for (const key of this.#paramNamesSet) {\n const value = toState.params[key];\n\n if (!Object.hasOwn(toState.params, key) || value === undefined) {\n /* v8 ignore next 4 -- @preserve: defensive removal for states committed via navigateToState bypassing forwardState */\n if (\n Object.hasOwn(this.#persistentParams, key) &&\n this.#persistentParams[key] !== undefined\n ) {\n newParams ??= { ...this.#persistentParams };\n delete newParams[key];\n }\n\n continue;\n }\n\n validateParamValue(key, value);\n\n if (this.#persistentParams[key] !== value) {\n newParams ??= { ...this.#persistentParams };\n newParams[key] = value;\n }\n }\n\n if (newParams) {\n this.#persistentParams = Object.freeze(newParams);\n }\n }\n\n #teardown(): void {\n this.#removeBuildPathInterceptor();\n this.#removeForwardStateInterceptor();\n\n /* v8 ignore start -- @preserve: setRootPath throws RouterError(ROUTER_DISPOSED) during router.dispose() */\n try {\n this.#api.setRootPath(this.#originalRootPath);\n } catch {\n // Expected during router.dispose(): FSM enters DISPOSED before plugin teardown,\n // so setRootPath's throwIfDisposed() check throws. Restoring rootPath on a\n // destroyed router is unnecessary — swallow silently.\n }\n /* v8 ignore stop */\n }\n}\n","// packages/persistent-params-plugin/src/factory.ts\n\nimport { getPluginApi } from \"@real-router/core\";\n\nimport { PersistentParamsPlugin } from \"./plugin\";\nimport { validateConfig } from \"./validation\";\n\nimport type { PersistentParamsConfig } from \"./types\";\nimport type { Params, PluginFactory, Plugin } from \"@real-router/core\";\n\n// Shared singleton — frozen by core on first use. Do not add properties.\nconst EMPTY_PLUGIN: Plugin = {};\nconst noop: PluginFactory = () => EMPTY_PLUGIN;\n\n/**\n * Factory for the persistent parameters' plugin.\n *\n * This plugin allows you to specify certain route parameters to be persisted across\n * all navigation transitions. Persisted parameters are automatically merged into\n * route parameters when building paths or states.\n *\n * Key features:\n * - Automatic persistence of query parameters across navigations\n * - Support for default values\n * - Type-safe (only primitives: string, number, boolean)\n * - Immutable internal state\n * - Protection against prototype pollution\n * - Full teardown support (can be safely unsubscribed)\n *\n * If a persisted parameter is explicitly set to `undefined` during navigation,\n * it will be removed from the persisted state and omitted from subsequent URLs.\n *\n * The plugin also adjusts the router's root path to include query parameters for\n * all persistent params, ensuring correct URL construction.\n *\n * @param params - Either an array of parameter names (strings) to persist,\n * or an object mapping parameter names to initial values.\n * If an array, initial values will be `undefined`.\n *\n * @returns A PluginFactory that creates the persistent params plugin instance.\n *\n * @example\n * // Persist parameters without default values\n * router.usePlugin(persistentParamsPlugin(['mode', 'lang']));\n *\n * @example\n * // Persist parameters with default values\n * router.usePlugin(persistentParamsPlugin({ mode: 'dev', lang: 'en' }));\n *\n * @example\n * // Removing a persisted parameter\n * router.navigate('route', { mode: undefined }); // mode will be removed\n *\n * @example\n * // Unsubscribing (full cleanup)\n * const unsubscribe = router.usePlugin(persistentParamsPlugin(['mode']));\n * unsubscribe(); // Restores original router state\n *\n * @throws {TypeError} If params is not a valid array of strings or object with primitives\n * @throws {Error} If plugin is already initialized on this router instance\n */\nexport function persistentParamsPluginFactory(\n params: PersistentParamsConfig = {},\n): PluginFactory {\n validateConfig(params);\n\n const paramNames = Array.isArray(params) ? params : Object.keys(params);\n\n if (paramNames.length === 0) {\n return noop;\n }\n\n const initialParams: Params = {};\n\n if (Array.isArray(params)) {\n for (const param of params) {\n initialParams[param] = undefined;\n }\n } else {\n Object.assign(initialParams, params);\n }\n\n Object.freeze(initialParams);\n\n const paramNamesSet = new Set<string>(paramNames);\n\n return (router): Plugin => {\n const api = getPluginApi(router);\n const plugin = new PersistentParamsPlugin(\n api,\n initialParams,\n new Set(paramNamesSet),\n api.getRootPath(),\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
@@ -1 +1 @@
1
- {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/types.ts":{"bytes":515,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/param-utils.ts":{"bytes":2555,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/validation.ts":{"bytes":3824,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/plugin.ts":{"bytes":4027,"imports":[{"path":"src/param-utils.ts","kind":"import-statement","original":"./param-utils"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/factory.ts":{"bytes":3414,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":165,"imports":[{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/factory.ts","kind":"import-statement","original":"./factory"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":19205},"dist/cjs/index.js":{"imports":[{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["PersistentParamsConfig","persistentParamsPluginFactory"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":988},"src/param-utils.ts":{"bytesInOutput":679},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":118},"src/validation.ts":{"bytesInOutput":2226},"src/plugin.ts":{"bytesInOutput":3092},"src/index.ts":{"bytesInOutput":0}},"bytes":7507}}}
1
+ {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/constants.ts":{"bytes":178,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/param-utils.ts":{"bytes":1731,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/validation.ts":{"bytes":3773,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/plugin.ts":{"bytes":4412,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/param-utils.ts","kind":"import-statement","original":"./param-utils"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/factory.ts":{"bytes":3227,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":166,"imports":[{"path":"src/factory.ts","kind":"import-statement","original":"./factory"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":18658},"dist/cjs/index.js":{"imports":[{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["persistentParamsPluginFactory"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":822},"src/constants.ts":{"bytesInOutput":104},"src/param-utils.ts":{"bytesInOutput":604},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":118},"src/validation.ts":{"bytesInOutput":2130},"src/plugin.ts":{"bytesInOutput":3126},"src/index.ts":{"bytesInOutput":0}},"bytes":7437}}}
@@ -1 +1 @@
1
- import{getPluginApi as t}from"@real-router/core";function r(t){const r={};for(const e in t)Object.hasOwn(t,e)&&(r[e]=t[e]);return r}function e(t){return"number"==typeof t?Number.isFinite(t):"string"==typeof t||"boolean"==typeof t}var a=/[\s#%&/=?\\]/,s=String.raw`Cannot contain: = & ? # % / \ or whitespace`;function n(t){if(a.test(t))throw new TypeError(`[@real-router/persistent-params-plugin] Invalid parameter name "${t}". ${s}`)}function o(t,r){if(null===r)throw new TypeError(`[@real-router/persistent-params-plugin] Parameter "${t}" cannot be null. Use undefined to remove the parameter from persistence.`);if(void 0!==r&&!e(r)){const e=Array.isArray(r)?"array":typeof r;throw new TypeError(`[@real-router/persistent-params-plugin] Parameter "${t}" must be a primitive value (string, number, or boolean), got ${e}. Objects and arrays are not supported in URL parameters.`)}}var i=class{#t;#r;#e;#a;#s;#n;constructor(t,r,e,a){this.#t=t,this.#a=r,this.#r=e,this.#e=a}getPlugin(){try{const t=[...this.#r].join("&");this.#t.setRootPath(`${this.#e}?${t}`)}catch(t){throw new Error(`[@real-router/persistent-params-plugin] Failed to update root path: ${t instanceof Error?t.message:String(t)}`,{cause:t})}return this.#s=this.#t.addInterceptor("buildPath",(t,r,e)=>t(r,this.#o(e??{}))),this.#n=this.#t.addInterceptor("forwardState",(t,r,e)=>{const a=t(r,e);return{...a,params:this.#o(a.params)}}),{onTransitionSuccess:t=>{this.#i(t)},teardown:()=>{this.#p()}}}#o(t){const e=r(t);let a;for(const t of Object.keys(e)){const r=e[t];void 0===r&&this.#r.has(t)?(this.#r.delete(t),a??={...this.#a},delete a[t]):o(t,r)}return a&&(this.#a=Object.freeze(a)),function(t,e){const a=r(e),s={};for(const r in t)Object.hasOwn(t,r)&&void 0!==t[r]&&(s[r]=t[r]);for(const t of Object.keys(a)){const r=a[t];void 0===r?delete s[t]:s[t]=r}return s}(this.#a,e)}#i(t){let r;for(const e of this.#r){const a=t.params[e];Object.hasOwn(t.params,e)&&void 0!==a?(o(e,a),this.#a[e]!==a&&(r??={...this.#a},r[e]=a)):Object.hasOwn(this.#a,e)&&void 0!==this.#a[e]&&(r??={...this.#a},delete r[e])}r&&(this.#a=Object.freeze(r))}#p(){try{this.#s?.(),this.#n?.(),this.#t.setRootPath(this.#e)}catch(t){console.error("persistent-params-plugin","Error during teardown:",t)}}},p=()=>({});function c(r={}){return function(t){if(null==(r=t)||!(Array.isArray(r)?r.every(t=>{if("string"!=typeof t||0===t.length)return!1;try{return n(t),!0}catch{return!1}}):"object"==typeof r&&Object.getPrototypeOf(r)===Object.prototype&&Object.entries(r).every(([t,r])=>{if("string"!=typeof t||0===t.length)return!1;try{n(t)}catch{return!1}return e(r)}))){let r;throw r=null===t?"null":Array.isArray(t)?"array with invalid items":typeof t,new TypeError(`[@real-router/persistent-params-plugin] Invalid params configuration. Expected array of non-empty strings or object with primitive values, got ${r}.`)}var r}(r),Array.isArray(r)&&0===r.length?p:Array.isArray(r)||0!==Object.keys(r).length?e=>{let a;if(Array.isArray(r)){const t={};for(const e of r)t[e]=void 0;a=Object.freeze(t)}else a=Object.freeze({...r});const s=new Set(Array.isArray(r)?[...r]:Object.keys(r)),n=t(e),o=n.getRootPath();return new i(n,a,s,o).getPlugin()}:p}export{c as persistentParamsPluginFactory};//# sourceMappingURL=index.mjs.map
1
+ import{getPluginApi as t}from"@real-router/core";var e="[@real-router/persistent-params-plugin]";function r(t){return"number"==typeof t?Number.isFinite(t):"string"==typeof t||"boolean"==typeof t}var a=/[\s#%&/=?\\]/,s=String.raw`Cannot contain: = & ? # % / \ or whitespace`;function n(t){if(a.test(t))throw new TypeError(`${e} Invalid parameter name "${t}". ${s}`)}function o(t,a){if(null===a)throw new TypeError(`${e} Parameter "${t}" cannot be null. Use undefined to remove the parameter from persistence.`);if(void 0!==a&&!r(a)){const r=Array.isArray(a)?"array":typeof a;throw new TypeError(`${e} Parameter "${t}" must be a primitive value (string, number, or boolean), got ${r}. Objects and arrays are not supported in URL parameters.`)}}var i=class{#t;#e;#r;#a;#s;#n;constructor(t,r,a,s){let n,o;this.#t=t,this.#n=r,this.#e=a,this.#r=s;try{t.setRootPath(`${s}?${[...a].join("&")}`),n=t.addInterceptor("buildPath",(t,e,r)=>t(e,this.#o(r??{}))),o=t.addInterceptor("forwardState",(t,e,r)=>{const a=t(e,r);return{...a,params:this.#o(a.params)}})}catch(r){throw n?.(),o?.(),t.setRootPath(s),new Error(`${e} Failed to initialize: ${r instanceof Error?r.message:String(r)}`,{cause:r})}this.#a=n,this.#s=o}getPlugin(){return{onTransitionSuccess:t=>{this.#i(t)},teardown:()=>{this.#c()}}}#o(t){const e=function(t){const e={};for(const r in t)Object.hasOwn(t,r)&&(e[r]=t[r]);return e}(t);let r;for(const t of Object.keys(e)){const a=e[t];void 0===a&&this.#e.has(t)?(this.#e.delete(t),r??={...this.#n},delete r[t]):o(t,a)}return r&&(this.#n=Object.freeze(r)),function(t,e){const r={};for(const e in t)Object.hasOwn(t,e)&&void 0!==t[e]&&(r[e]=t[e]);for(const t of Object.keys(e)){const a=e[t];void 0===a?delete r[t]:r[t]=a}return r}(this.#n,e)}#i(t){let e;for(const r of this.#e){const a=t.params[r];Object.hasOwn(t.params,r)&&void 0!==a?(o(r,a),this.#n[r]!==a&&(e??={...this.#n},e[r]=a)):Object.hasOwn(this.#n,r)&&void 0!==this.#n[r]&&(e??={...this.#n},delete e[r])}e&&(this.#n=Object.freeze(e))}#c(){this.#a(),this.#s();try{this.#t.setRootPath(this.#r)}catch{}}},c={},p=()=>c;function h(a={}){!function(t){if(null==(a=t)||!(Array.isArray(a)?a.every(t=>{if("string"!=typeof t||0===t.length)return!1;try{return n(t),!0}catch{return!1}}):"object"==typeof a&&Object.getPrototypeOf(a)===Object.prototype&&Object.entries(a).every(([t,e])=>{if("string"!=typeof t||0===t.length)return!1;try{n(t)}catch{return!1}return r(e)}))){let r;throw r=null===t?"null":Array.isArray(t)?"array with invalid items":typeof t,new TypeError(`${e} Invalid params configuration. Expected array of non-empty strings or object with primitive values, got ${r}.`)}var a}(a);const s=Array.isArray(a)?a:Object.keys(a);if(0===s.length)return p;const o={};if(Array.isArray(a))for(const t of a)o[t]=void 0;else Object.assign(o,a);Object.freeze(o);const c=new Set(s);return e=>{const r=t(e);return new i(r,o,new Set(c),r.getRootPath()).getPlugin()}}export{h as persistentParamsPluginFactory};//# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/param-utils.ts","../../src/validation.ts","../../src/plugin.ts","../../src/factory.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// packages/persistent-params-plugin/modules/param-utils.ts\n\nimport type { Params } from \"@real-router/core\";\n\n/**\n * Safely extracts own properties from params object.\n * Uses Object.hasOwn to prevent prototype pollution attacks.\n *\n * @param params - Parameters object (may contain inherited properties)\n * @returns New object with only own properties\n *\n * @example\n * const malicious = Object.create({ __proto__: { admin: true } });\n * malicious.mode = 'dev';\n * const safe = extractOwnParams(malicious); // { mode: 'dev' } (no __proto__)\n */\nexport function extractOwnParams(params: Params): Params {\n const result: Params = {};\n\n for (const key in params) {\n /* v8 ignore next -- @preserve: core validates params prototype — inherited keys never reach here */\n if (Object.hasOwn(params, key)) {\n result[key] = params[key];\n }\n }\n\n return result;\n}\n\n/**\n * Merges persistent and current parameters into a single Params object.\n * Keys explicitly set to `undefined` in current params are removed from result.\n *\n * Creates a new immutable object - does not mutate input parameters.\n *\n * @param persistent - Frozen persistent parameters\n * @param current - Current parameters from navigation\n * @returns New Params object with merged values\n *\n * @example\n * const persistent = { lang: 'en', theme: 'dark' };\n * const current = { theme: 'light', mode: 'dev' };\n * mergeParams(persistent, current); // { lang: 'en', theme: 'light', mode: 'dev' }\n *\n * @example\n * // Removing parameters with undefined\n * const persistent = { lang: 'en', theme: 'dark' };\n * const current = { theme: undefined };\n * mergeParams(persistent, current); // { lang: 'en' } (theme removed)\n */\nexport function mergeParams(\n persistent: Readonly<Params>,\n current: Params,\n): Params {\n // Safely extract own properties from current params\n const safeCurrentParams = extractOwnParams(current);\n\n // Start with persistent params, but EXCLUDE undefined values\n // (undefined values don't appear in URLs, so we shouldn't include them)\n const result: Params = {};\n\n for (const key in persistent) {\n if (Object.hasOwn(persistent, key) && persistent[key] !== undefined) {\n result[key] = persistent[key];\n }\n }\n\n // Apply current params\n for (const key of Object.keys(safeCurrentParams)) {\n const value = safeCurrentParams[key];\n\n if (value === undefined) {\n // Remove param if explicitly set to undefined\n delete result[key];\n } else {\n // Add or update param\n result[key] = value;\n }\n }\n\n return result;\n}\n","// packages/persistent-params-plugin/src/validation.ts\n\nimport { isPrimitiveValue } from \"type-guards\";\n\nimport type { PersistentParamsConfig } from \"./types\";\n\nconst INVALID_PARAM_KEY_REGEX = /[\\s#%&/=?\\\\]/;\nconst INVALID_CHARS_MESSAGE = String.raw`Cannot contain: = & ? # % / \\ or whitespace`;\n\nexport function validateParamKey(key: string): void {\n if (INVALID_PARAM_KEY_REGEX.test(key)) {\n throw new TypeError(\n `[@real-router/persistent-params-plugin] Invalid parameter name \"${key}\". ${INVALID_CHARS_MESSAGE}`,\n );\n }\n}\n\n/**\n * Validates params configuration structure and values.\n * Ensures all parameter names are non-empty strings and all default values are primitives.\n *\n * @param config - Configuration to validate\n * @returns true if configuration is valid\n */\nexport function isValidParamsConfig(\n config: unknown,\n): config is PersistentParamsConfig {\n if (config === null || config === undefined) {\n return false;\n }\n\n // Array configuration: all items must be non-empty strings\n if (Array.isArray(config)) {\n return config.every((item) => {\n if (typeof item !== \"string\" || item.length === 0) {\n return false;\n }\n\n try {\n validateParamKey(item);\n\n return true;\n } catch {\n return false;\n }\n });\n }\n\n // Object configuration: must be plain object with primitive values\n if (typeof config === \"object\") {\n // Reject non-plain objects (Date, Map, etc.)\n if (Object.getPrototypeOf(config) !== Object.prototype) {\n return false;\n }\n\n // All keys must be non-empty strings, all values must be primitives\n return Object.entries(config).every(([key, value]) => {\n // Check key is non-empty string\n if (typeof key !== \"string\" || key.length === 0) {\n return false;\n }\n\n // Validate key doesn't contain special characters\n try {\n validateParamKey(key);\n } catch {\n return false;\n }\n\n // Validate value is primitive (NaN/Infinity already rejected by isPrimitiveValue)\n return isPrimitiveValue(value);\n });\n }\n\n return false;\n}\n\n/**\n * Validates parameter value before persisting.\n * Throws descriptive TypeError if value is not valid for URL parameters.\n *\n * @param key - Parameter name for error messages\n * @param value - Value to validate\n * @throws {TypeError} If value is null, array, object, or other non-primitive type\n */\nexport function validateParamValue(key: string, value: unknown): void {\n if (value === null) {\n throw new TypeError(\n `[@real-router/persistent-params-plugin] Parameter \"${key}\" cannot be null. ` +\n `Use undefined to remove the parameter from persistence.`,\n );\n }\n\n if (value !== undefined && !isPrimitiveValue(value)) {\n const actualType = Array.isArray(value) ? \"array\" : typeof value;\n\n throw new TypeError(\n `[@real-router/persistent-params-plugin] Parameter \"${key}\" must be a primitive value ` +\n `(string, number, or boolean), got ${actualType}. ` +\n `Objects and arrays are not supported in URL parameters.`,\n );\n }\n}\n\n/**\n * Validates the params configuration and throws a descriptive error if invalid.\n *\n * @param params - Configuration to validate\n * @throws {TypeError} If params is not a valid configuration\n */\nexport function validateConfig(params: unknown): void {\n if (!isValidParamsConfig(params)) {\n let actualType: string;\n\n if (params === null) {\n actualType = \"null\";\n } else if (Array.isArray(params)) {\n actualType = \"array with invalid items\";\n } else {\n actualType = typeof params;\n }\n\n throw new TypeError(\n `[@real-router/persistent-params-plugin] Invalid params configuration. ` +\n `Expected array of non-empty strings or object with primitive values, got ${actualType}.`,\n );\n }\n}\n","// packages/persistent-params-plugin/src/plugin.ts\n\nimport { extractOwnParams, mergeParams } from \"./param-utils\";\nimport { validateParamValue } from \"./validation\";\n\nimport type { Params, PluginApi, State, Plugin } from \"@real-router/core\";\n\nexport class PersistentParamsPlugin {\n readonly #api: PluginApi;\n readonly #paramNamesSet: Set<string>;\n readonly #originalRootPath: string;\n\n #persistentParams: Readonly<Params>;\n #removeBuildPathInterceptor: (() => void) | undefined;\n #removeForwardStateInterceptor: (() => void) | undefined;\n\n constructor(\n api: PluginApi,\n persistentParams: Readonly<Params>,\n paramNamesSet: Set<string>,\n originalRootPath: string,\n ) {\n this.#api = api;\n this.#persistentParams = persistentParams;\n this.#paramNamesSet = paramNamesSet;\n this.#originalRootPath = originalRootPath;\n }\n\n getPlugin(): Plugin {\n try {\n const queryString = [...this.#paramNamesSet].join(\"&\");\n\n this.#api.setRootPath(`${this.#originalRootPath}?${queryString}`);\n } /* v8 ignore start -- @preserve: defensive error wrapping for setRootPath failure */ catch (error) {\n throw new Error(\n `[@real-router/persistent-params-plugin] Failed to update root path: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n } /* v8 ignore stop */\n\n this.#removeBuildPathInterceptor = this.#api.addInterceptor(\n \"buildPath\",\n (next, route, navParams) =>\n next(route, this.#withPersistentParams(navParams ?? {})),\n );\n\n this.#removeForwardStateInterceptor = this.#api.addInterceptor(\n \"forwardState\",\n (next, routeName, routeParams) => {\n const result = next(routeName, routeParams);\n\n return {\n ...result,\n params: this.#withPersistentParams(result.params),\n };\n },\n );\n\n return {\n onTransitionSuccess: (toState) => {\n this.#onTransitionSuccess(toState);\n },\n teardown: () => {\n this.#teardown();\n },\n };\n }\n\n #withPersistentParams(additionalParams: Params): Params {\n const safeParams = extractOwnParams(additionalParams);\n let newParams: Params | undefined;\n\n for (const key of Object.keys(safeParams)) {\n const value = safeParams[key];\n\n if (value === undefined && this.#paramNamesSet.has(key)) {\n this.#paramNamesSet.delete(key);\n newParams ??= { ...this.#persistentParams };\n delete newParams[key];\n } else {\n validateParamValue(key, value);\n }\n }\n\n if (newParams) {\n this.#persistentParams = Object.freeze(newParams);\n }\n\n return mergeParams(this.#persistentParams, safeParams);\n }\n\n #onTransitionSuccess(toState: State): void {\n let newParams: Params | undefined;\n\n for (const key of this.#paramNamesSet) {\n const value = toState.params[key];\n\n if (!Object.hasOwn(toState.params, key) || value === undefined) {\n /* v8 ignore next 4 -- @preserve: defensive removal for states committed via navigateToState bypassing forwardState */\n if (\n Object.hasOwn(this.#persistentParams, key) &&\n this.#persistentParams[key] !== undefined\n ) {\n newParams ??= { ...this.#persistentParams };\n delete newParams[key];\n }\n\n continue;\n }\n\n validateParamValue(key, value);\n\n if (this.#persistentParams[key] !== value) {\n newParams ??= { ...this.#persistentParams };\n newParams[key] = value;\n }\n }\n\n if (newParams) {\n this.#persistentParams = Object.freeze(newParams);\n }\n }\n\n #teardown(): void {\n try {\n this.#removeBuildPathInterceptor?.();\n this.#removeForwardStateInterceptor?.();\n this.#api.setRootPath(this.#originalRootPath);\n } /* v8 ignore start -- @preserve: defensive error logging for teardown failure */ catch (error) {\n console.error(\n \"persistent-params-plugin\",\n \"Error during teardown:\",\n error,\n );\n } /* v8 ignore stop */\n }\n}\n","// packages/persistent-params-plugin/modules/factory.ts\n\nimport { getPluginApi } from \"@real-router/core\";\n\nimport { PersistentParamsPlugin } from \"./plugin\";\nimport { validateConfig } from \"./validation\";\n\nimport type { PersistentParamsConfig } from \"./types\";\nimport type { Params, PluginFactory, Plugin } from \"@real-router/core\";\n\nconst noop = () => ({});\n\n/**\n * Factory for the persistent parameters' plugin.\n *\n * This plugin allows you to specify certain route parameters to be persisted across\n * all navigation transitions. Persisted parameters are automatically merged into\n * route parameters when building paths or states.\n *\n * Key features:\n * - Automatic persistence of query parameters across navigations\n * - Support for default values\n * - Type-safe (only primitives: string, number, boolean)\n * - Immutable internal state\n * - Protection against prototype pollution\n * - Full teardown support (can be safely unsubscribed)\n *\n * If a persisted parameter is explicitly set to `undefined` during navigation,\n * it will be removed from the persisted state and omitted from subsequent URLs.\n *\n * The plugin also adjusts the router's root path to include query parameters for\n * all persistent params, ensuring correct URL construction.\n *\n * @param params - Either an array of parameter names (strings) to persist,\n * or an object mapping parameter names to initial values.\n * If an array, initial values will be `undefined`.\n *\n * @returns A PluginFactory that creates the persistent params plugin instance.\n *\n * @example\n * // Persist parameters without default values\n * router.usePlugin(persistentParamsPlugin(['mode', 'lang']));\n *\n * @example\n * // Persist parameters with default values\n * router.usePlugin(persistentParamsPlugin({ mode: 'dev', lang: 'en' }));\n *\n * @example\n * // Removing a persisted parameter\n * router.navigate('route', { mode: undefined }); // mode will be removed\n *\n * @example\n * // Unsubscribing (full cleanup)\n * const unsubscribe = router.usePlugin(persistentParamsPlugin(['mode']));\n * unsubscribe(); // Restores original router state\n *\n * @throws {TypeError} If params is not a valid array of strings or object with primitives\n * @throws {Error} If plugin is already initialized on this router instance\n */\nexport function persistentParamsPluginFactory(\n params: PersistentParamsConfig = {},\n): PluginFactory {\n validateConfig(params);\n\n // Empty configuration - valid but does nothing\n if (Array.isArray(params) && params.length === 0) {\n return noop;\n }\n\n if (!Array.isArray(params) && Object.keys(params).length === 0) {\n return noop;\n }\n\n return (router): Plugin => {\n // Initialize frozen persistent parameters\n let persistentParams: Readonly<Params>;\n\n if (Array.isArray(params)) {\n const initial: Params = {};\n\n for (const param of params) {\n initial[param] = undefined;\n }\n\n persistentParams = Object.freeze(initial);\n } else {\n persistentParams = Object.freeze({ ...params });\n }\n\n const paramNamesSet = new Set<string>(\n Array.isArray(params) ? [...params] : Object.keys(params),\n );\n\n const api = getPluginApi(router);\n const originalRootPath = api.getRootPath();\n\n const plugin = new PersistentParamsPlugin(\n api,\n persistentParams,\n paramNamesSet,\n originalRootPath,\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/param-utils.ts","../../src/validation.ts","../../src/plugin.ts","../../src/factory.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["// packages/persistent-params-plugin/src/constants.ts\n\nexport const LOGGER_CONTEXT = \"persistent-params-plugin\";\n\nexport const ERROR_PREFIX = `[@real-router/${LOGGER_CONTEXT}]`;\n","// packages/persistent-params-plugin/src/param-utils.ts\n\nimport type { Params } from \"@real-router/core\";\n\n/**\n * Safely extracts own properties from params object.\n * Uses Object.hasOwn to prevent prototype pollution attacks.\n *\n * @param params - Parameters object (may contain inherited properties)\n * @returns New object with only own properties\n *\n * @example\n * const malicious = Object.create({ __proto__: { admin: true } });\n * malicious.mode = 'dev';\n * const safe = extractOwnParams(malicious); // { mode: 'dev' } (no __proto__)\n */\nexport function extractOwnParams(params: Params): Params {\n const result: Params = {};\n\n for (const key in params) {\n /* v8 ignore next -- @preserve: core validates params prototype — inherited keys never reach here */\n if (Object.hasOwn(params, key)) {\n result[key] = params[key];\n }\n }\n\n return result;\n}\n\n/**\n * Merges persistent and current parameters into a single Params object.\n *\n * IMPORTANT: `current` must be pre-sanitized via `extractOwnParams()` by the caller.\n * This function does NOT perform prototype pollution protection on its own.\n *\n * @param persistent - Frozen persistent parameters\n * @param current - Pre-sanitized current parameters (own properties only)\n */\nexport function mergeParams(\n persistent: Readonly<Params>,\n current: Params,\n): Params {\n const result: Params = {};\n\n for (const key in persistent) {\n if (Object.hasOwn(persistent, key) && persistent[key] !== undefined) {\n result[key] = persistent[key];\n }\n }\n\n for (const key of Object.keys(current)) {\n const value = current[key];\n\n if (value === undefined) {\n delete result[key];\n } else {\n result[key] = value;\n }\n }\n\n return result;\n}\n","// packages/persistent-params-plugin/src/validation.ts\n\nimport { isPrimitiveValue } from \"type-guards\";\n\nimport { ERROR_PREFIX } from \"./constants\";\n\nimport type { PersistentParamsConfig } from \"./types\";\n\nconst INVALID_PARAM_KEY_REGEX = /[\\s#%&/=?\\\\]/;\nconst INVALID_CHARS_MESSAGE = String.raw`Cannot contain: = & ? # % / \\ or whitespace`;\n\nexport function validateParamKey(key: string): void {\n if (INVALID_PARAM_KEY_REGEX.test(key)) {\n throw new TypeError(\n `${ERROR_PREFIX} Invalid parameter name \"${key}\". ${INVALID_CHARS_MESSAGE}`,\n );\n }\n}\n\n/**\n * Validates params configuration structure and values.\n * Ensures all parameter names are non-empty strings and all default values are primitives.\n *\n * @param config - Configuration to validate\n * @returns true if configuration is valid\n */\nexport function isValidParamsConfig(\n config: unknown,\n): config is PersistentParamsConfig {\n if (config === null || config === undefined) {\n return false;\n }\n\n // Array configuration: all items must be non-empty strings\n if (Array.isArray(config)) {\n return config.every((item) => {\n if (typeof item !== \"string\" || item.length === 0) {\n return false;\n }\n\n try {\n validateParamKey(item);\n\n return true;\n } catch {\n return false;\n }\n });\n }\n\n // Object configuration: must be plain object with primitive values\n if (typeof config === \"object\") {\n // Reject non-plain objects (Date, Map, etc.)\n if (Object.getPrototypeOf(config) !== Object.prototype) {\n return false;\n }\n\n // All keys must be non-empty strings, all values must be primitives\n return Object.entries(config).every(([key, value]) => {\n // Check key is non-empty string\n if (typeof key !== \"string\" || key.length === 0) {\n return false;\n }\n\n // Validate key doesn't contain special characters\n try {\n validateParamKey(key);\n } catch {\n return false;\n }\n\n // Validate value is primitive (NaN/Infinity already rejected by isPrimitiveValue)\n return isPrimitiveValue(value);\n });\n }\n\n return false;\n}\n\n/**\n * Validates parameter value before persisting.\n * Throws descriptive TypeError if value is not valid for URL parameters.\n *\n * @param key - Parameter name for error messages\n * @param value - Value to validate\n * @throws {TypeError} If value is null, array, object, or other non-primitive type\n */\nexport function validateParamValue(key: string, value: unknown): void {\n if (value === null) {\n throw new TypeError(\n `${ERROR_PREFIX} Parameter \"${key}\" cannot be null. ` +\n `Use undefined to remove the parameter from persistence.`,\n );\n }\n\n if (value !== undefined && !isPrimitiveValue(value)) {\n const actualType = Array.isArray(value) ? \"array\" : typeof value;\n\n throw new TypeError(\n `${ERROR_PREFIX} Parameter \"${key}\" must be a primitive value ` +\n `(string, number, or boolean), got ${actualType}. ` +\n `Objects and arrays are not supported in URL parameters.`,\n );\n }\n}\n\n/**\n * Validates the params configuration and throws a descriptive error if invalid.\n *\n * @param params - Configuration to validate\n * @throws {TypeError} If params is not a valid configuration\n */\nexport function validateConfig(params: unknown): void {\n if (!isValidParamsConfig(params)) {\n let actualType: string;\n\n if (params === null) {\n actualType = \"null\";\n } else if (Array.isArray(params)) {\n actualType = \"array with invalid items\";\n } else {\n actualType = typeof params;\n }\n\n throw new TypeError(\n `${ERROR_PREFIX} Invalid params configuration. ` +\n `Expected array of non-empty strings or object with primitive values, got ${actualType}.`,\n );\n }\n}\n","// packages/persistent-params-plugin/src/plugin.ts\n\nimport { ERROR_PREFIX } from \"./constants\";\nimport { extractOwnParams, mergeParams } from \"./param-utils\";\nimport { validateParamValue } from \"./validation\";\n\nimport type { Params, PluginApi, State, Plugin } from \"@real-router/core\";\n\nexport class PersistentParamsPlugin {\n readonly #api: PluginApi;\n readonly #paramNamesSet: Set<string>;\n readonly #originalRootPath: string;\n readonly #removeBuildPathInterceptor: () => void;\n readonly #removeForwardStateInterceptor: () => void;\n\n #persistentParams: Readonly<Params>;\n\n constructor(\n api: PluginApi,\n persistentParams: Readonly<Params>,\n paramNamesSet: Set<string>,\n originalRootPath: string,\n ) {\n this.#api = api;\n this.#persistentParams = persistentParams;\n this.#paramNamesSet = paramNamesSet;\n this.#originalRootPath = originalRootPath;\n\n let removeBuildPath: (() => void) | undefined;\n let removeForwardState: (() => void) | undefined;\n\n try {\n api.setRootPath(`${originalRootPath}?${[...paramNamesSet].join(\"&\")}`);\n\n removeBuildPath = api.addInterceptor(\n \"buildPath\",\n (next, route, navParams) =>\n next(route, this.#withPersistentParams(navParams ?? {})),\n );\n\n removeForwardState = api.addInterceptor(\n \"forwardState\",\n (next, routeName, routeParams) => {\n const result = next(routeName, routeParams);\n\n return {\n ...result,\n params: this.#withPersistentParams(result.params),\n };\n },\n );\n } /* v8 ignore start -- @preserve: rollback on partial initialization failure */ catch (error) {\n removeBuildPath?.();\n removeForwardState?.();\n api.setRootPath(originalRootPath);\n\n throw new Error(\n `${ERROR_PREFIX} Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n } /* v8 ignore stop */\n\n this.#removeBuildPathInterceptor = removeBuildPath;\n this.#removeForwardStateInterceptor = removeForwardState;\n }\n\n getPlugin(): Plugin {\n return {\n onTransitionSuccess: (toState) => {\n this.#onTransitionSuccess(toState);\n },\n teardown: () => {\n this.#teardown();\n },\n };\n }\n\n #withPersistentParams(additionalParams: Params): Params {\n const safeParams = extractOwnParams(additionalParams);\n let newParams: Params | undefined;\n\n for (const key of Object.keys(safeParams)) {\n const value = safeParams[key];\n\n if (value === undefined && this.#paramNamesSet.has(key)) {\n this.#paramNamesSet.delete(key);\n newParams ??= { ...this.#persistentParams };\n delete newParams[key];\n } else {\n validateParamValue(key, value);\n }\n }\n\n if (newParams) {\n this.#persistentParams = Object.freeze(newParams);\n }\n\n return mergeParams(this.#persistentParams, safeParams);\n }\n\n #onTransitionSuccess(toState: State): void {\n let newParams: Params | undefined;\n\n for (const key of this.#paramNamesSet) {\n const value = toState.params[key];\n\n if (!Object.hasOwn(toState.params, key) || value === undefined) {\n /* v8 ignore next 4 -- @preserve: defensive removal for states committed via navigateToState bypassing forwardState */\n if (\n Object.hasOwn(this.#persistentParams, key) &&\n this.#persistentParams[key] !== undefined\n ) {\n newParams ??= { ...this.#persistentParams };\n delete newParams[key];\n }\n\n continue;\n }\n\n validateParamValue(key, value);\n\n if (this.#persistentParams[key] !== value) {\n newParams ??= { ...this.#persistentParams };\n newParams[key] = value;\n }\n }\n\n if (newParams) {\n this.#persistentParams = Object.freeze(newParams);\n }\n }\n\n #teardown(): void {\n this.#removeBuildPathInterceptor();\n this.#removeForwardStateInterceptor();\n\n /* v8 ignore start -- @preserve: setRootPath throws RouterError(ROUTER_DISPOSED) during router.dispose() */\n try {\n this.#api.setRootPath(this.#originalRootPath);\n } catch {\n // Expected during router.dispose(): FSM enters DISPOSED before plugin teardown,\n // so setRootPath's throwIfDisposed() check throws. Restoring rootPath on a\n // destroyed router is unnecessary — swallow silently.\n }\n /* v8 ignore stop */\n }\n}\n","// packages/persistent-params-plugin/src/factory.ts\n\nimport { getPluginApi } from \"@real-router/core\";\n\nimport { PersistentParamsPlugin } from \"./plugin\";\nimport { validateConfig } from \"./validation\";\n\nimport type { PersistentParamsConfig } from \"./types\";\nimport type { Params, PluginFactory, Plugin } from \"@real-router/core\";\n\n// Shared singleton — frozen by core on first use. Do not add properties.\nconst EMPTY_PLUGIN: Plugin = {};\nconst noop: PluginFactory = () => EMPTY_PLUGIN;\n\n/**\n * Factory for the persistent parameters' plugin.\n *\n * This plugin allows you to specify certain route parameters to be persisted across\n * all navigation transitions. Persisted parameters are automatically merged into\n * route parameters when building paths or states.\n *\n * Key features:\n * - Automatic persistence of query parameters across navigations\n * - Support for default values\n * - Type-safe (only primitives: string, number, boolean)\n * - Immutable internal state\n * - Protection against prototype pollution\n * - Full teardown support (can be safely unsubscribed)\n *\n * If a persisted parameter is explicitly set to `undefined` during navigation,\n * it will be removed from the persisted state and omitted from subsequent URLs.\n *\n * The plugin also adjusts the router's root path to include query parameters for\n * all persistent params, ensuring correct URL construction.\n *\n * @param params - Either an array of parameter names (strings) to persist,\n * or an object mapping parameter names to initial values.\n * If an array, initial values will be `undefined`.\n *\n * @returns A PluginFactory that creates the persistent params plugin instance.\n *\n * @example\n * // Persist parameters without default values\n * router.usePlugin(persistentParamsPlugin(['mode', 'lang']));\n *\n * @example\n * // Persist parameters with default values\n * router.usePlugin(persistentParamsPlugin({ mode: 'dev', lang: 'en' }));\n *\n * @example\n * // Removing a persisted parameter\n * router.navigate('route', { mode: undefined }); // mode will be removed\n *\n * @example\n * // Unsubscribing (full cleanup)\n * const unsubscribe = router.usePlugin(persistentParamsPlugin(['mode']));\n * unsubscribe(); // Restores original router state\n *\n * @throws {TypeError} If params is not a valid array of strings or object with primitives\n * @throws {Error} If plugin is already initialized on this router instance\n */\nexport function persistentParamsPluginFactory(\n params: PersistentParamsConfig = {},\n): PluginFactory {\n validateConfig(params);\n\n const paramNames = Array.isArray(params) ? params : Object.keys(params);\n\n if (paramNames.length === 0) {\n return noop;\n }\n\n const initialParams: Params = {};\n\n if (Array.isArray(params)) {\n for (const param of params) {\n initialParams[param] = undefined;\n }\n } else {\n Object.assign(initialParams, params);\n }\n\n Object.freeze(initialParams);\n\n const paramNamesSet = new Set<string>(paramNames);\n\n return (router): Plugin => {\n const api = getPluginApi(router);\n const plugin = new PersistentParamsPlugin(\n api,\n initialParams,\n new Set(paramNamesSet),\n api.getRootPath(),\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
@@ -1 +1 @@
1
- {"inputs":{"src/types.ts":{"bytes":515,"imports":[],"format":"esm"},"src/param-utils.ts":{"bytes":2555,"imports":[],"format":"esm"},"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[],"format":"esm"},"src/validation.ts":{"bytes":3824,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"}],"format":"esm"},"src/plugin.ts":{"bytes":4027,"imports":[{"path":"src/param-utils.ts","kind":"import-statement","original":"./param-utils"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"}],"format":"esm"},"src/factory.ts":{"bytes":3414,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"}],"format":"esm"},"src/index.ts":{"bytes":165,"imports":[{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/factory.ts","kind":"import-statement","original":"./factory"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":19205},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["PersistentParamsConfig","persistentParamsPluginFactory"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":988},"src/param-utils.ts":{"bytesInOutput":679},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":118},"src/validation.ts":{"bytesInOutput":2226},"src/plugin.ts":{"bytesInOutput":3092},"src/index.ts":{"bytesInOutput":0}},"bytes":7507}}}
1
+ {"inputs":{"src/constants.ts":{"bytes":178,"imports":[],"format":"esm"},"src/param-utils.ts":{"bytes":1731,"imports":[],"format":"esm"},"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[],"format":"esm"},"src/validation.ts":{"bytes":3773,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"}],"format":"esm"},"src/plugin.ts":{"bytes":4412,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/param-utils.ts","kind":"import-statement","original":"./param-utils"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"}],"format":"esm"},"src/factory.ts":{"bytes":3227,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"}],"format":"esm"},"src/index.ts":{"bytes":166,"imports":[{"path":"src/factory.ts","kind":"import-statement","original":"./factory"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18658},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["persistentParamsPluginFactory"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":822},"src/constants.ts":{"bytesInOutput":104},"src/param-utils.ts":{"bytesInOutput":604},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":118},"src/validation.ts":{"bytesInOutput":2130},"src/plugin.ts":{"bytesInOutput":3126},"src/index.ts":{"bytesInOutput":0}},"bytes":7437}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/persistent-params-plugin",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "type": "commonjs",
5
5
  "description": "Persist query parameters across route transitions",
6
6
  "main": "./dist/cjs/index.js",
@@ -45,7 +45,7 @@
45
45
  "homepage": "https://github.com/greydragon888/real-router",
46
46
  "sideEffects": false,
47
47
  "dependencies": {
48
- "@real-router/core": "^0.35.0"
48
+ "@real-router/core": "^0.35.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "type-guards": "^0.3.5"
@@ -0,0 +1,5 @@
1
+ // packages/persistent-params-plugin/src/constants.ts
2
+
3
+ export const LOGGER_CONTEXT = "persistent-params-plugin";
4
+
5
+ export const ERROR_PREFIX = `[@real-router/${LOGGER_CONTEXT}]`;
package/src/factory.ts CHANGED
@@ -1,4 +1,4 @@
1
- // packages/persistent-params-plugin/modules/factory.ts
1
+ // packages/persistent-params-plugin/src/factory.ts
2
2
 
3
3
  import { getPluginApi } from "@real-router/core";
4
4
 
@@ -8,7 +8,9 @@ import { validateConfig } from "./validation";
8
8
  import type { PersistentParamsConfig } from "./types";
9
9
  import type { Params, PluginFactory, Plugin } from "@real-router/core";
10
10
 
11
- const noop = () => ({});
11
+ // Shared singleton frozen by core on first use. Do not add properties.
12
+ const EMPTY_PLUGIN: Plugin = {};
13
+ const noop: PluginFactory = () => EMPTY_PLUGIN;
12
14
 
13
15
  /**
14
16
  * Factory for the persistent parameters' plugin.
@@ -62,43 +64,33 @@ export function persistentParamsPluginFactory(
62
64
  ): PluginFactory {
63
65
  validateConfig(params);
64
66
 
65
- // Empty configuration - valid but does nothing
66
- if (Array.isArray(params) && params.length === 0) {
67
- return noop;
68
- }
67
+ const paramNames = Array.isArray(params) ? params : Object.keys(params);
69
68
 
70
- if (!Array.isArray(params) && Object.keys(params).length === 0) {
69
+ if (paramNames.length === 0) {
71
70
  return noop;
72
71
  }
73
72
 
74
- return (router): Plugin => {
75
- // Initialize frozen persistent parameters
76
- let persistentParams: Readonly<Params>;
77
-
78
- if (Array.isArray(params)) {
79
- const initial: Params = {};
80
-
81
- for (const param of params) {
82
- initial[param] = undefined;
83
- }
73
+ const initialParams: Params = {};
84
74
 
85
- persistentParams = Object.freeze(initial);
86
- } else {
87
- persistentParams = Object.freeze({ ...params });
75
+ if (Array.isArray(params)) {
76
+ for (const param of params) {
77
+ initialParams[param] = undefined;
88
78
  }
79
+ } else {
80
+ Object.assign(initialParams, params);
81
+ }
89
82
 
90
- const paramNamesSet = new Set<string>(
91
- Array.isArray(params) ? [...params] : Object.keys(params),
92
- );
83
+ Object.freeze(initialParams);
93
84
 
94
- const api = getPluginApi(router);
95
- const originalRootPath = api.getRootPath();
85
+ const paramNamesSet = new Set<string>(paramNames);
96
86
 
87
+ return (router): Plugin => {
88
+ const api = getPluginApi(router);
97
89
  const plugin = new PersistentParamsPlugin(
98
90
  api,
99
- persistentParams,
100
- paramNamesSet,
101
- originalRootPath,
91
+ initialParams,
92
+ new Set(paramNamesSet),
93
+ api.getRootPath(),
102
94
  );
103
95
 
104
96
  return plugin.getPlugin();
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- // packages/persistent-params-plugin/modules/index.ts
1
+ // packages/persistent-params-plugin/src/index.ts
2
2
 
3
- export { PersistentParamsConfig } from "./types";
3
+ export type { PersistentParamsConfig } from "./types";
4
4
 
5
5
  export { persistentParamsPluginFactory } from "./factory";
@@ -1,4 +1,4 @@
1
- // packages/persistent-params-plugin/modules/param-utils.ts
1
+ // packages/persistent-params-plugin/src/param-utils.ts
2
2
 
3
3
  import type { Params } from "@real-router/core";
4
4
 
@@ -29,34 +29,17 @@ export function extractOwnParams(params: Params): Params {
29
29
 
30
30
  /**
31
31
  * Merges persistent and current parameters into a single Params object.
32
- * Keys explicitly set to `undefined` in current params are removed from result.
33
32
  *
34
- * Creates a new immutable object - does not mutate input parameters.
33
+ * IMPORTANT: `current` must be pre-sanitized via `extractOwnParams()` by the caller.
34
+ * This function does NOT perform prototype pollution protection on its own.
35
35
  *
36
36
  * @param persistent - Frozen persistent parameters
37
- * @param current - Current parameters from navigation
38
- * @returns New Params object with merged values
39
- *
40
- * @example
41
- * const persistent = { lang: 'en', theme: 'dark' };
42
- * const current = { theme: 'light', mode: 'dev' };
43
- * mergeParams(persistent, current); // { lang: 'en', theme: 'light', mode: 'dev' }
44
- *
45
- * @example
46
- * // Removing parameters with undefined
47
- * const persistent = { lang: 'en', theme: 'dark' };
48
- * const current = { theme: undefined };
49
- * mergeParams(persistent, current); // { lang: 'en' } (theme removed)
37
+ * @param current - Pre-sanitized current parameters (own properties only)
50
38
  */
51
39
  export function mergeParams(
52
40
  persistent: Readonly<Params>,
53
41
  current: Params,
54
42
  ): Params {
55
- // Safely extract own properties from current params
56
- const safeCurrentParams = extractOwnParams(current);
57
-
58
- // Start with persistent params, but EXCLUDE undefined values
59
- // (undefined values don't appear in URLs, so we shouldn't include them)
60
43
  const result: Params = {};
61
44
 
62
45
  for (const key in persistent) {
@@ -65,15 +48,12 @@ export function mergeParams(
65
48
  }
66
49
  }
67
50
 
68
- // Apply current params
69
- for (const key of Object.keys(safeCurrentParams)) {
70
- const value = safeCurrentParams[key];
51
+ for (const key of Object.keys(current)) {
52
+ const value = current[key];
71
53
 
72
54
  if (value === undefined) {
73
- // Remove param if explicitly set to undefined
74
55
  delete result[key];
75
56
  } else {
76
- // Add or update param
77
57
  result[key] = value;
78
58
  }
79
59
  }
package/src/plugin.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // packages/persistent-params-plugin/src/plugin.ts
2
2
 
3
+ import { ERROR_PREFIX } from "./constants";
3
4
  import { extractOwnParams, mergeParams } from "./param-utils";
4
5
  import { validateParamValue } from "./validation";
5
6
 
@@ -9,10 +10,10 @@ export class PersistentParamsPlugin {
9
10
  readonly #api: PluginApi;
10
11
  readonly #paramNamesSet: Set<string>;
11
12
  readonly #originalRootPath: string;
13
+ readonly #removeBuildPathInterceptor: () => void;
14
+ readonly #removeForwardStateInterceptor: () => void;
12
15
 
13
16
  #persistentParams: Readonly<Params>;
14
- #removeBuildPathInterceptor: (() => void) | undefined;
15
- #removeForwardStateInterceptor: (() => void) | undefined;
16
17
 
17
18
  constructor(
18
19
  api: PluginApi,
@@ -24,38 +25,46 @@ export class PersistentParamsPlugin {
24
25
  this.#persistentParams = persistentParams;
25
26
  this.#paramNamesSet = paramNamesSet;
26
27
  this.#originalRootPath = originalRootPath;
27
- }
28
28
 
29
- getPlugin(): Plugin {
29
+ let removeBuildPath: (() => void) | undefined;
30
+ let removeForwardState: (() => void) | undefined;
31
+
30
32
  try {
31
- const queryString = [...this.#paramNamesSet].join("&");
33
+ api.setRootPath(`${originalRootPath}?${[...paramNamesSet].join("&")}`);
34
+
35
+ removeBuildPath = api.addInterceptor(
36
+ "buildPath",
37
+ (next, route, navParams) =>
38
+ next(route, this.#withPersistentParams(navParams ?? {})),
39
+ );
40
+
41
+ removeForwardState = api.addInterceptor(
42
+ "forwardState",
43
+ (next, routeName, routeParams) => {
44
+ const result = next(routeName, routeParams);
45
+
46
+ return {
47
+ ...result,
48
+ params: this.#withPersistentParams(result.params),
49
+ };
50
+ },
51
+ );
52
+ } /* v8 ignore start -- @preserve: rollback on partial initialization failure */ catch (error) {
53
+ removeBuildPath?.();
54
+ removeForwardState?.();
55
+ api.setRootPath(originalRootPath);
32
56
 
33
- this.#api.setRootPath(`${this.#originalRootPath}?${queryString}`);
34
- } /* v8 ignore start -- @preserve: defensive error wrapping for setRootPath failure */ catch (error) {
35
57
  throw new Error(
36
- `[@real-router/persistent-params-plugin] Failed to update root path: ${error instanceof Error ? error.message : String(error)}`,
58
+ `${ERROR_PREFIX} Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,
37
59
  { cause: error },
38
60
  );
39
61
  } /* v8 ignore stop */
40
62
 
41
- this.#removeBuildPathInterceptor = this.#api.addInterceptor(
42
- "buildPath",
43
- (next, route, navParams) =>
44
- next(route, this.#withPersistentParams(navParams ?? {})),
45
- );
46
-
47
- this.#removeForwardStateInterceptor = this.#api.addInterceptor(
48
- "forwardState",
49
- (next, routeName, routeParams) => {
50
- const result = next(routeName, routeParams);
51
-
52
- return {
53
- ...result,
54
- params: this.#withPersistentParams(result.params),
55
- };
56
- },
57
- );
63
+ this.#removeBuildPathInterceptor = removeBuildPath;
64
+ this.#removeForwardStateInterceptor = removeForwardState;
65
+ }
58
66
 
67
+ getPlugin(): Plugin {
59
68
  return {
60
69
  onTransitionSuccess: (toState) => {
61
70
  this.#onTransitionSuccess(toState);
@@ -122,16 +131,17 @@ export class PersistentParamsPlugin {
122
131
  }
123
132
 
124
133
  #teardown(): void {
134
+ this.#removeBuildPathInterceptor();
135
+ this.#removeForwardStateInterceptor();
136
+
137
+ /* v8 ignore start -- @preserve: setRootPath throws RouterError(ROUTER_DISPOSED) during router.dispose() */
125
138
  try {
126
- this.#removeBuildPathInterceptor?.();
127
- this.#removeForwardStateInterceptor?.();
128
139
  this.#api.setRootPath(this.#originalRootPath);
129
- } /* v8 ignore start -- @preserve: defensive error logging for teardown failure */ catch (error) {
130
- console.error(
131
- "persistent-params-plugin",
132
- "Error during teardown:",
133
- error,
134
- );
135
- } /* v8 ignore stop */
140
+ } catch {
141
+ // Expected during router.dispose(): FSM enters DISPOSED before plugin teardown,
142
+ // so setRootPath's throwIfDisposed() check throws. Restoring rootPath on a
143
+ // destroyed router is unnecessary — swallow silently.
144
+ }
145
+ /* v8 ignore stop */
136
146
  }
137
147
  }
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- // packages/persistent-params-plugin/modules/types.ts
1
+ // packages/persistent-params-plugin/src/types.ts
2
2
 
3
3
  /**
4
4
  * Configuration for persistent parameters' plugin.
package/src/validation.ts CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  import { isPrimitiveValue } from "type-guards";
4
4
 
5
+ import { ERROR_PREFIX } from "./constants";
6
+
5
7
  import type { PersistentParamsConfig } from "./types";
6
8
 
7
9
  const INVALID_PARAM_KEY_REGEX = /[\s#%&/=?\\]/;
@@ -10,7 +12,7 @@ const INVALID_CHARS_MESSAGE = String.raw`Cannot contain: = & ? # % / \ or whites
10
12
  export function validateParamKey(key: string): void {
11
13
  if (INVALID_PARAM_KEY_REGEX.test(key)) {
12
14
  throw new TypeError(
13
- `[@real-router/persistent-params-plugin] Invalid parameter name "${key}". ${INVALID_CHARS_MESSAGE}`,
15
+ `${ERROR_PREFIX} Invalid parameter name "${key}". ${INVALID_CHARS_MESSAGE}`,
14
16
  );
15
17
  }
16
18
  }
@@ -86,7 +88,7 @@ export function isValidParamsConfig(
86
88
  export function validateParamValue(key: string, value: unknown): void {
87
89
  if (value === null) {
88
90
  throw new TypeError(
89
- `[@real-router/persistent-params-plugin] Parameter "${key}" cannot be null. ` +
91
+ `${ERROR_PREFIX} Parameter "${key}" cannot be null. ` +
90
92
  `Use undefined to remove the parameter from persistence.`,
91
93
  );
92
94
  }
@@ -95,7 +97,7 @@ export function validateParamValue(key: string, value: unknown): void {
95
97
  const actualType = Array.isArray(value) ? "array" : typeof value;
96
98
 
97
99
  throw new TypeError(
98
- `[@real-router/persistent-params-plugin] Parameter "${key}" must be a primitive value ` +
100
+ `${ERROR_PREFIX} Parameter "${key}" must be a primitive value ` +
99
101
  `(string, number, or boolean), got ${actualType}. ` +
100
102
  `Objects and arrays are not supported in URL parameters.`,
101
103
  );
@@ -121,7 +123,7 @@ export function validateConfig(params: unknown): void {
121
123
  }
122
124
 
123
125
  throw new TypeError(
124
- `[@real-router/persistent-params-plugin] Invalid params configuration. ` +
126
+ `${ERROR_PREFIX} Invalid params configuration. ` +
125
127
  `Expected array of non-empty strings or object with primitive values, got ${actualType}.`,
126
128
  );
127
129
  }