@real-router/browser-plugin 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/metafile-cjs.json +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/metafile-esm.json +1 -1
- package/package.json +3 -3
- package/src/plugin.ts +51 -62
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ See [Wiki](https://github.com/greydragon888/real-router/wiki/browser-plugin#3-co
|
|
|
75
75
|
|
|
76
76
|
## Added Router Methods
|
|
77
77
|
|
|
78
|
-
The plugin extends the router with browser-specific methods:
|
|
78
|
+
The plugin extends the router instance with browser-specific methods (via [`extendRouter()`](https://github.com/greydragon888/real-router/wiki/core#extendrouter)):
|
|
79
79
|
|
|
80
80
|
#### `router.buildUrl(name: string, params?: Params): string`
|
|
81
81
|
|
package/dist/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=require("@real-router/core"),t=require("@real-router/logger"),r={forceDeactivate:!0,useHash:!1,hashPrefix:"",base:"",preserveHash:!0},
|
|
1
|
+
var e=require("@real-router/core"),t=require("@real-router/logger"),r={forceDeactivate:!0,useHash:!1,hashPrefix:"",base:"",preserveHash:!0},o="popstate",n="browser-plugin",s=new Map,i=e=>{const t=s.get(e);if(void 0!==t)return t;const r=e.replaceAll(/[$()*+.?[\\\]^{|}-]/g,String.raw`\$&`);return s.set(e,r),r};function a(e,t,r,o){if(r.useHash){const e=i(r.hashPrefix);return(e?t.replace(o.get(`^#${e}`),""):t.slice(1))||"/"}if(r.base){const t=i(r.base),n=e.replace(o.get(`^${t}`),"");return n.startsWith("/")?n:`/${n}`}return e}function h(){const e=new Map;return{get(t){const r=e.get(t);if(void 0!==r)return r;const o=new RegExp(t);return e.set(t,o),o}}}var c=()=>{},u=(e,t)=>{globalThis.history.pushState(e,"",t)},p=(e,t)=>{globalThis.history.replaceState(e,"",t)},l=e=>(globalThis.addEventListener("popstate",e),()=>{globalThis.removeEventListener("popstate",e)}),f=h(),d=e=>(e=>{try{return encodeURI(decodeURI(e))}catch(r){return t.logger.warn(n,`Could not encode path "${e}"`,r),e}})(a(globalThis.location.pathname,globalThis.location.hash,e,f))+globalThis.location.search,m=()=>globalThis.location.hash;var b=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function g(e,t=new WeakSet){if(null==e)return!0;const r=typeof e;if("string"===r||"boolean"===r)return!0;if("number"===r)return Number.isFinite(e);if("function"===r||"symbol"===r)return!1;if(Array.isArray(e))return!t.has(e)&&(t.add(e),e.every(e=>g(e,t)));if("object"===r){if(t.has(e))return!1;t.add(e);const r=Object.getPrototypeOf(e);return(null===r||r===Object.prototype)&&Object.values(e).every(e=>g(e,t))}return!1}function v(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}function y(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;const t=Object.getPrototypeOf(e);if(null!==t&&t!==Object.prototype)return!1;let r=!1;for(const t in e){if(!Object.hasOwn(e,t))continue;const o=e[t];if(!v(o)){const e=typeof o;if("function"===e||"symbol"===e)return!1;r=!0;break}}return!r||g(e)}function w(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||("number"===t?Number.isFinite(e):!!Array.isArray(e)&&e.every(e=>{const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}))}function P(e){if("object"!=typeof e||null===e)return!1;const t=e;return!!function(e){return function(e){return"string"==typeof e&&(""===e||!(e.length>1e4)&&(!!e.startsWith("@@")||b.test(e)))}(e.name)&&"string"==typeof e.path&&y(e.params)}(t)&&(void 0===t.meta||function(e){if("object"!=typeof e||null===e)return!1;const t=e;return!("params"in t&&!function(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;for(const t in e)if(Object.hasOwn(e,t)&&!w(e[t]))return!1;return!0}(t.params)||"id"in t&&"number"!=typeof t.id)}(t.meta))}function S(e,t,r,o){const n={meta:e.meta,name:e.name,params:e.params,path:e.path};r?o.replaceState(n,t):o.pushState(n,t)}var $=class{#e;#t;#r;#o;#n;#s;#i;#a;#h=!1;#c=null;#u;#p;constructor(e,t,r,o,s,i,h){this.#e=e,this.#t=t,this.#r=r,this.#o=o,this.#n=s,this.#i=i,this.#a=h,this.#s=r.useHash?`#${r.hashPrefix}`:"",this.#u=this.#t.addInterceptor("start",(e,t)=>e(t??this.#o.getLocation(this.#r))),this.#p=this.#t.extendRouter({buildUrl:(e,t)=>{return r=this.#e.buildPath(e,t),o=this.#r.base,n=this.#s,o+n+r;var r,o,n},matchUrl:e=>{const t=function(e,t,r){try{const o=new URL(e,globalThis.location.origin);return["http:","https:"].includes(o.protocol)?a(o.pathname,o.hash,t,r)+o.search:(console.warn(`[${n}] Invalid URL protocol in ${e}`),null)}catch(t){return console.warn(`[${n}] Could not parse url ${e}`,t),null}}(e,this.#r,this.#n);return t?this.#t.matchPath(t):void 0},replaceHistoryState:(e,t={})=>{const r=this.#t.buildState(e,t);if(!r)throw new Error(`[real-router] Cannot replace state: route "${e}" is not found`);S(this.#t.makeState(r.name,r.params,this.#e.buildPath(r.name,r.params),{params:r.meta},1),this.#e.buildUrl(e,t),!0,this.#o)}})}getPlugin(){return{onStart:()=>{this.#a.removePopStateListener&&this.#a.removePopStateListener(),this.#a.removePopStateListener=this.#o.addPopstateListener(e=>{this.#l(e)})},onStop:()=>{this.#a.removePopStateListener&&(this.#a.removePopStateListener(),this.#a.removePopStateListener=void 0)},onTransitionSuccess:(e,t,r)=>{const o=(r.replace??!t)||!!r.reload&&this.#e.areStatesEqual(e,t,!1),n=this.#e.buildUrl(e.name,e.params);S(e,!this.#r.preserveHash||t&&t.path!==e.path?n:n+this.#o.getHash(),o,this.#o)},teardown:()=>{this.#a.removePopStateListener&&(this.#a.removePopStateListener(),this.#a.removePopStateListener=void 0),this.#u(),this.#p()}}}#f(){if(this.#c){const e=this.#c;this.#c=null,console.warn(`[${n}] Processing deferred popstate event`),this.#l(e)}}async#l(t){if(this.#h)return console.warn(`[${n}] Transition in progress, deferring popstate event`),void(this.#c=t);this.#h=!0;try{const e=function(e,t,r,o){if(P(e.state))return{name:e.state.name,params:e.state.params};const n=t.matchPath(r.getLocation(o));return n?{name:n.name,params:n.params}:void 0}(t,this.#t,this.#o,this.#r);e?await this.#e.navigate(e.name,e.params,this.#i):await this.#e.navigateToDefault({...this.#i,reload:!0,replace:!0})}catch(t){t instanceof e.RouterError||this.#d(t)}finally{this.#h=!1,this.#f()}}#d(e){console.error(`[${n}] Critical error in onPopState`,e);try{const e=this.#e.getState();if(e){const t=this.#e.buildUrl(e.name,e.params);this.#o.replaceState(e,t)}}catch(e){console.error(`[${n}] Failed to recover from critical error`,e)}}};function x(e,t){return e in t}function L(e,t,r){const o=typeof t;return o===r||void 0===t||(console.warn(`[${n}] Invalid type for '${e}': expected ${r}, got ${o}`),!1)}exports.browserPluginFactory=function(s,i=function(){return void 0!==globalThis.window&&globalThis.history?{pushState:u,replaceState:p,addPopstateListener:l,getLocation:d,getHash:m}:function(){let e=!1;const r=r=>{e||(t.logger.warn(n,`Browser plugin is running in a non-browser environment. Method "${r}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),e=!0)};return{pushState:()=>{r("pushState")},replaceState:()=>{r("replaceState")},addPopstateListener:()=>(r("addPopstateListener"),c),getLocation:()=>(r("getLocation"),""),getHash:()=>(r("getHash"),"")}}()}()){const a=function(e,t){if(!e)return!1;let r=!1;for(const o of Object.keys(e))x(o,t)&&(L(o,e[o],typeof t[o])||(r=!0));if(!0===e.useHash&&"preserveHash"in e&&console.warn(`[${n}] preserveHash ignored in hash mode`),!1===e.useHash&&"hashPrefix"in e){const t=e.hashPrefix;void 0!==t&&""!==t&&console.warn(`[${n}] hashPrefix ignored in history mode`)}return r}(s,r);let f={...r,...s};a&&(console.warn(`[${n}] Using default options due to invalid types`),f={...r}),!0===f.useHash?delete f.preserveHash:delete f.hashPrefix,f.base&&(f.base.startsWith("/")||(f.base=`/${f.base}`),f.base.endsWith("/")&&(f.base=f.base.slice(0,-1)));const b=h(),g=f.forceDeactivate,v=void 0===g?{source:o,replace:!0}:{forceDeactivate:g,source:o,replace:!0},y={removePopStateListener:void 0};return function(t){return new $(t,e.getPluginApi(t),f,i,b,v,y).getPlugin()}},exports.isState=P;//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/browser.ts","../../src/popstate-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["regExpCache","logger","RouterError","defaultOptions","getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/browser-plugin/modules/constants.ts\n\n/**\n * Internal type for default options.\n *\n * Why separate type instead of BrowserPluginOptions?\n *\n * BrowserPluginOptions is a discriminated union:\n * - HashModeOptions: allows hashPrefix, forbids preserveHash (never)\n * - HistoryModeOptions: allows preserveHash, forbids hashPrefix (never)\n *\n * We cannot create a single object of type BrowserPluginOptions that contains\n * BOTH hashPrefix and preserveHash - one will always be 'never' depending on useHash.\n *\n * Example - this would fail TypeScript:\n * const defaults: BrowserPluginOptions = {\n * useHash: false, // → HistoryModeOptions branch\n * preserveHash: true, // ✅ OK\n * hashPrefix: \"\" // ❌ Error: Type 'string' is not assignable to type 'never'\n * };\n *\n * DefaultBrowserPluginOptions solves this by containing ALL options,\n * enabling:\n * - Default values for every option\n * - Type validation via typeof defaultOptions\n * - Runtime validation of user-provided option types\n */\nexport interface DefaultBrowserPluginOptions {\n forceDeactivate: boolean;\n useHash: boolean;\n base: string;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n preserveHash: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n * Used to distinguish browser-initiated navigation (back/forward buttons)\n * from programmatic navigation (router.navigate()).\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"browser-plugin\";\n","// packages/browser-plugin/src/url-utils.ts\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nimport type { URLParseOptions, RegExpCache } from \"./types\";\n\nconst escapeRegExpCache = new Map<string, string>();\n\nexport const escapeRegExp = (str: string): string => {\n const cached = escapeRegExpCache.get(str);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const escaped = str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n\n escapeRegExpCache.set(str, escaped);\n\n return escaped;\n};\n\nexport function extractPath(\n pathname: string,\n hash: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string {\n if (options.useHash) {\n const escapedHashPrefix = escapeRegExp(options.hashPrefix);\n const path = escapedHashPrefix\n ? hash.replace(regExpCache.get(`^#${escapedHashPrefix}`), \"\")\n : hash.slice(1);\n\n return path || \"/\";\n }\n\n if (options.base) {\n const escapedBase = escapeRegExp(options.base);\n const stripped = pathname.replace(regExpCache.get(`^${escapedBase}`), \"\");\n\n return stripped.startsWith(\"/\") ? stripped : `/${stripped}`;\n }\n\n return pathname;\n}\n\nexport function urlToPath(\n url: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${LOGGER_CONTEXT}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return (\n extractPath(parsedUrl.pathname, parsedUrl.hash, options, regExpCache) +\n parsedUrl.search\n );\n } catch (error) {\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n\nexport function buildUrl(path: string, base: string, prefix: string): string {\n return base + prefix + path;\n}\n\nexport function createRegExpCache(): RegExpCache {\n const cache = new Map<string, RegExp>();\n\n return {\n get(pattern: string): RegExp {\n const cached = cache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n cache.set(pattern, newRegExp);\n\n return newRegExp;\n },\n };\n}\n","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { createRegExpCache, extractPath } from \"./url-utils\";\n\nimport type { Browser, BrowserPluginOptions, URLParseOptions } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\nconst pushState = (state: State, path: string) => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nconst replaceState = (state: State, path: string) => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nconst regExpCache = createRegExpCache();\n\n/**\n * Safely encodes/decodes path to normalize URL encoding\n *\n * @param path - Path to normalize\n * @returns Normalized path or original on error\n */\nconst safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n logger.warn(LOGGER_CONTEXT, `Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n\nconst getLocation = (opts: BrowserPluginOptions) => {\n const rawPath = extractPath(\n globalThis.location.pathname,\n globalThis.location.hash,\n opts as URLParseOptions,\n regExpCache,\n );\n\n return safelyEncodePath(rawPath) + globalThis.location.search;\n};\n\n/**\n * Gets current URL hash\n */\nconst getHash = () => globalThis.location.hash;\n\n/**\n * Creates a fallback browser for non-browser environments (SSR).\n * Logs warning on first method call to help diagnose misconfiguration.\n *\n * @returns Browser API with no-op implementations\n */\nfunction createFallbackBrowser(): Browser {\n let hasWarned = false;\n\n const warnOnce = (method: string) => {\n if (!hasWarned) {\n logger.warn(\n LOGGER_CONTEXT,\n `Browser plugin is running in a non-browser environment. ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n}\n\n/**\n * Creates browser API abstraction that works in both browser and SSR environments\n *\n * @returns Browser API object\n */\nexport function createSafeBrowser(): Browser {\n const isBrowser =\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n\n return isBrowser\n ? {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n }\n : createFallbackBrowser();\n}\n","import { isStateStrict as isState } from \"type-guards\";\n\nimport type { BrowserPluginOptions, Browser } from \"./types\";\nimport type { PluginApi, State, Params } from \"@real-router/core\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n options: BrowserPluginOptions,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation(options));\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { getRouteFromEvent, updateBrowserState } from \"./popstate-utils\";\nimport { buildUrl, urlToPath } from \"./url-utils\";\n\nimport type {\n Browser,\n BrowserPluginOptions,\n RegExpCache,\n SharedFactoryState,\n URLParseOptions,\n} from \"./types\";\nimport type {\n NavigationOptions,\n PluginApi,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\n\nexport class BrowserPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: BrowserPluginOptions;\n readonly #browser: Browser;\n readonly #regExpCache: RegExpCache;\n readonly #prefix: string;\n readonly #transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n readonly #shared: SharedFactoryState;\n\n #isTransitioning = false;\n #deferredPopstateEvent: PopStateEvent | null = null;\n readonly #removeStartInterceptor: () => void;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: BrowserPluginOptions,\n browser: Browser,\n regExpCache: RegExpCache,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#browser = browser;\n this.#regExpCache = regExpCache;\n this.#transitionOptions = transitionOptions;\n this.#shared = shared;\n\n const normalizedOptions = options as URLParseOptions;\n\n this.#prefix = options.useHash ? `#${normalizedOptions.hashPrefix}` : \"\";\n\n this.#removeStartInterceptor = this.#api.addInterceptor(\n \"start\",\n (next, path) => next(path ?? this.#browser.getLocation(this.#options)),\n );\n\n this.#augmentRouter();\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n }\n\n this.#shared.removePopStateListener = this.#browser.addPopstateListener(\n (evt: PopStateEvent) => void this.#onPopState(evt),\n );\n },\n\n onStop: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n },\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const shouldReplaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n this.#router.areStatesEqual(toState, fromState, false));\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n const shouldPreserveHash =\n !!this.#options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash\n ? url + this.#browser.getHash()\n : url;\n\n updateBrowserState(\n toState,\n finalUrl,\n shouldReplaceHistory,\n this.#browser,\n );\n },\n\n teardown: () => {\n this.#cleanupAugmentation();\n },\n };\n }\n\n #augmentRouter(): void {\n const router = this.#router;\n\n router.buildUrl = (route, params) => {\n const path = router.buildPath(route, params);\n\n return buildUrl(\n path,\n (this.#options as URLParseOptions).base,\n this.#prefix,\n );\n };\n\n router.matchUrl = (url) => {\n const path = urlToPath(\n url,\n this.#options as URLParseOptions,\n this.#regExpCache,\n );\n\n return path ? this.#api.matchPath(path) : undefined;\n };\n\n router.replaceHistoryState = (name, params = {}) => {\n const state = this.#api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = this.#api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n 1, // forceId\n );\n const url = router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, this.#browser);\n };\n }\n\n #cleanupAugmentation(): void {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n\n this.#removeStartInterceptor();\n\n delete (this.#router as Partial<Router>).buildUrl;\n delete (this.#router as Partial<Router>).matchUrl;\n delete (this.#router as Partial<Router>).replaceHistoryState;\n }\n\n #processDeferredEvent(): void {\n if (this.#deferredPopstateEvent) {\n const event = this.#deferredPopstateEvent;\n\n this.#deferredPopstateEvent = null;\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void this.#onPopState(event);\n }\n }\n\n async #onPopState(evt: PopStateEvent): Promise<void> {\n if (this.#isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n this.#deferredPopstateEvent = evt;\n\n return;\n }\n\n this.#isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(\n evt,\n this.#api,\n this.#browser,\n this.#options,\n );\n\n // eslint-disable-next-line unicorn/prefer-ternary\n if (route) {\n await this.#router.navigate(\n route.name,\n route.params,\n this.#transitionOptions,\n );\n } else {\n await this.#router.navigateToDefault({\n ...this.#transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n this.#recoverFromCriticalError(error);\n }\n } finally {\n this.#isTransitioning = false;\n this.#processDeferredEvent();\n }\n }\n\n #recoverFromCriticalError(error: unknown): void {\n console.error(`[${LOGGER_CONTEXT}] Critical error in onPopState`, error);\n\n try {\n const currentState = this.#router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = this.#router.buildUrl(\n currentState.name,\n currentState.params,\n );\n\n this.#browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n}\n","import { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions } from \"./types\";\n\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\nfunction validateOptionType(\n key: keyof DefaultBrowserPluginOptions,\n value: unknown,\n expectedType: string,\n): boolean {\n const actualType = typeof value;\n\n if (actualType !== expectedType && value !== undefined) {\n console.warn(\n `[${LOGGER_CONTEXT}] Invalid type for '${key}': expected ${expectedType}, got ${actualType}`,\n );\n\n return false;\n }\n\n return true;\n}\n\nexport function validateOptions(\n opts: Partial<BrowserPluginOptions> | undefined,\n defaultOptions: DefaultBrowserPluginOptions,\n): boolean {\n if (!opts) {\n return false;\n }\n\n let hasInvalidTypes = false;\n\n for (const key of Object.keys(opts)) {\n if (isDefaultOptionKey(key, defaultOptions)) {\n const expectedType = typeof defaultOptions[key];\n const value = opts[key];\n const isValid = validateOptionType(key, value, expectedType);\n\n if (!isValid) {\n hasInvalidTypes = true;\n }\n }\n }\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n const optsRecord = opts as unknown as Record<string, unknown>;\n const hashPrefix = optsRecord.hashPrefix;\n\n if (hashPrefix !== undefined && hashPrefix !== \"\") {\n console.warn(`[${LOGGER_CONTEXT}] hashPrefix ignored in history mode`);\n }\n }\n\n return hasInvalidTypes;\n}\n","import { getPluginApi } from \"@real-router/core\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport { BrowserPlugin } from \"./plugin\";\nimport { createRegExpCache } from \"./url-utils\";\nimport { validateOptions } from \"./validation\";\n\nimport type {\n BrowserPluginOptions,\n Browser,\n SharedFactoryState,\n} from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * @param opts - Plugin configuration options\n * @param browser - Browser API abstraction (for testing/SSR)\n * @returns Plugin factory function\n *\n * @example\n * ```ts\n * // Hash routing\n * router.usePlugin(browserPluginFactory({ useHash: true, hashPrefix: \"!\" }));\n *\n * // History routing with hash preservation\n * router.usePlugin(browserPluginFactory({ useHash: false, preserveHash: true }));\n * ```\n */\nexport function browserPluginFactory(\n opts?: Partial<BrowserPluginOptions>,\n browser: Browser = createSafeBrowser(),\n): PluginFactory {\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n if (hasInvalidTypes) {\n console.warn(\n `[${LOGGER_CONTEXT}] Using default options due to invalid types`,\n );\n options = { ...defaultOptions } as BrowserPluginOptions;\n }\n\n if (options.useHash === true) {\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n if (options.base) {\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n const regExpCache = createRegExpCache();\n\n const forceDeactivate = options.forceDeactivate;\n /* v8 ignore next 4 -- @preserve both branches tested, coverage tool limitation */\n const transitionOptions =\n forceDeactivate === undefined\n ? { source, replace: true as const }\n : { forceDeactivate, source, replace: true as const };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function browserPlugin(routerBase) {\n const plugin = new BrowserPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n browser,\n regExpCache,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/browser.ts","../../src/popstate-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["regExpCache","logger","RouterError","defaultOptions","getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/browser-plugin/modules/constants.ts\n\n/**\n * Internal type for default options.\n *\n * Why separate type instead of BrowserPluginOptions?\n *\n * BrowserPluginOptions is a discriminated union:\n * - HashModeOptions: allows hashPrefix, forbids preserveHash (never)\n * - HistoryModeOptions: allows preserveHash, forbids hashPrefix (never)\n *\n * We cannot create a single object of type BrowserPluginOptions that contains\n * BOTH hashPrefix and preserveHash - one will always be 'never' depending on useHash.\n *\n * Example - this would fail TypeScript:\n * const defaults: BrowserPluginOptions = {\n * useHash: false, // → HistoryModeOptions branch\n * preserveHash: true, // ✅ OK\n * hashPrefix: \"\" // ❌ Error: Type 'string' is not assignable to type 'never'\n * };\n *\n * DefaultBrowserPluginOptions solves this by containing ALL options,\n * enabling:\n * - Default values for every option\n * - Type validation via typeof defaultOptions\n * - Runtime validation of user-provided option types\n */\nexport interface DefaultBrowserPluginOptions {\n forceDeactivate: boolean;\n useHash: boolean;\n base: string;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n preserveHash: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n * Used to distinguish browser-initiated navigation (back/forward buttons)\n * from programmatic navigation (router.navigate()).\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"browser-plugin\";\n","// packages/browser-plugin/src/url-utils.ts\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nimport type { URLParseOptions, RegExpCache } from \"./types\";\n\nconst escapeRegExpCache = new Map<string, string>();\n\nexport const escapeRegExp = (str: string): string => {\n const cached = escapeRegExpCache.get(str);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const escaped = str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n\n escapeRegExpCache.set(str, escaped);\n\n return escaped;\n};\n\nexport function extractPath(\n pathname: string,\n hash: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string {\n if (options.useHash) {\n const escapedHashPrefix = escapeRegExp(options.hashPrefix);\n const path = escapedHashPrefix\n ? hash.replace(regExpCache.get(`^#${escapedHashPrefix}`), \"\")\n : hash.slice(1);\n\n return path || \"/\";\n }\n\n if (options.base) {\n const escapedBase = escapeRegExp(options.base);\n const stripped = pathname.replace(regExpCache.get(`^${escapedBase}`), \"\");\n\n return stripped.startsWith(\"/\") ? stripped : `/${stripped}`;\n }\n\n return pathname;\n}\n\nexport function urlToPath(\n url: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${LOGGER_CONTEXT}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return (\n extractPath(parsedUrl.pathname, parsedUrl.hash, options, regExpCache) +\n parsedUrl.search\n );\n } catch (error) {\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n\nexport function buildUrl(path: string, base: string, prefix: string): string {\n return base + prefix + path;\n}\n\nexport function createRegExpCache(): RegExpCache {\n const cache = new Map<string, RegExp>();\n\n return {\n get(pattern: string): RegExp {\n const cached = cache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n cache.set(pattern, newRegExp);\n\n return newRegExp;\n },\n };\n}\n","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { createRegExpCache, extractPath } from \"./url-utils\";\n\nimport type { Browser, BrowserPluginOptions, URLParseOptions } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\nconst pushState = (state: State, path: string) => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nconst replaceState = (state: State, path: string) => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nconst regExpCache = createRegExpCache();\n\n/**\n * Safely encodes/decodes path to normalize URL encoding\n *\n * @param path - Path to normalize\n * @returns Normalized path or original on error\n */\nconst safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n logger.warn(LOGGER_CONTEXT, `Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n\nconst getLocation = (opts: BrowserPluginOptions) => {\n const rawPath = extractPath(\n globalThis.location.pathname,\n globalThis.location.hash,\n opts as URLParseOptions,\n regExpCache,\n );\n\n return safelyEncodePath(rawPath) + globalThis.location.search;\n};\n\n/**\n * Gets current URL hash\n */\nconst getHash = () => globalThis.location.hash;\n\n/**\n * Creates a fallback browser for non-browser environments (SSR).\n * Logs warning on first method call to help diagnose misconfiguration.\n *\n * @returns Browser API with no-op implementations\n */\nfunction createFallbackBrowser(): Browser {\n let hasWarned = false;\n\n const warnOnce = (method: string) => {\n if (!hasWarned) {\n logger.warn(\n LOGGER_CONTEXT,\n `Browser plugin is running in a non-browser environment. ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n}\n\n/**\n * Creates browser API abstraction that works in both browser and SSR environments\n *\n * @returns Browser API object\n */\nexport function createSafeBrowser(): Browser {\n const isBrowser =\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n\n return isBrowser\n ? {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n }\n : createFallbackBrowser();\n}\n","import { isStateStrict as isState } from \"type-guards\";\n\nimport type { BrowserPluginOptions, Browser } from \"./types\";\nimport type { PluginApi, State, Params } from \"@real-router/core\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n options: BrowserPluginOptions,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation(options));\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { getRouteFromEvent, updateBrowserState } from \"./popstate-utils\";\nimport { buildUrl, urlToPath } from \"./url-utils\";\n\nimport type {\n Browser,\n BrowserPluginOptions,\n RegExpCache,\n SharedFactoryState,\n URLParseOptions,\n} from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n PluginApi,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\n\nexport class BrowserPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: BrowserPluginOptions;\n readonly #browser: Browser;\n readonly #regExpCache: RegExpCache;\n readonly #prefix: string;\n readonly #transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n readonly #shared: SharedFactoryState;\n\n #isTransitioning = false;\n #deferredPopstateEvent: PopStateEvent | null = null;\n readonly #removeStartInterceptor: () => void;\n readonly #removeExtensions: () => void;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: BrowserPluginOptions,\n browser: Browser,\n regExpCache: RegExpCache,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#browser = browser;\n this.#regExpCache = regExpCache;\n this.#transitionOptions = transitionOptions;\n this.#shared = shared;\n\n const normalizedOptions = options as URLParseOptions;\n\n this.#prefix = options.useHash ? `#${normalizedOptions.hashPrefix}` : \"\";\n\n this.#removeStartInterceptor = this.#api.addInterceptor(\n \"start\",\n (next, path) => next(path ?? this.#browser.getLocation(this.#options)),\n );\n\n this.#removeExtensions = this.#api.extendRouter({\n buildUrl: (route: string, params?: Params) => {\n const path = this.#router.buildPath(route, params);\n\n return buildUrl(\n path,\n (this.#options as URLParseOptions).base,\n this.#prefix,\n );\n },\n matchUrl: (url: string) => {\n const path = urlToPath(\n url,\n this.#options as URLParseOptions,\n this.#regExpCache,\n );\n\n return path ? this.#api.matchPath(path) : undefined;\n },\n replaceHistoryState: (name: string, params: Params = {}) => {\n const state = this.#api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = this.#api.makeState(\n state.name,\n state.params,\n this.#router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n 1, // forceId\n );\n const url = this.#router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, this.#browser);\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n }\n\n this.#shared.removePopStateListener = this.#browser.addPopstateListener(\n (evt: PopStateEvent) => void this.#onPopState(evt),\n );\n },\n\n onStop: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n },\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const shouldReplaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n this.#router.areStatesEqual(toState, fromState, false));\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n const shouldPreserveHash =\n !!this.#options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash\n ? url + this.#browser.getHash()\n : url;\n\n updateBrowserState(\n toState,\n finalUrl,\n shouldReplaceHistory,\n this.#browser,\n );\n },\n\n teardown: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n\n this.#removeStartInterceptor();\n this.#removeExtensions();\n },\n };\n }\n\n #processDeferredEvent(): void {\n if (this.#deferredPopstateEvent) {\n const event = this.#deferredPopstateEvent;\n\n this.#deferredPopstateEvent = null;\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void this.#onPopState(event);\n }\n }\n\n async #onPopState(evt: PopStateEvent): Promise<void> {\n if (this.#isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n this.#deferredPopstateEvent = evt;\n\n return;\n }\n\n this.#isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(\n evt,\n this.#api,\n this.#browser,\n this.#options,\n );\n\n // eslint-disable-next-line unicorn/prefer-ternary\n if (route) {\n await this.#router.navigate(\n route.name,\n route.params,\n this.#transitionOptions,\n );\n } else {\n await this.#router.navigateToDefault({\n ...this.#transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n this.#recoverFromCriticalError(error);\n }\n } finally {\n this.#isTransitioning = false;\n this.#processDeferredEvent();\n }\n }\n\n #recoverFromCriticalError(error: unknown): void {\n console.error(`[${LOGGER_CONTEXT}] Critical error in onPopState`, error);\n\n try {\n const currentState = this.#router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = this.#router.buildUrl(\n currentState.name,\n currentState.params,\n );\n\n this.#browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n}\n","import { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions } from \"./types\";\n\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\nfunction validateOptionType(\n key: keyof DefaultBrowserPluginOptions,\n value: unknown,\n expectedType: string,\n): boolean {\n const actualType = typeof value;\n\n if (actualType !== expectedType && value !== undefined) {\n console.warn(\n `[${LOGGER_CONTEXT}] Invalid type for '${key}': expected ${expectedType}, got ${actualType}`,\n );\n\n return false;\n }\n\n return true;\n}\n\nexport function validateOptions(\n opts: Partial<BrowserPluginOptions> | undefined,\n defaultOptions: DefaultBrowserPluginOptions,\n): boolean {\n if (!opts) {\n return false;\n }\n\n let hasInvalidTypes = false;\n\n for (const key of Object.keys(opts)) {\n if (isDefaultOptionKey(key, defaultOptions)) {\n const expectedType = typeof defaultOptions[key];\n const value = opts[key];\n const isValid = validateOptionType(key, value, expectedType);\n\n if (!isValid) {\n hasInvalidTypes = true;\n }\n }\n }\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n const optsRecord = opts as unknown as Record<string, unknown>;\n const hashPrefix = optsRecord.hashPrefix;\n\n if (hashPrefix !== undefined && hashPrefix !== \"\") {\n console.warn(`[${LOGGER_CONTEXT}] hashPrefix ignored in history mode`);\n }\n }\n\n return hasInvalidTypes;\n}\n","import { getPluginApi } from \"@real-router/core\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport { BrowserPlugin } from \"./plugin\";\nimport { createRegExpCache } from \"./url-utils\";\nimport { validateOptions } from \"./validation\";\n\nimport type {\n BrowserPluginOptions,\n Browser,\n SharedFactoryState,\n} from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * @param opts - Plugin configuration options\n * @param browser - Browser API abstraction (for testing/SSR)\n * @returns Plugin factory function\n *\n * @example\n * ```ts\n * // Hash routing\n * router.usePlugin(browserPluginFactory({ useHash: true, hashPrefix: \"!\" }));\n *\n * // History routing with hash preservation\n * router.usePlugin(browserPluginFactory({ useHash: false, preserveHash: true }));\n * ```\n */\nexport function browserPluginFactory(\n opts?: Partial<BrowserPluginOptions>,\n browser: Browser = createSafeBrowser(),\n): PluginFactory {\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n if (hasInvalidTypes) {\n console.warn(\n `[${LOGGER_CONTEXT}] Using default options due to invalid types`,\n );\n options = { ...defaultOptions } as BrowserPluginOptions;\n }\n\n if (options.useHash === true) {\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n if (options.base) {\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n const regExpCache = createRegExpCache();\n\n const forceDeactivate = options.forceDeactivate;\n /* v8 ignore next 4 -- @preserve both branches tested, coverage tool limitation */\n const transitionOptions =\n forceDeactivate === undefined\n ? { source, replace: true as const }\n : { forceDeactivate, source, replace: true as const };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function browserPlugin(routerBase) {\n const plugin = new BrowserPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n browser,\n regExpCache,\n transitionOptions,\n shared,\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/constants.ts":{"bytes":1624,"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/url-utils.ts":{"bytes":2202,"imports":[{"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/browser.ts":{"bytes":3076,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"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/popstate-utils.ts":{"bytes":1657,"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":
|
|
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":1624,"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/url-utils.ts":{"bytes":2202,"imports":[{"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/browser.ts":{"bytes":3076,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"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/popstate-utils.ts":{"bytes":1657,"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":6640,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/popstate-utils.ts","kind":"import-statement","original":"./popstate-utils"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"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":1699,"imports":[{"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/factory.ts":{"bytes":2537,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/browser.ts","kind":"import-statement","original":"./browser"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-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/index.ts":{"bytes":1275,"imports":[{"path":"src/factory.ts","kind":"import-statement","original":"./factory"},{"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"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":28827},"dist/cjs/index.js":{"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["browserPluginFactory","isState"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":1257},"src/browser.ts":{"bytesInOutput":1962},"src/constants.ts":{"bytesInOutput":184},"src/url-utils.ts":{"bytesInOutput":1749},"src/plugin.ts":{"bytesInOutput":5184},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2275},"src/popstate-utils.ts":{"bytesInOutput":581},"src/validation.ts":{"bytesInOutput":1259},"src/index.ts":{"bytesInOutput":0}},"bytes":14929}}}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getPluginApi as e,RouterError as t}from"@real-router/core";import{logger as r}from"@real-router/logger";var
|
|
1
|
+
import{getPluginApi as e,RouterError as t}from"@real-router/core";import{logger as r}from"@real-router/logger";var o={forceDeactivate:!0,useHash:!1,hashPrefix:"",base:"",preserveHash:!0},n="popstate",s="browser-plugin",i=new Map,a=e=>{const t=i.get(e);if(void 0!==t)return t;const r=e.replaceAll(/[$()*+.?[\\\]^{|}-]/g,String.raw`\$&`);return i.set(e,r),r};function h(e,t,r,o){if(r.useHash){const e=a(r.hashPrefix);return(e?t.replace(o.get(`^#${e}`),""):t.slice(1))||"/"}if(r.base){const t=a(r.base),n=e.replace(o.get(`^${t}`),"");return n.startsWith("/")?n:`/${n}`}return e}function c(){const e=new Map;return{get(t){const r=e.get(t);if(void 0!==r)return r;const o=new RegExp(t);return e.set(t,o),o}}}var p=()=>{},u=(e,t)=>{globalThis.history.pushState(e,"",t)},l=(e,t)=>{globalThis.history.replaceState(e,"",t)},f=e=>(globalThis.addEventListener("popstate",e),()=>{globalThis.removeEventListener("popstate",e)}),d=c(),m=e=>(e=>{try{return encodeURI(decodeURI(e))}catch(t){return r.warn(s,`Could not encode path "${e}"`,t),e}})(h(globalThis.location.pathname,globalThis.location.hash,e,d))+globalThis.location.search,b=()=>globalThis.location.hash;var v=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function g(e,t=new WeakSet){if(null==e)return!0;const r=typeof e;if("string"===r||"boolean"===r)return!0;if("number"===r)return Number.isFinite(e);if("function"===r||"symbol"===r)return!1;if(Array.isArray(e))return!t.has(e)&&(t.add(e),e.every(e=>g(e,t)));if("object"===r){if(t.has(e))return!1;t.add(e);const r=Object.getPrototypeOf(e);return(null===r||r===Object.prototype)&&Object.values(e).every(e=>g(e,t))}return!1}function y(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}function w(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;const t=Object.getPrototypeOf(e);if(null!==t&&t!==Object.prototype)return!1;let r=!1;for(const t in e){if(!Object.hasOwn(e,t))continue;const o=e[t];if(!y(o)){const e=typeof o;if("function"===e||"symbol"===e)return!1;r=!0;break}}return!r||g(e)}function S(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||("number"===t?Number.isFinite(e):!!Array.isArray(e)&&e.every(e=>{const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}))}function P(e){if("object"!=typeof e||null===e)return!1;const t=e;return!!function(e){return function(e){return"string"==typeof e&&(""===e||!(e.length>1e4)&&(!!e.startsWith("@@")||v.test(e)))}(e.name)&&"string"==typeof e.path&&w(e.params)}(t)&&(void 0===t.meta||function(e){if("object"!=typeof e||null===e)return!1;const t=e;return!("params"in t&&!function(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;for(const t in e)if(Object.hasOwn(e,t)&&!S(e[t]))return!1;return!0}(t.params)||"id"in t&&"number"!=typeof t.id)}(t.meta))}function $(e,t,r,o){const n={meta:e.meta,name:e.name,params:e.params,path:e.path};r?o.replaceState(n,t):o.pushState(n,t)}var L=class{#e;#t;#r;#o;#n;#s;#i;#a;#h=!1;#c=null;#p;#u;constructor(e,t,r,o,n,i,a){this.#e=e,this.#t=t,this.#r=r,this.#o=o,this.#n=n,this.#i=i,this.#a=a,this.#s=r.useHash?`#${r.hashPrefix}`:"",this.#p=this.#t.addInterceptor("start",(e,t)=>e(t??this.#o.getLocation(this.#r))),this.#u=this.#t.extendRouter({buildUrl:(e,t)=>{return r=this.#e.buildPath(e,t),o=this.#r.base,n=this.#s,o+n+r;var r,o,n},matchUrl:e=>{const t=function(e,t,r){try{const o=new URL(e,globalThis.location.origin);return["http:","https:"].includes(o.protocol)?h(o.pathname,o.hash,t,r)+o.search:(console.warn(`[${s}] Invalid URL protocol in ${e}`),null)}catch(t){return console.warn(`[${s}] Could not parse url ${e}`,t),null}}(e,this.#r,this.#n);return t?this.#t.matchPath(t):void 0},replaceHistoryState:(e,t={})=>{const r=this.#t.buildState(e,t);if(!r)throw new Error(`[real-router] Cannot replace state: route "${e}" is not found`);$(this.#t.makeState(r.name,r.params,this.#e.buildPath(r.name,r.params),{params:r.meta},1),this.#e.buildUrl(e,t),!0,this.#o)}})}getPlugin(){return{onStart:()=>{this.#a.removePopStateListener&&this.#a.removePopStateListener(),this.#a.removePopStateListener=this.#o.addPopstateListener(e=>{this.#l(e)})},onStop:()=>{this.#a.removePopStateListener&&(this.#a.removePopStateListener(),this.#a.removePopStateListener=void 0)},onTransitionSuccess:(e,t,r)=>{const o=(r.replace??!t)||!!r.reload&&this.#e.areStatesEqual(e,t,!1),n=this.#e.buildUrl(e.name,e.params);$(e,!this.#r.preserveHash||t&&t.path!==e.path?n:n+this.#o.getHash(),o,this.#o)},teardown:()=>{this.#a.removePopStateListener&&(this.#a.removePopStateListener(),this.#a.removePopStateListener=void 0),this.#p(),this.#u()}}}#f(){if(this.#c){const e=this.#c;this.#c=null,console.warn(`[${s}] Processing deferred popstate event`),this.#l(e)}}async#l(e){if(this.#h)return console.warn(`[${s}] Transition in progress, deferring popstate event`),void(this.#c=e);this.#h=!0;try{const t=function(e,t,r,o){if(P(e.state))return{name:e.state.name,params:e.state.params};const n=t.matchPath(r.getLocation(o));return n?{name:n.name,params:n.params}:void 0}(e,this.#t,this.#o,this.#r);t?await this.#e.navigate(t.name,t.params,this.#i):await this.#e.navigateToDefault({...this.#i,reload:!0,replace:!0})}catch(e){e instanceof t||this.#d(e)}finally{this.#h=!1,this.#f()}}#d(e){console.error(`[${s}] Critical error in onPopState`,e);try{const e=this.#e.getState();if(e){const t=this.#e.buildUrl(e.name,e.params);this.#o.replaceState(e,t)}}catch(e){console.error(`[${s}] Failed to recover from critical error`,e)}}};function x(e,t){return e in t}function E(e,t,r){const o=typeof t;return o===r||void 0===t||(console.warn(`[${s}] Invalid type for '${e}': expected ${r}, got ${o}`),!1)}function T(t,i=function(){return void 0!==globalThis.window&&globalThis.history?{pushState:u,replaceState:l,addPopstateListener:f,getLocation:m,getHash:b}:function(){let e=!1;const t=t=>{e||(r.warn(s,`Browser plugin is running in a non-browser environment. Method "${t}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),e=!0)};return{pushState:()=>{t("pushState")},replaceState:()=>{t("replaceState")},addPopstateListener:()=>(t("addPopstateListener"),p),getLocation:()=>(t("getLocation"),""),getHash:()=>(t("getHash"),"")}}()}()){const a=function(e,t){if(!e)return!1;let r=!1;for(const o of Object.keys(e))x(o,t)&&(E(o,e[o],typeof t[o])||(r=!0));if(!0===e.useHash&&"preserveHash"in e&&console.warn(`[${s}] preserveHash ignored in hash mode`),!1===e.useHash&&"hashPrefix"in e){const t=e.hashPrefix;void 0!==t&&""!==t&&console.warn(`[${s}] hashPrefix ignored in history mode`)}return r}(t,o);let h={...o,...t};a&&(console.warn(`[${s}] Using default options due to invalid types`),h={...o}),!0===h.useHash?delete h.preserveHash:delete h.hashPrefix,h.base&&(h.base.startsWith("/")||(h.base=`/${h.base}`),h.base.endsWith("/")&&(h.base=h.base.slice(0,-1)));const d=c(),v=h.forceDeactivate,g=void 0===v?{source:n,replace:!0}:{forceDeactivate:v,source:n,replace:!0},y={removePopStateListener:void 0};return function(t){return new L(t,e(t),h,i,d,g,y).getPlugin()}}export{T as browserPluginFactory,P as isState};//# sourceMappingURL=index.mjs.map
|
package/dist/esm/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/browser.ts","../../src/popstate-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["regExpCache","defaultOptions"],"mappings":"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","file":"index.mjs","sourcesContent":["// packages/browser-plugin/modules/constants.ts\n\n/**\n * Internal type for default options.\n *\n * Why separate type instead of BrowserPluginOptions?\n *\n * BrowserPluginOptions is a discriminated union:\n * - HashModeOptions: allows hashPrefix, forbids preserveHash (never)\n * - HistoryModeOptions: allows preserveHash, forbids hashPrefix (never)\n *\n * We cannot create a single object of type BrowserPluginOptions that contains\n * BOTH hashPrefix and preserveHash - one will always be 'never' depending on useHash.\n *\n * Example - this would fail TypeScript:\n * const defaults: BrowserPluginOptions = {\n * useHash: false, // → HistoryModeOptions branch\n * preserveHash: true, // ✅ OK\n * hashPrefix: \"\" // ❌ Error: Type 'string' is not assignable to type 'never'\n * };\n *\n * DefaultBrowserPluginOptions solves this by containing ALL options,\n * enabling:\n * - Default values for every option\n * - Type validation via typeof defaultOptions\n * - Runtime validation of user-provided option types\n */\nexport interface DefaultBrowserPluginOptions {\n forceDeactivate: boolean;\n useHash: boolean;\n base: string;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n preserveHash: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n * Used to distinguish browser-initiated navigation (back/forward buttons)\n * from programmatic navigation (router.navigate()).\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"browser-plugin\";\n","// packages/browser-plugin/src/url-utils.ts\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nimport type { URLParseOptions, RegExpCache } from \"./types\";\n\nconst escapeRegExpCache = new Map<string, string>();\n\nexport const escapeRegExp = (str: string): string => {\n const cached = escapeRegExpCache.get(str);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const escaped = str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n\n escapeRegExpCache.set(str, escaped);\n\n return escaped;\n};\n\nexport function extractPath(\n pathname: string,\n hash: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string {\n if (options.useHash) {\n const escapedHashPrefix = escapeRegExp(options.hashPrefix);\n const path = escapedHashPrefix\n ? hash.replace(regExpCache.get(`^#${escapedHashPrefix}`), \"\")\n : hash.slice(1);\n\n return path || \"/\";\n }\n\n if (options.base) {\n const escapedBase = escapeRegExp(options.base);\n const stripped = pathname.replace(regExpCache.get(`^${escapedBase}`), \"\");\n\n return stripped.startsWith(\"/\") ? stripped : `/${stripped}`;\n }\n\n return pathname;\n}\n\nexport function urlToPath(\n url: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${LOGGER_CONTEXT}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return (\n extractPath(parsedUrl.pathname, parsedUrl.hash, options, regExpCache) +\n parsedUrl.search\n );\n } catch (error) {\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n\nexport function buildUrl(path: string, base: string, prefix: string): string {\n return base + prefix + path;\n}\n\nexport function createRegExpCache(): RegExpCache {\n const cache = new Map<string, RegExp>();\n\n return {\n get(pattern: string): RegExp {\n const cached = cache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n cache.set(pattern, newRegExp);\n\n return newRegExp;\n },\n };\n}\n","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { createRegExpCache, extractPath } from \"./url-utils\";\n\nimport type { Browser, BrowserPluginOptions, URLParseOptions } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\nconst pushState = (state: State, path: string) => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nconst replaceState = (state: State, path: string) => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nconst regExpCache = createRegExpCache();\n\n/**\n * Safely encodes/decodes path to normalize URL encoding\n *\n * @param path - Path to normalize\n * @returns Normalized path or original on error\n */\nconst safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n logger.warn(LOGGER_CONTEXT, `Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n\nconst getLocation = (opts: BrowserPluginOptions) => {\n const rawPath = extractPath(\n globalThis.location.pathname,\n globalThis.location.hash,\n opts as URLParseOptions,\n regExpCache,\n );\n\n return safelyEncodePath(rawPath) + globalThis.location.search;\n};\n\n/**\n * Gets current URL hash\n */\nconst getHash = () => globalThis.location.hash;\n\n/**\n * Creates a fallback browser for non-browser environments (SSR).\n * Logs warning on first method call to help diagnose misconfiguration.\n *\n * @returns Browser API with no-op implementations\n */\nfunction createFallbackBrowser(): Browser {\n let hasWarned = false;\n\n const warnOnce = (method: string) => {\n if (!hasWarned) {\n logger.warn(\n LOGGER_CONTEXT,\n `Browser plugin is running in a non-browser environment. ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n}\n\n/**\n * Creates browser API abstraction that works in both browser and SSR environments\n *\n * @returns Browser API object\n */\nexport function createSafeBrowser(): Browser {\n const isBrowser =\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n\n return isBrowser\n ? {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n }\n : createFallbackBrowser();\n}\n","import { isStateStrict as isState } from \"type-guards\";\n\nimport type { BrowserPluginOptions, Browser } from \"./types\";\nimport type { PluginApi, State, Params } from \"@real-router/core\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n options: BrowserPluginOptions,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation(options));\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { getRouteFromEvent, updateBrowserState } from \"./popstate-utils\";\nimport { buildUrl, urlToPath } from \"./url-utils\";\n\nimport type {\n Browser,\n BrowserPluginOptions,\n RegExpCache,\n SharedFactoryState,\n URLParseOptions,\n} from \"./types\";\nimport type {\n NavigationOptions,\n PluginApi,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\n\nexport class BrowserPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: BrowserPluginOptions;\n readonly #browser: Browser;\n readonly #regExpCache: RegExpCache;\n readonly #prefix: string;\n readonly #transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n readonly #shared: SharedFactoryState;\n\n #isTransitioning = false;\n #deferredPopstateEvent: PopStateEvent | null = null;\n readonly #removeStartInterceptor: () => void;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: BrowserPluginOptions,\n browser: Browser,\n regExpCache: RegExpCache,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#browser = browser;\n this.#regExpCache = regExpCache;\n this.#transitionOptions = transitionOptions;\n this.#shared = shared;\n\n const normalizedOptions = options as URLParseOptions;\n\n this.#prefix = options.useHash ? `#${normalizedOptions.hashPrefix}` : \"\";\n\n this.#removeStartInterceptor = this.#api.addInterceptor(\n \"start\",\n (next, path) => next(path ?? this.#browser.getLocation(this.#options)),\n );\n\n this.#augmentRouter();\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n }\n\n this.#shared.removePopStateListener = this.#browser.addPopstateListener(\n (evt: PopStateEvent) => void this.#onPopState(evt),\n );\n },\n\n onStop: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n },\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const shouldReplaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n this.#router.areStatesEqual(toState, fromState, false));\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n const shouldPreserveHash =\n !!this.#options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash\n ? url + this.#browser.getHash()\n : url;\n\n updateBrowserState(\n toState,\n finalUrl,\n shouldReplaceHistory,\n this.#browser,\n );\n },\n\n teardown: () => {\n this.#cleanupAugmentation();\n },\n };\n }\n\n #augmentRouter(): void {\n const router = this.#router;\n\n router.buildUrl = (route, params) => {\n const path = router.buildPath(route, params);\n\n return buildUrl(\n path,\n (this.#options as URLParseOptions).base,\n this.#prefix,\n );\n };\n\n router.matchUrl = (url) => {\n const path = urlToPath(\n url,\n this.#options as URLParseOptions,\n this.#regExpCache,\n );\n\n return path ? this.#api.matchPath(path) : undefined;\n };\n\n router.replaceHistoryState = (name, params = {}) => {\n const state = this.#api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = this.#api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n 1, // forceId\n );\n const url = router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, this.#browser);\n };\n }\n\n #cleanupAugmentation(): void {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n\n this.#removeStartInterceptor();\n\n delete (this.#router as Partial<Router>).buildUrl;\n delete (this.#router as Partial<Router>).matchUrl;\n delete (this.#router as Partial<Router>).replaceHistoryState;\n }\n\n #processDeferredEvent(): void {\n if (this.#deferredPopstateEvent) {\n const event = this.#deferredPopstateEvent;\n\n this.#deferredPopstateEvent = null;\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void this.#onPopState(event);\n }\n }\n\n async #onPopState(evt: PopStateEvent): Promise<void> {\n if (this.#isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n this.#deferredPopstateEvent = evt;\n\n return;\n }\n\n this.#isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(\n evt,\n this.#api,\n this.#browser,\n this.#options,\n );\n\n // eslint-disable-next-line unicorn/prefer-ternary\n if (route) {\n await this.#router.navigate(\n route.name,\n route.params,\n this.#transitionOptions,\n );\n } else {\n await this.#router.navigateToDefault({\n ...this.#transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n this.#recoverFromCriticalError(error);\n }\n } finally {\n this.#isTransitioning = false;\n this.#processDeferredEvent();\n }\n }\n\n #recoverFromCriticalError(error: unknown): void {\n console.error(`[${LOGGER_CONTEXT}] Critical error in onPopState`, error);\n\n try {\n const currentState = this.#router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = this.#router.buildUrl(\n currentState.name,\n currentState.params,\n );\n\n this.#browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n}\n","import { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions } from \"./types\";\n\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\nfunction validateOptionType(\n key: keyof DefaultBrowserPluginOptions,\n value: unknown,\n expectedType: string,\n): boolean {\n const actualType = typeof value;\n\n if (actualType !== expectedType && value !== undefined) {\n console.warn(\n `[${LOGGER_CONTEXT}] Invalid type for '${key}': expected ${expectedType}, got ${actualType}`,\n );\n\n return false;\n }\n\n return true;\n}\n\nexport function validateOptions(\n opts: Partial<BrowserPluginOptions> | undefined,\n defaultOptions: DefaultBrowserPluginOptions,\n): boolean {\n if (!opts) {\n return false;\n }\n\n let hasInvalidTypes = false;\n\n for (const key of Object.keys(opts)) {\n if (isDefaultOptionKey(key, defaultOptions)) {\n const expectedType = typeof defaultOptions[key];\n const value = opts[key];\n const isValid = validateOptionType(key, value, expectedType);\n\n if (!isValid) {\n hasInvalidTypes = true;\n }\n }\n }\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n const optsRecord = opts as unknown as Record<string, unknown>;\n const hashPrefix = optsRecord.hashPrefix;\n\n if (hashPrefix !== undefined && hashPrefix !== \"\") {\n console.warn(`[${LOGGER_CONTEXT}] hashPrefix ignored in history mode`);\n }\n }\n\n return hasInvalidTypes;\n}\n","import { getPluginApi } from \"@real-router/core\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport { BrowserPlugin } from \"./plugin\";\nimport { createRegExpCache } from \"./url-utils\";\nimport { validateOptions } from \"./validation\";\n\nimport type {\n BrowserPluginOptions,\n Browser,\n SharedFactoryState,\n} from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * @param opts - Plugin configuration options\n * @param browser - Browser API abstraction (for testing/SSR)\n * @returns Plugin factory function\n *\n * @example\n * ```ts\n * // Hash routing\n * router.usePlugin(browserPluginFactory({ useHash: true, hashPrefix: \"!\" }));\n *\n * // History routing with hash preservation\n * router.usePlugin(browserPluginFactory({ useHash: false, preserveHash: true }));\n * ```\n */\nexport function browserPluginFactory(\n opts?: Partial<BrowserPluginOptions>,\n browser: Browser = createSafeBrowser(),\n): PluginFactory {\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n if (hasInvalidTypes) {\n console.warn(\n `[${LOGGER_CONTEXT}] Using default options due to invalid types`,\n );\n options = { ...defaultOptions } as BrowserPluginOptions;\n }\n\n if (options.useHash === true) {\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n if (options.base) {\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n const regExpCache = createRegExpCache();\n\n const forceDeactivate = options.forceDeactivate;\n /* v8 ignore next 4 -- @preserve both branches tested, coverage tool limitation */\n const transitionOptions =\n forceDeactivate === undefined\n ? { source, replace: true as const }\n : { forceDeactivate, source, replace: true as const };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function browserPlugin(routerBase) {\n const plugin = new BrowserPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n browser,\n regExpCache,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/browser.ts","../../src/popstate-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["regExpCache","defaultOptions"],"mappings":"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","file":"index.mjs","sourcesContent":["// packages/browser-plugin/modules/constants.ts\n\n/**\n * Internal type for default options.\n *\n * Why separate type instead of BrowserPluginOptions?\n *\n * BrowserPluginOptions is a discriminated union:\n * - HashModeOptions: allows hashPrefix, forbids preserveHash (never)\n * - HistoryModeOptions: allows preserveHash, forbids hashPrefix (never)\n *\n * We cannot create a single object of type BrowserPluginOptions that contains\n * BOTH hashPrefix and preserveHash - one will always be 'never' depending on useHash.\n *\n * Example - this would fail TypeScript:\n * const defaults: BrowserPluginOptions = {\n * useHash: false, // → HistoryModeOptions branch\n * preserveHash: true, // ✅ OK\n * hashPrefix: \"\" // ❌ Error: Type 'string' is not assignable to type 'never'\n * };\n *\n * DefaultBrowserPluginOptions solves this by containing ALL options,\n * enabling:\n * - Default values for every option\n * - Type validation via typeof defaultOptions\n * - Runtime validation of user-provided option types\n */\nexport interface DefaultBrowserPluginOptions {\n forceDeactivate: boolean;\n useHash: boolean;\n base: string;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n preserveHash: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n * Used to distinguish browser-initiated navigation (back/forward buttons)\n * from programmatic navigation (router.navigate()).\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"browser-plugin\";\n","// packages/browser-plugin/src/url-utils.ts\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nimport type { URLParseOptions, RegExpCache } from \"./types\";\n\nconst escapeRegExpCache = new Map<string, string>();\n\nexport const escapeRegExp = (str: string): string => {\n const cached = escapeRegExpCache.get(str);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const escaped = str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n\n escapeRegExpCache.set(str, escaped);\n\n return escaped;\n};\n\nexport function extractPath(\n pathname: string,\n hash: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string {\n if (options.useHash) {\n const escapedHashPrefix = escapeRegExp(options.hashPrefix);\n const path = escapedHashPrefix\n ? hash.replace(regExpCache.get(`^#${escapedHashPrefix}`), \"\")\n : hash.slice(1);\n\n return path || \"/\";\n }\n\n if (options.base) {\n const escapedBase = escapeRegExp(options.base);\n const stripped = pathname.replace(regExpCache.get(`^${escapedBase}`), \"\");\n\n return stripped.startsWith(\"/\") ? stripped : `/${stripped}`;\n }\n\n return pathname;\n}\n\nexport function urlToPath(\n url: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${LOGGER_CONTEXT}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return (\n extractPath(parsedUrl.pathname, parsedUrl.hash, options, regExpCache) +\n parsedUrl.search\n );\n } catch (error) {\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n\nexport function buildUrl(path: string, base: string, prefix: string): string {\n return base + prefix + path;\n}\n\nexport function createRegExpCache(): RegExpCache {\n const cache = new Map<string, RegExp>();\n\n return {\n get(pattern: string): RegExp {\n const cached = cache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n cache.set(pattern, newRegExp);\n\n return newRegExp;\n },\n };\n}\n","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { createRegExpCache, extractPath } from \"./url-utils\";\n\nimport type { Browser, BrowserPluginOptions, URLParseOptions } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\nconst pushState = (state: State, path: string) => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nconst replaceState = (state: State, path: string) => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nconst regExpCache = createRegExpCache();\n\n/**\n * Safely encodes/decodes path to normalize URL encoding\n *\n * @param path - Path to normalize\n * @returns Normalized path or original on error\n */\nconst safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n logger.warn(LOGGER_CONTEXT, `Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n\nconst getLocation = (opts: BrowserPluginOptions) => {\n const rawPath = extractPath(\n globalThis.location.pathname,\n globalThis.location.hash,\n opts as URLParseOptions,\n regExpCache,\n );\n\n return safelyEncodePath(rawPath) + globalThis.location.search;\n};\n\n/**\n * Gets current URL hash\n */\nconst getHash = () => globalThis.location.hash;\n\n/**\n * Creates a fallback browser for non-browser environments (SSR).\n * Logs warning on first method call to help diagnose misconfiguration.\n *\n * @returns Browser API with no-op implementations\n */\nfunction createFallbackBrowser(): Browser {\n let hasWarned = false;\n\n const warnOnce = (method: string) => {\n if (!hasWarned) {\n logger.warn(\n LOGGER_CONTEXT,\n `Browser plugin is running in a non-browser environment. ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n}\n\n/**\n * Creates browser API abstraction that works in both browser and SSR environments\n *\n * @returns Browser API object\n */\nexport function createSafeBrowser(): Browser {\n const isBrowser =\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n\n return isBrowser\n ? {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n }\n : createFallbackBrowser();\n}\n","import { isStateStrict as isState } from \"type-guards\";\n\nimport type { BrowserPluginOptions, Browser } from \"./types\";\nimport type { PluginApi, State, Params } from \"@real-router/core\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n options: BrowserPluginOptions,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation(options));\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { getRouteFromEvent, updateBrowserState } from \"./popstate-utils\";\nimport { buildUrl, urlToPath } from \"./url-utils\";\n\nimport type {\n Browser,\n BrowserPluginOptions,\n RegExpCache,\n SharedFactoryState,\n URLParseOptions,\n} from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n PluginApi,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\n\nexport class BrowserPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: BrowserPluginOptions;\n readonly #browser: Browser;\n readonly #regExpCache: RegExpCache;\n readonly #prefix: string;\n readonly #transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n readonly #shared: SharedFactoryState;\n\n #isTransitioning = false;\n #deferredPopstateEvent: PopStateEvent | null = null;\n readonly #removeStartInterceptor: () => void;\n readonly #removeExtensions: () => void;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: BrowserPluginOptions,\n browser: Browser,\n regExpCache: RegExpCache,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#browser = browser;\n this.#regExpCache = regExpCache;\n this.#transitionOptions = transitionOptions;\n this.#shared = shared;\n\n const normalizedOptions = options as URLParseOptions;\n\n this.#prefix = options.useHash ? `#${normalizedOptions.hashPrefix}` : \"\";\n\n this.#removeStartInterceptor = this.#api.addInterceptor(\n \"start\",\n (next, path) => next(path ?? this.#browser.getLocation(this.#options)),\n );\n\n this.#removeExtensions = this.#api.extendRouter({\n buildUrl: (route: string, params?: Params) => {\n const path = this.#router.buildPath(route, params);\n\n return buildUrl(\n path,\n (this.#options as URLParseOptions).base,\n this.#prefix,\n );\n },\n matchUrl: (url: string) => {\n const path = urlToPath(\n url,\n this.#options as URLParseOptions,\n this.#regExpCache,\n );\n\n return path ? this.#api.matchPath(path) : undefined;\n },\n replaceHistoryState: (name: string, params: Params = {}) => {\n const state = this.#api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = this.#api.makeState(\n state.name,\n state.params,\n this.#router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n 1, // forceId\n );\n const url = this.#router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, this.#browser);\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n }\n\n this.#shared.removePopStateListener = this.#browser.addPopstateListener(\n (evt: PopStateEvent) => void this.#onPopState(evt),\n );\n },\n\n onStop: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n },\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const shouldReplaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n this.#router.areStatesEqual(toState, fromState, false));\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n const shouldPreserveHash =\n !!this.#options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash\n ? url + this.#browser.getHash()\n : url;\n\n updateBrowserState(\n toState,\n finalUrl,\n shouldReplaceHistory,\n this.#browser,\n );\n },\n\n teardown: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n\n this.#removeStartInterceptor();\n this.#removeExtensions();\n },\n };\n }\n\n #processDeferredEvent(): void {\n if (this.#deferredPopstateEvent) {\n const event = this.#deferredPopstateEvent;\n\n this.#deferredPopstateEvent = null;\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void this.#onPopState(event);\n }\n }\n\n async #onPopState(evt: PopStateEvent): Promise<void> {\n if (this.#isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n this.#deferredPopstateEvent = evt;\n\n return;\n }\n\n this.#isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(\n evt,\n this.#api,\n this.#browser,\n this.#options,\n );\n\n // eslint-disable-next-line unicorn/prefer-ternary\n if (route) {\n await this.#router.navigate(\n route.name,\n route.params,\n this.#transitionOptions,\n );\n } else {\n await this.#router.navigateToDefault({\n ...this.#transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n this.#recoverFromCriticalError(error);\n }\n } finally {\n this.#isTransitioning = false;\n this.#processDeferredEvent();\n }\n }\n\n #recoverFromCriticalError(error: unknown): void {\n console.error(`[${LOGGER_CONTEXT}] Critical error in onPopState`, error);\n\n try {\n const currentState = this.#router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = this.#router.buildUrl(\n currentState.name,\n currentState.params,\n );\n\n this.#browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n}\n","import { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions } from \"./types\";\n\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\nfunction validateOptionType(\n key: keyof DefaultBrowserPluginOptions,\n value: unknown,\n expectedType: string,\n): boolean {\n const actualType = typeof value;\n\n if (actualType !== expectedType && value !== undefined) {\n console.warn(\n `[${LOGGER_CONTEXT}] Invalid type for '${key}': expected ${expectedType}, got ${actualType}`,\n );\n\n return false;\n }\n\n return true;\n}\n\nexport function validateOptions(\n opts: Partial<BrowserPluginOptions> | undefined,\n defaultOptions: DefaultBrowserPluginOptions,\n): boolean {\n if (!opts) {\n return false;\n }\n\n let hasInvalidTypes = false;\n\n for (const key of Object.keys(opts)) {\n if (isDefaultOptionKey(key, defaultOptions)) {\n const expectedType = typeof defaultOptions[key];\n const value = opts[key];\n const isValid = validateOptionType(key, value, expectedType);\n\n if (!isValid) {\n hasInvalidTypes = true;\n }\n }\n }\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n const optsRecord = opts as unknown as Record<string, unknown>;\n const hashPrefix = optsRecord.hashPrefix;\n\n if (hashPrefix !== undefined && hashPrefix !== \"\") {\n console.warn(`[${LOGGER_CONTEXT}] hashPrefix ignored in history mode`);\n }\n }\n\n return hasInvalidTypes;\n}\n","import { getPluginApi } from \"@real-router/core\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport { BrowserPlugin } from \"./plugin\";\nimport { createRegExpCache } from \"./url-utils\";\nimport { validateOptions } from \"./validation\";\n\nimport type {\n BrowserPluginOptions,\n Browser,\n SharedFactoryState,\n} from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * @param opts - Plugin configuration options\n * @param browser - Browser API abstraction (for testing/SSR)\n * @returns Plugin factory function\n *\n * @example\n * ```ts\n * // Hash routing\n * router.usePlugin(browserPluginFactory({ useHash: true, hashPrefix: \"!\" }));\n *\n * // History routing with hash preservation\n * router.usePlugin(browserPluginFactory({ useHash: false, preserveHash: true }));\n * ```\n */\nexport function browserPluginFactory(\n opts?: Partial<BrowserPluginOptions>,\n browser: Browser = createSafeBrowser(),\n): PluginFactory {\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n if (hasInvalidTypes) {\n console.warn(\n `[${LOGGER_CONTEXT}] Using default options due to invalid types`,\n );\n options = { ...defaultOptions } as BrowserPluginOptions;\n }\n\n if (options.useHash === true) {\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n if (options.base) {\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n const regExpCache = createRegExpCache();\n\n const forceDeactivate = options.forceDeactivate;\n /* v8 ignore next 4 -- @preserve both branches tested, coverage tool limitation */\n const transitionOptions =\n forceDeactivate === undefined\n ? { source, replace: true as const }\n : { forceDeactivate, source, replace: true as const };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function browserPlugin(routerBase) {\n const plugin = new BrowserPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n browser,\n regExpCache,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"src/constants.ts":{"bytes":1624,"imports":[],"format":"esm"},"src/url-utils.ts":{"bytes":2202,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"}],"format":"esm"},"src/browser.ts":{"bytes":3076,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"}],"format":"esm"},"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[],"format":"esm"},"src/popstate-utils.ts":{"bytes":1657,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"}],"format":"esm"},"src/plugin.ts":{"bytes":
|
|
1
|
+
{"inputs":{"src/constants.ts":{"bytes":1624,"imports":[],"format":"esm"},"src/url-utils.ts":{"bytes":2202,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"}],"format":"esm"},"src/browser.ts":{"bytes":3076,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"}],"format":"esm"},"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[],"format":"esm"},"src/popstate-utils.ts":{"bytes":1657,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"}],"format":"esm"},"src/plugin.ts":{"bytes":6640,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/popstate-utils.ts","kind":"import-statement","original":"./popstate-utils"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"}],"format":"esm"},"src/validation.ts":{"bytes":1699,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"}],"format":"esm"},"src/factory.ts":{"bytes":2537,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/browser.ts","kind":"import-statement","original":"./browser"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"}],"format":"esm"},"src/index.ts":{"bytes":1275,"imports":[{"path":"src/factory.ts","kind":"import-statement","original":"./factory"},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":28827},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["browserPluginFactory","isState"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":1257},"src/browser.ts":{"bytesInOutput":1962},"src/constants.ts":{"bytesInOutput":184},"src/url-utils.ts":{"bytesInOutput":1749},"src/plugin.ts":{"bytesInOutput":5184},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2275},"src/popstate-utils.ts":{"bytesInOutput":581},"src/validation.ts":{"bytesInOutput":1259},"src/index.ts":{"bytesInOutput":0}},"bytes":14929}}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/browser-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "Browser integration plugin with History API, hash routing, and popstate support",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -45,13 +45,13 @@
|
|
|
45
45
|
},
|
|
46
46
|
"sideEffects": false,
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@real-router/core": "^0.
|
|
48
|
+
"@real-router/core": "^0.34.0",
|
|
49
49
|
"@real-router/logger": "^0.2.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@testing-library/jest-dom": "6.9.1",
|
|
53
53
|
"jsdom": "27.4.0",
|
|
54
|
-
"type-guards": "^0.3.
|
|
54
|
+
"type-guards": "^0.3.4"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"test": "vitest",
|
package/src/plugin.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
} from "./types";
|
|
14
14
|
import type {
|
|
15
15
|
NavigationOptions,
|
|
16
|
+
Params,
|
|
16
17
|
PluginApi,
|
|
17
18
|
Router,
|
|
18
19
|
State,
|
|
@@ -36,6 +37,7 @@ export class BrowserPlugin {
|
|
|
36
37
|
#isTransitioning = false;
|
|
37
38
|
#deferredPopstateEvent: PopStateEvent | null = null;
|
|
38
39
|
readonly #removeStartInterceptor: () => void;
|
|
40
|
+
readonly #removeExtensions: () => void;
|
|
39
41
|
|
|
40
42
|
constructor(
|
|
41
43
|
router: Router,
|
|
@@ -67,7 +69,48 @@ export class BrowserPlugin {
|
|
|
67
69
|
(next, path) => next(path ?? this.#browser.getLocation(this.#options)),
|
|
68
70
|
);
|
|
69
71
|
|
|
70
|
-
this.#
|
|
72
|
+
this.#removeExtensions = this.#api.extendRouter({
|
|
73
|
+
buildUrl: (route: string, params?: Params) => {
|
|
74
|
+
const path = this.#router.buildPath(route, params);
|
|
75
|
+
|
|
76
|
+
return buildUrl(
|
|
77
|
+
path,
|
|
78
|
+
(this.#options as URLParseOptions).base,
|
|
79
|
+
this.#prefix,
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
matchUrl: (url: string) => {
|
|
83
|
+
const path = urlToPath(
|
|
84
|
+
url,
|
|
85
|
+
this.#options as URLParseOptions,
|
|
86
|
+
this.#regExpCache,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return path ? this.#api.matchPath(path) : undefined;
|
|
90
|
+
},
|
|
91
|
+
replaceHistoryState: (name: string, params: Params = {}) => {
|
|
92
|
+
const state = this.#api.buildState(name, params);
|
|
93
|
+
|
|
94
|
+
if (!state) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`[real-router] Cannot replace state: route "${name}" is not found`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const builtState = this.#api.makeState(
|
|
101
|
+
state.name,
|
|
102
|
+
state.params,
|
|
103
|
+
this.#router.buildPath(state.name, state.params),
|
|
104
|
+
{
|
|
105
|
+
params: state.meta,
|
|
106
|
+
},
|
|
107
|
+
1, // forceId
|
|
108
|
+
);
|
|
109
|
+
const url = this.#router.buildUrl(name, params);
|
|
110
|
+
|
|
111
|
+
updateBrowserState(builtState, url, true, this.#browser);
|
|
112
|
+
},
|
|
113
|
+
});
|
|
71
114
|
}
|
|
72
115
|
|
|
73
116
|
getPlugin(): Plugin {
|
|
@@ -118,71 +161,17 @@ export class BrowserPlugin {
|
|
|
118
161
|
},
|
|
119
162
|
|
|
120
163
|
teardown: () => {
|
|
121
|
-
this.#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
#augmentRouter(): void {
|
|
127
|
-
const router = this.#router;
|
|
128
|
-
|
|
129
|
-
router.buildUrl = (route, params) => {
|
|
130
|
-
const path = router.buildPath(route, params);
|
|
131
|
-
|
|
132
|
-
return buildUrl(
|
|
133
|
-
path,
|
|
134
|
-
(this.#options as URLParseOptions).base,
|
|
135
|
-
this.#prefix,
|
|
136
|
-
);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
router.matchUrl = (url) => {
|
|
140
|
-
const path = urlToPath(
|
|
141
|
-
url,
|
|
142
|
-
this.#options as URLParseOptions,
|
|
143
|
-
this.#regExpCache,
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
return path ? this.#api.matchPath(path) : undefined;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
router.replaceHistoryState = (name, params = {}) => {
|
|
150
|
-
const state = this.#api.buildState(name, params);
|
|
151
|
-
|
|
152
|
-
if (!state) {
|
|
153
|
-
throw new Error(
|
|
154
|
-
`[real-router] Cannot replace state: route "${name}" is not found`,
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const builtState = this.#api.makeState(
|
|
159
|
-
state.name,
|
|
160
|
-
state.params,
|
|
161
|
-
router.buildPath(state.name, state.params),
|
|
162
|
-
{
|
|
163
|
-
params: state.meta,
|
|
164
|
-
},
|
|
165
|
-
1, // forceId
|
|
166
|
-
);
|
|
167
|
-
const url = router.buildUrl(name, params);
|
|
164
|
+
if (this.#shared.removePopStateListener) {
|
|
165
|
+
this.#shared.removePopStateListener();
|
|
166
|
+
this.#shared.removePopStateListener = undefined;
|
|
167
|
+
}
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
this.#removeStartInterceptor();
|
|
170
|
+
this.#removeExtensions();
|
|
171
|
+
},
|
|
170
172
|
};
|
|
171
173
|
}
|
|
172
174
|
|
|
173
|
-
#cleanupAugmentation(): void {
|
|
174
|
-
if (this.#shared.removePopStateListener) {
|
|
175
|
-
this.#shared.removePopStateListener();
|
|
176
|
-
this.#shared.removePopStateListener = undefined;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
this.#removeStartInterceptor();
|
|
180
|
-
|
|
181
|
-
delete (this.#router as Partial<Router>).buildUrl;
|
|
182
|
-
delete (this.#router as Partial<Router>).matchUrl;
|
|
183
|
-
delete (this.#router as Partial<Router>).replaceHistoryState;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
175
|
#processDeferredEvent(): void {
|
|
187
176
|
if (this.#deferredPopstateEvent) {
|
|
188
177
|
const event = this.#deferredPopstateEvent;
|