@real-router/browser-plugin 0.5.2 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var e=require("@real-router/logger"),t=require("@real-router/core"),r=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function n(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=>n(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=>n(e,t))}return!1}function a(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}function o(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 n=e[t];if(!a(n)){const e=typeof n;if("function"===e||"symbol"===e)return!1;r=!0;break}}return!r||n(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 i(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)||"options"in t&&"object"!=typeof t.options||"id"in t&&"number"!=typeof t.id)}function l(e){return function(e){return"string"==typeof e&&(""===e||!(e.length>1e4)&&(!!e.startsWith("@@")||r.test(e)))}(e.name)&&"string"==typeof e.path&&o(e.params)}function c(e){if("object"!=typeof e||null===e)return!1;const t=e;return!!l(t)&&(void 0===t.meta||i(t.meta))}function u(e){if("object"!=typeof e||null===e)return!1;const t=e;return!!l(t)&&"meta"in t&&i(t.meta)}var h={forceDeactivate:!0,useHash:!1,hashPrefix:"",base:"",mergeState:!1,preserveHash:!0},p="popstate",f="browser-plugin",g=new Map,d=e=>{const t=g.get(e);if(void 0!==t)return t;const r=e.replaceAll(/[$()*+.?[\\\]^{|}-]/g,String.raw`\$&`);return g.set(e,r),r};function b(e,t,r,n,a){const o={meta:e.meta,name:e.name,params:e.params,path:e.path},s=a.mergeState&&n.getState()?{...n.getState(),...o}:o;r?n.replaceState(s,"",t):n.pushState(s,"",t)}function m(e,r,n,a,o,s,i){e&&e.code===t.errorCodes.CANNOT_DEACTIVATE&&r&&n&&!a&&b(n,o.buildUrl(n.name,n.params),!0,s,i)}function y(e,t){return e in t}function v(e,t,r){const n=typeof t;return n===r||void 0===t||(console.warn(`[${f}] Invalid type for '${e}': expected ${r}, got ${n}`),!1)}var S=()=>{},w=()=>globalThis.location.pathname,T=(()=>{let e,t=!1;return()=>{const r=globalThis.navigator.userAgent;return r!==e&&(e=r,t=!r.includes("Trident")),t}})(),$=(e,t,r)=>{globalThis.history.pushState(e,t??"",r)},H=(e,t,r)=>{globalThis.history.replaceState(e,t??"",r)},P=(e,t)=>{const r=t.useHash&&!T();return globalThis.addEventListener("popstate",e),r&&globalThis.addEventListener("hashchange",e),()=>{globalThis.removeEventListener("popstate",e),r&&globalThis.removeEventListener("hashchange",e)}},j=(()=>{const e=new Map;return t=>{let r=e.get(t);return r||(r=new RegExp(t),e.set(t,r)),r}})(),O=e=>{const{useHash:t,hashPrefix:r="",base:n=""}=e;if(!r&&!n){const e=t?globalThis.location.hash.slice(1):globalThis.location.pathname;return(x(e)||"/")+globalThis.location.search}const a=d(r),o=d(n),s=t?globalThis.location.hash.replace(j(`^#${a}`),""):globalThis.location.pathname.replace(j(`^${o}`),"");return(x(s)||"/")+globalThis.location.search},x=t=>{try{return encodeURI(decodeURI(t))}catch(r){return e.logger.warn(f,`Could not encode path "${t}"`,r),t}},A=()=>{if(globalThis.history.state){if(u(globalThis.history.state))return globalThis.history.state;e.logger.warn(f,"History state is not a valid state object, ignoring",globalThis.history.state)}},L=()=>globalThis.location.hash;exports.browserPluginFactory=function(t,r=function(){return void 0!==globalThis.window&&globalThis.history?{getBase:w,pushState:$,replaceState:H,addPopstateListener:P,getLocation:O,getState:A,getHash:L}:function(){let t=!1;const r=r=>{t||(e.logger.warn(f,`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.`),t=!0)};return{getBase:()=>(r("getBase"),""),pushState:()=>{r("pushState")},replaceState:()=>{r("replaceState")},addPopstateListener:()=>(r("addPopstateListener"),S),getLocation:()=>(r("getLocation"),""),getState:()=>{r("getState")},getHash:()=>(r("getHash"),"")}}()}()){const n=function(e,t){if(!e)return!1;let r=!1;for(const n of Object.keys(e))y(n,t)&&(v(n,e[n],typeof t[n])||(r=!0));if(!0===e.useHash&&"preserveHash"in e&&console.warn(`[${f}] preserveHash ignored in hash mode`),!1===e.useHash&&"hashPrefix"in e){const t=e.hashPrefix;void 0!==t&&""!==t&&console.warn(`[${f}] hashPrefix ignored in history mode`)}return r}(t,h);let a={...h,...t};n&&(console.warn(`[${f}] Using default options due to invalid types`),a={...h}),!0===a.useHash?delete a.preserveHash:delete a.hashPrefix,a.base&&"string"==typeof a.base&&(a.base.startsWith("/")||(a.base=`/${a.base}`),a.base.endsWith("/")&&(a.base=a.base.slice(0,-1)));const o=new Map,s=e=>{const t=o.get(e);if(void 0!==t)return t;const r=new RegExp(e);return o.set(e,r),r},i=a.forceDeactivate,l=void 0===i?{source:p,replace:!0}:{forceDeactivate:i,source:p,replace:!0};let u;return function(e){const t=e.start;let n,o=!1,i=null;const h=()=>a.base??"",p=a.hashPrefix??"",g=d(p),y=a.useHash?`#${p}`:"",v=g?s(`^#${g}`):null;function S(){if(i){const e=i;i=null,console.warn(`[${f}] Processing deferred popstate event`),w(e)}}async function w(t){if(o)return console.warn(`[${f}] Transition in progress, deferring popstate event`),void(i=t);try{const n=e.getState(),s=function(e,t,r,n){return c(e.state)?t.makeState(e.state.name,e.state.params,e.state.path,{...e.state.meta,params:e.state.meta?.params??{},options:e.state.meta?.options??{}},e.state.meta?.id):t.matchPath(r.getLocation(n))}(t,e,r,a),i=!c(t.state);if(!s&&function(e,t){const r=e.getOptions(),{defaultRoute:n}=r;return!!n&&(e.navigateToDefault({...t,reload:!0,replace:!0}),!0)}(e,l))return;if(function(e,t,r){return!e||!(!t||!r.areStatesEqual(e,t,!1))}(s,n,e))return;if(!s)return;o=!0;try{m(void 0,await e.navigateToState(s,n,l),n,i,e,r,a)}catch(t){m(t,void 0,n,i,e,r,a)}finally{o=!1,S()}}catch(t){o=!1,console.error(`[${f}] Critical error in onPopState`,t);try{const t=e.getState();if(t){const n=e.buildUrl(t.name,t.params);r.replaceState(t,"",n)}}catch(e){console.error(`[${f}] Failed to recover from critical error`,e)}S()}}return e.start=async e=>t(e??r.getLocation(a)),e.buildUrl=(t,r)=>{const n=e.buildPath(t,r);return h()+y+n},e.matchUrl=t=>{const r=(e=>{try{const t=new URL(e,globalThis.location.origin),r=t.pathname,n=t.hash,o=t.search,i=h();if(!["http:","https:"].includes(t.protocol))return console.warn(`[${f}] Invalid URL protocol in ${e}`),null;if(a.useHash)return(v?n.replace(v,""):n.slice(1))+o;if(i){const e=d(i),t=s(`^${e}`),n=r.replace(t,"");return(n.startsWith("/")?"":"/")+n+o}return r+o}catch(t){return console.warn(`[${f}] Could not parse url ${e}`,t),null}})(t);return r?e.matchPath(r):void 0},e.replaceHistoryState=(t,n={})=>{const o=e.buildState(t,n);if(!o)throw new Error(`[real-router] Cannot replace state: route "${t}" is not found`);b(e.makeState(o.name,o.params,e.buildPath(o.name,o.params),{params:o.meta,options:{}},1),e.buildUrl(t,n),!0,r,a)},Object.defineProperty(e,"lastKnownState",{get:()=>n,set(e){n=e?Object.freeze({...e}):void 0},enumerable:!0,configurable:!0}),{onStart:()=>{u&&u(),u=r.addPopstateListener(e=>{w(e)},a)},onStop:()=>{u&&(u(),u=void 0)},onTransitionSuccess:(t,n,o)=>{e.lastKnownState=t;const s=(o.replace??!n)||!!o.reload&&e.areStatesEqual(t,n,!1),i=e.buildUrl(t.name,t.params);b(t,!a.preserveHash||n&&n.path!==t.path?i:i+r.getHash(),s,r,a)},teardown:()=>{u&&(u(),u=void 0),e.start=t,delete e.buildUrl,delete e.matchUrl,delete e.replaceHistoryState,delete e.lastKnownState}}}},exports.isHistoryState=u,exports.isState=c;//# sourceMappingURL=index.js.map
1
+ var e=require("@real-router/core"),t=require("@real-router/logger"),r=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function n(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=>n(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=>n(e,t))}return!1}function a(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}function o(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 n=e[t];if(!a(n)){const e=typeof n;if("function"===e||"symbol"===e)return!1;r=!0;break}}return!r||n(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 i(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)||"options"in t&&"object"!=typeof t.options||"id"in t&&"number"!=typeof t.id)}function l(e){return function(e){return"string"==typeof e&&(""===e||!(e.length>1e4)&&(!!e.startsWith("@@")||r.test(e)))}(e.name)&&"string"==typeof e.path&&o(e.params)}function c(e){if("object"!=typeof e||null===e)return!1;const t=e;return!!l(t)&&(void 0===t.meta||i(t.meta))}function u(e){if("object"!=typeof e||null===e)return!1;const t=e;return!!l(t)&&"meta"in t&&i(t.meta)}var h={forceDeactivate:!0,useHash:!1,hashPrefix:"",base:"",mergeState:!1,preserveHash:!0},p="popstate",f="browser-plugin",g=new Map,d=e=>{const t=g.get(e);if(void 0!==t)return t;const r=e.replaceAll(/[$()*+.?[\\\]^{|}-]/g,String.raw`\$&`);return g.set(e,r),r};function b(e,t,r,n,a){const o={meta:e.meta,name:e.name,params:e.params,path:e.path},s=a.mergeState&&n.getState()?{...n.getState(),...o}:o;r?n.replaceState(s,"",t):n.pushState(s,"",t)}function m(t,r,n,a,o,s,i){t&&t.code===e.errorCodes.CANNOT_DEACTIVATE&&r&&n&&!a&&b(n,o.buildUrl(n.name,n.params),!0,s,i)}function y(e,t){return e in t}function v(e,t,r){const n=typeof t;return n===r||void 0===t||(console.warn(`[${f}] Invalid type for '${e}': expected ${r}, got ${n}`),!1)}var S=()=>{},w=()=>globalThis.location.pathname,T=(()=>{let e,t=!1;return()=>{const r=globalThis.navigator.userAgent;return r!==e&&(e=r,t=!r.includes("Trident")),t}})(),$=(e,t,r)=>{globalThis.history.pushState(e,t??"",r)},P=(e,t,r)=>{globalThis.history.replaceState(e,t??"",r)},H=(e,t)=>{const r=t.useHash&&!T();return globalThis.addEventListener("popstate",e),r&&globalThis.addEventListener("hashchange",e),()=>{globalThis.removeEventListener("popstate",e),r&&globalThis.removeEventListener("hashchange",e)}},j=(()=>{const e=new Map;return t=>{let r=e.get(t);return r||(r=new RegExp(t),e.set(t,r)),r}})(),A=e=>{const{useHash:t,hashPrefix:r="",base:n=""}=e;if(!r&&!n){const e=t?globalThis.location.hash.slice(1):globalThis.location.pathname;return(O(e)||"/")+globalThis.location.search}const a=d(r),o=d(n),s=t?globalThis.location.hash.replace(j(`^#${a}`),""):globalThis.location.pathname.replace(j(`^${o}`),"");return(O(s)||"/")+globalThis.location.search},O=e=>{try{return encodeURI(decodeURI(e))}catch(r){return t.logger.warn(f,`Could not encode path "${e}"`,r),e}},x=()=>{if(globalThis.history.state){if(u(globalThis.history.state))return globalThis.history.state;t.logger.warn(f,"History state is not a valid state object, ignoring",globalThis.history.state)}},L=()=>globalThis.location.hash;exports.browserPluginFactory=function(r,n=function(){return void 0!==globalThis.window&&globalThis.history?{getBase:w,pushState:$,replaceState:P,addPopstateListener:H,getLocation:A,getState:x,getHash:L}:function(){let e=!1;const r=r=>{e||(t.logger.warn(f,`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{getBase:()=>(r("getBase"),""),pushState:()=>{r("pushState")},replaceState:()=>{r("replaceState")},addPopstateListener:()=>(r("addPopstateListener"),S),getLocation:()=>(r("getLocation"),""),getState:()=>{r("getState")},getHash:()=>(r("getHash"),"")}}()}()){const a=function(e,t){if(!e)return!1;let r=!1;for(const n of Object.keys(e))y(n,t)&&(v(n,e[n],typeof t[n])||(r=!0));if(!0===e.useHash&&"preserveHash"in e&&console.warn(`[${f}] preserveHash ignored in hash mode`),!1===e.useHash&&"hashPrefix"in e){const t=e.hashPrefix;void 0!==t&&""!==t&&console.warn(`[${f}] hashPrefix ignored in history mode`)}return r}(r,h);let o={...h,...r};a&&(console.warn(`[${f}] Using default options due to invalid types`),o={...h}),!0===o.useHash?delete o.preserveHash:delete o.hashPrefix,o.base&&"string"==typeof o.base&&(o.base.startsWith("/")||(o.base=`/${o.base}`),o.base.endsWith("/")&&(o.base=o.base.slice(0,-1)));const s=new Map,i=e=>{const t=s.get(e);if(void 0!==t)return t;const r=new RegExp(e);return s.set(e,r),r},l=o.forceDeactivate,u=void 0===l?{source:p,replace:!0}:{forceDeactivate:l,source:p,replace:!0};let g;return function(t){const r=t,a=e.getPluginApi(t),s=r.start;let l,h=!1,p=null;const y=()=>o.base??"",v=o.hashPrefix??"",S=d(v),w=o.useHash?`#${v}`:"",T=S?i(`^#${S}`):null;function $(){if(p){const e=p;p=null,console.warn(`[${f}] Processing deferred popstate event`),P(e)}}async function P(e){if(h)return console.warn(`[${f}] Transition in progress, deferring popstate event`),void(p=e);try{const t=r.getState(),s=function(e,t,r,n){return c(e.state)?t.makeState(e.state.name,e.state.params,e.state.path,{...e.state.meta,params:e.state.meta?.params??{},options:e.state.meta?.options??{}},e.state.meta?.id):t.matchPath(r.getLocation(n))}(e,a,n,o),i=!c(e.state);if(!s&&function(e,t,r){const n=t.getOptions(),{defaultRoute:a}=n;return!!a&&(e.navigateToDefault({...r,reload:!0,replace:!0}),!0)}(r,a,u))return;if(function(e,t,r){return!e||!(!t||!r.areStatesEqual(e,t,!1))}(s,t,r))return;if(!s)return;h=!0;try{m(void 0,await a.navigateToState(s,t,u),t,i,r,n,o)}catch(e){m(e,void 0,t,i,r,n,o)}finally{h=!1,$()}}catch(e){h=!1,console.error(`[${f}] Critical error in onPopState`,e);try{const e=r.getState();if(e){const t=r.buildUrl(e.name,e.params);n.replaceState(e,"",t)}}catch(e){console.error(`[${f}] Failed to recover from critical error`,e)}$()}}return r.start=e=>s(e??n.getLocation(o)),r.buildUrl=(e,t)=>{const n=r.buildPath(e,t);return y()+w+n},r.matchUrl=e=>{const t=(e=>{try{const t=new URL(e,globalThis.location.origin),r=t.pathname,n=t.hash,a=t.search,s=y();if(!["http:","https:"].includes(t.protocol))return console.warn(`[${f}] Invalid URL protocol in ${e}`),null;if(o.useHash)return(T?n.replace(T,""):n.slice(1))+a;if(s){const e=d(s),t=i(`^${e}`),n=r.replace(t,"");return(n.startsWith("/")?"":"/")+n+a}return r+a}catch(t){return console.warn(`[${f}] Could not parse url ${e}`,t),null}})(e);return t?a.matchPath(t):void 0},r.replaceHistoryState=(e,t={})=>{const s=a.buildState(e,t);if(!s)throw new Error(`[real-router] Cannot replace state: route "${e}" is not found`);b(a.makeState(s.name,s.params,r.buildPath(s.name,s.params),{params:s.meta,options:{}},1),r.buildUrl(e,t),!0,n,o)},Object.defineProperty(r,"lastKnownState",{get:()=>l,set(e){l=e?Object.freeze({...e}):void 0},enumerable:!0,configurable:!0}),{onStart:()=>{g&&g(),g=n.addPopstateListener(e=>{P(e)},o)},onStop:()=>{g&&(g(),g=void 0)},onTransitionSuccess:(e,t,a)=>{r.lastKnownState=e;const s=(a.replace??!t)||!!a.reload&&r.areStatesEqual(e,t,!1),i=r.buildUrl(e.name,e.params);b(e,!o.preserveHash||t&&t.path!==e.path?i:i+n.getHash(),s,n,o)},teardown:()=>{g&&(g(),g=void 0),r.start=s,delete r.buildUrl,delete r.matchUrl,delete r.replaceHistoryState,delete r.lastKnownState}}}},exports.isHistoryState=u,exports.isState=c;//# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constants.ts","../../src/utils.ts","../../src/browser.ts","../../src/plugin.ts"],"names":["errorCodes","defaultOptions","rawPath","safePath","logger","getCachedRegExp","getBase"],"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 mergeState: boolean;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n mergeState: false,\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/modules/utils.ts\n\nimport { errorCodes } from \"@real-router/core\";\nimport { isStateStrict as isState } from \"type-guards\";\n\nimport { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions, HistoryState, Browser } from \"./types\";\nimport type {\n Router,\n NavigationOptions,\n RouterError,\n State,\n} from \"@real-router/core\";\n\n/**\n * No-op function for default callbacks\n */\nexport const noop = (): void => undefined;\n\n/**\n * Cache for escaped RegExp strings\n */\nconst escapeRegExpCache = new Map<string, string>();\n\n/**\n * Escapes special RegExp characters in a string.\n * Used to safely build RegExp from user-provided strings (hashPrefix, base).\n *\n * @param str - String to escape\n * @returns Escaped string safe for RegExp construction\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\n/**\n * Creates state from popstate event\n *\n * @param evt - PopStateEvent from browser\n * @param router - Router instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n * @returns Router state or undefined\n */\nexport function createStateFromEvent(\n evt: PopStateEvent,\n router: Router,\n browser: Browser,\n options: BrowserPluginOptions,\n): State | undefined {\n const isNewState = !isState(evt.state);\n\n if (isNewState) {\n return router.matchPath(browser.getLocation(options));\n }\n\n return router.makeState(\n evt.state.name,\n evt.state.params,\n evt.state.path,\n {\n ...evt.state.meta,\n params: evt.state.meta?.params ?? {},\n options: evt.state.meta?.options ?? {},\n },\n evt.state.meta?.id,\n );\n}\n\n/**\n * Checks if transition should be skipped (same states)\n *\n * @param newState - New state from event\n * @param currentState - Current router state\n * @param router - Router instance\n * @returns true if transition should be skipped\n */\nexport function shouldSkipTransition(\n newState: State | undefined,\n currentState: State | undefined,\n router: Router,\n): boolean {\n if (!newState) {\n return true;\n }\n\n return !!(\n currentState && router.areStatesEqual(newState, currentState, false)\n );\n}\n\n/**\n * Handles missing state by navigating to default route\n *\n * @param router - Router instance\n * @param transitionOptions - Options for transition\n * @returns true if handled, false if no default route\n */\nexport function handleMissingState(\n router: Router,\n transitionOptions: NavigationOptions,\n): boolean {\n const routerOptions = router.getOptions();\n const { defaultRoute } = routerOptions;\n\n if (!defaultRoute) {\n return false;\n }\n\n void router.navigateToDefault({\n ...transitionOptions,\n reload: true,\n replace: true,\n });\n\n return true;\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 * @param options - Browser plugin options\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n options: BrowserPluginOptions,\n): void {\n const trimmedState: HistoryState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n const finalState: HistoryState =\n options.mergeState && browser.getState()\n ? { ...browser.getState(), ...trimmedState }\n : trimmedState;\n\n if (replace) {\n browser.replaceState(finalState, \"\", url);\n } else {\n browser.pushState(finalState, \"\", url);\n }\n}\n\n/**\n * Handles transition result (success or error)\n *\n * Success case is handled by the router FSM chain (TRANSITION_SUCCESS event).\n * This function only handles error cases that need URL restoration.\n *\n * @param err - Router error or undefined if successful\n * @param toState - Target state\n * @param fromState - Source state\n * @param isNewState - Whether this is a new state (not from history)\n * @param router - Router instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n */\nexport function handleTransitionResult(\n err: RouterError | undefined,\n toState: State | undefined,\n fromState: State | undefined,\n isNewState: boolean,\n router: Router,\n browser: Browser,\n options: BrowserPluginOptions,\n): void {\n // Success case handled by the router FSM chain (TRANSITION_SUCCESS event)\n if (!err) {\n return;\n }\n\n // Handle CANNOT_DEACTIVATE - restore previous URL\n if (\n err.code === errorCodes.CANNOT_DEACTIVATE &&\n toState &&\n fromState &&\n !isNewState\n ) {\n const url = router.buildUrl(fromState.name, fromState.params);\n\n updateBrowserState(fromState, url, true, browser, options);\n }\n}\n\n/**\n * Type guard to check if a key exists in default options\n */\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\n/**\n * Validates that an option value has the correct type\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\n/**\n * Validates browser plugin options and warns about conflicting configurations.\n * TypeScript types prevent conflicts at compile-time, but runtime validation\n * is needed for JavaScript users and dynamic configurations.\n *\n * IMPORTANT: This validates only user-provided options, not merged defaults.\n *\n * @returns true if invalid types detected, false otherwise\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 // Validate option types against defaults\n // Using Object.keys ensures we only check properties that actually exist\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 // Check for hash mode conflicts\n // Runtime validation for JS users - TypeScript prevents this at compile time\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n // Check for history mode conflicts\n // Runtime validation for JS users - TypeScript prevents this at compile time\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n // Single type assertion needed: TypeScript narrows opts to HistoryModeOptions\n // where hashPrefix is 'never', but we need to check it at runtime for JS users\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","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\nimport { isHistoryState } from \"type-guards\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { escapeRegExp } from \"./utils\";\n\nimport type { Browser, BrowserPluginOptions, HistoryState } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\n/**\n * Returns current base path from browser location\n */\nconst getBase = () => globalThis.location.pathname;\n\n/**\n * Detects if browser supports popstate events on hash changes.\n * Old IE (Trident engine) doesn't fire popstate on hashchange.\n * Uses memoization based on userAgent for performance while remaining testable.\n */\nconst supportsPopStateOnHashChange = (() => {\n let cachedUserAgent: string | undefined;\n let cachedResult = false;\n\n return (): boolean => {\n // Note: This function is only called from real browser's addPopstateListener,\n // never from fallback browser (SSR), so window is guaranteed to exist\n const currentUserAgent = globalThis.navigator.userAgent;\n\n // Only recalculate if userAgent changed (or first call)\n if (currentUserAgent !== cachedUserAgent) {\n cachedUserAgent = currentUserAgent;\n cachedResult = !currentUserAgent.includes(\"Trident\");\n }\n\n return cachedResult;\n };\n})();\n\n/**\n * Pushes new state to browser history\n */\nconst pushState = (state: State, title: string | null, path: string | URL) => {\n globalThis.history.pushState(state, title ?? \"\", path);\n};\n\n/**\n * Replaces current state in browser history\n */\nconst replaceState = (\n state: State,\n title: string | null,\n path: string | URL,\n) => {\n globalThis.history.replaceState(state, title ?? \"\", path);\n};\n\n/**\n * Adds popstate/hashchange event listeners based on browser capabilities\n *\n * @param fn - Event handler function\n * @param opts - Browser plugin options\n * @returns Cleanup function to remove listeners\n */\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn, opts) => {\n const needsHashChangeListener =\n opts.useHash && !supportsPopStateOnHashChange();\n\n globalThis.addEventListener(\"popstate\", fn as (evt: PopStateEvent) => void);\n\n if (needsHashChangeListener) {\n globalThis.addEventListener(\n \"hashchange\",\n fn as (evt: HashChangeEvent) => void,\n );\n }\n\n return () => {\n globalThis.removeEventListener(\n \"popstate\",\n fn as (evt: PopStateEvent) => void,\n );\n\n if (needsHashChangeListener) {\n globalThis.removeEventListener(\n \"hashchange\",\n fn as (evt: HashChangeEvent) => void,\n );\n }\n };\n};\n\n/**\n * Creates RegExp cache for getLocation optimization\n */\nconst createRegExpCache = () => {\n const cache = new Map<string, RegExp>();\n\n return (pattern: string): RegExp => {\n let regex = cache.get(pattern);\n\n if (!regex) {\n regex = new RegExp(pattern);\n cache.set(pattern, regex);\n }\n\n return regex;\n };\n};\n\nconst getCachedRegExp = createRegExpCache();\n\n/**\n * Gets current location path from browser, respecting plugin options\n *\n * @param opts - Browser plugin options\n * @returns Current path string\n */\nconst getLocation = (opts: BrowserPluginOptions) => {\n const { useHash, hashPrefix = \"\", base = \"\" } = opts;\n\n // Optimization: skip RegExp for empty values\n if (!hashPrefix && !base) {\n const rawPath = useHash\n ? globalThis.location.hash.slice(1)\n : globalThis.location.pathname;\n const safePath = safelyEncodePath(rawPath);\n\n return (safePath || \"/\") + globalThis.location.search;\n }\n\n const escapedHashPrefix = escapeRegExp(hashPrefix);\n const escapedBase = escapeRegExp(base);\n\n const rawPath = useHash\n ? globalThis.location.hash.replace(\n getCachedRegExp(`^#${escapedHashPrefix}`),\n \"\",\n )\n : globalThis.location.pathname.replace(\n getCachedRegExp(`^${escapedBase}`),\n \"\",\n );\n\n const safePath = safelyEncodePath(rawPath);\n\n return (safePath || \"/\") + globalThis.location.search;\n};\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\n/**\n * Gets current history state with validation.\n * Returns undefined instead of throwing for safer error handling.\n *\n * @returns Valid history state or undefined\n */\nconst getState = (): HistoryState | undefined => {\n if (!globalThis.history.state) {\n return undefined;\n }\n\n // Validate state structure instead of throwing\n if (!isHistoryState(globalThis.history.state)) {\n logger.warn(\n LOGGER_CONTEXT,\n \"History state is not a valid state object, ignoring\",\n globalThis.history.state,\n );\n\n return undefined;\n }\n\n return globalThis.history.state as HistoryState;\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 getBase: () => {\n warnOnce(\"getBase\");\n\n return \"\";\n },\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 getState: () => {\n warnOnce(\"getState\");\n\n // eslint-disable-next-line unicorn/no-useless-undefined\n return undefined;\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 getBase,\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getState,\n getHash,\n }\n : createFallbackBrowser();\n}\n","// packages/browser-plugin/modules/plugin.ts\n\nimport { isStateStrict as isState } from \"type-guards\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport {\n escapeRegExp,\n createStateFromEvent,\n shouldSkipTransition,\n handleMissingState,\n updateBrowserState,\n handleTransitionResult,\n validateOptions,\n} from \"./utils\";\n\nimport type { BrowserPluginOptions, Browser, HistoryState } from \"./types\";\nimport type {\n PluginFactory,\n Router,\n RouterError,\n State,\n} from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * Features:\n * - Syncs router state with browser history (pushState/replaceState)\n * - Handles popstate events for browser back/forward navigation\n * - Supports hash-based routing for legacy browsers\n * - Provides URL building and matching utilities\n * - SSR-safe with graceful fallbacks\n * - Runtime validation warns about conflicting options\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 // Validate user-provided options before merging with defaults\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n // Skip normalization if invalid types detected (prevents runtime errors)\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 // Remove conflicting properties based on mode to prevent misuse\n // This ensures options object is clean even if JS users pass invalid config\n if (options.useHash === true) {\n // Hash mode: remove history-only options\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n // History mode (default): remove hash-only options\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n // Normalize base path to prevent common configuration errors\n // Type check needed for runtime safety (JS users may pass wrong types)\n if (options.base && typeof options.base === \"string\") {\n // Ensure leading slash for absolute paths\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n // Remove trailing slash to prevent double slashes\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n // Cache RegExp patterns at plugin creation for performance\n const regExpCache = new Map<string, RegExp>();\n const getCachedRegExp = (pattern: string): RegExp => {\n const cached = regExpCache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n regExpCache.set(pattern, newRegExp);\n\n return newRegExp;\n };\n\n // Create transition options with proper typing for exactOptionalPropertyTypes\n // replace: true is needed because popstate means URL already changed (back/forward)\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 }\n : { forceDeactivate, source, replace: true };\n\n let removePopStateListener: (() => void) | undefined;\n\n return function browserPlugin(router: Router) {\n // Store original methods for restoration on teardown\n\n const routerStart = router.start;\n\n // Transition state management\n let isTransitioning = false;\n\n // Deferred popstate event queue (stores only the last event)\n let deferredPopstateEvent: PopStateEvent | null = null;\n\n // Frozen copy of lastKnownState for immutability\n let cachedFrozenState: State | undefined;\n\n // Options can be changed at runtime in onStart\n /* v8 ignore next -- @preserve fallback for undefined base */\n const getBase = () => options.base ?? \"\";\n /* v8 ignore next -- @preserve fallback for undefined hashPrefix */\n const hashPrefix = options.hashPrefix ?? \"\";\n const escapedHashPrefix = escapeRegExp(hashPrefix);\n const prefix = options.useHash ? `#${hashPrefix}` : \"\";\n\n // Pre-compute RegExp patterns\n const hashPrefixRegExp = escapedHashPrefix\n ? getCachedRegExp(`^#${escapedHashPrefix}`)\n : null;\n\n /**\n * Parses URL and extracts path using native URL API.\n * More robust than regex parsing - handles IPv6, Unicode, edge cases.\n *\n * @param url - URL to parse\n * @returns Path string or null on parse error\n */\n const urlToPath = (url: string): string | null => {\n try {\n // Use URL API for reliable parsing\n const parsedUrl = new URL(url, globalThis.location.origin);\n const pathname = parsedUrl.pathname;\n const hash = parsedUrl.hash;\n const search = parsedUrl.search;\n const base = getBase();\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 if (options.useHash) {\n // Use cached RegExp or simple slice if no prefix\n const path = hashPrefixRegExp\n ? hash.replace(hashPrefixRegExp, \"\")\n : hash.slice(1);\n\n return path + search;\n } else if (base) {\n // Remove base prefix\n const escapedBase = escapeRegExp(base);\n const baseRegExp = getCachedRegExp(`^${escapedBase}`);\n const stripped = pathname.replace(baseRegExp, \"\");\n\n return (stripped.startsWith(\"/\") ? \"\" : \"/\") + stripped + search;\n }\n\n return pathname + search;\n } catch (error) {\n // Graceful fallback instead of throw\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n };\n\n /**\n * Overrides router.start to integrate with browser location.\n * When no path is provided, resolves current browser URL automatically.\n */\n router.start = async (path?: string) => {\n return routerStart(path ?? browser.getLocation(options));\n };\n\n /**\n * Builds URL from route name and params.\n * Adds base path and hash prefix according to options.\n *\n * @security\n * When using buildUrl output in templates:\n * - ✅ SAFE: Modern frameworks (React, Vue, Angular) auto-escape in templates\n * - ✅ SAFE: Setting href attribute via DOM API (element.href = url)\n * - ❌ UNSAFE: Using innerHTML or similar without escaping\n *\n * @example\n * // Safe - React auto-escapes\n * <Link to={router.buildUrl('users', params)} />\n *\n * // Safe - Vue auto-escapes\n * <router-link :to=\"router.buildUrl('users', params)\" />\n *\n * // Unsafe - manual HTML construction\n * element.innerHTML = `<a href=\"${router.buildUrl('users', params)}\">Link</a>`; // ❌ DON'T\n */\n router.buildUrl = (route, params) => {\n const path = router.buildPath(route, params);\n\n return getBase() + prefix + path;\n };\n\n /**\n * Matches URL and returns corresponding state\n */\n router.matchUrl = (url) => {\n const path = urlToPath(url);\n\n return path ? router.matchPath(path) : undefined;\n };\n\n /**\n * Replaces current history state without triggering navigation.\n * Useful for updating URL without causing a full transition.\n */\n router.replaceHistoryState = (name, params = {}) => {\n const state = router.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 = router.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n options: {},\n },\n 1, // forceId\n );\n const url = router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, browser, options);\n };\n\n /**\n * lastKnownState: Immutable reference to last successful state.\n * Uses caching to avoid creating new objects on every read.\n * Optimized: Single copy + freeze operation instead of double copying.\n */\n Object.defineProperty(router, \"lastKnownState\", {\n get() {\n // Note: After teardown, this property is deleted from router,\n // so this getter is only called while plugin is active\n return cachedFrozenState;\n },\n set(value?: State) {\n // Create frozen copy in one operation (no double copying)\n cachedFrozenState = value ? Object.freeze({ ...value }) : undefined;\n },\n enumerable: true,\n configurable: true,\n });\n\n /**\n * Processes a deferred popstate event if one exists.\n * Called after transition completes.\n */\n function processDeferredEvent() {\n if (deferredPopstateEvent) {\n const event = deferredPopstateEvent;\n\n deferredPopstateEvent = null; // Clear before processing\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void onPopState(event);\n }\n }\n\n /**\n * Main popstate event handler.\n * Protected against concurrent transitions and handles errors gracefully.\n * Defers events during transitions to prevent browser history desync.\n */\n async function onPopState(evt: PopStateEvent) {\n // Race condition protection: defer event if transition in progress\n if (isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n // Store only the latest event (skip intermediate states)\n deferredPopstateEvent = evt;\n\n return;\n }\n\n // Top-level error recovery\n try {\n const routerState = router.getState();\n const state = createStateFromEvent(evt, router, browser, options);\n const isNewState = !isState(evt.state);\n\n // Handle missing state\n if (!state && handleMissingState(router, transitionOptions)) {\n return;\n }\n\n // Skip if states are equal\n if (shouldSkipTransition(state, routerState, router)) {\n return;\n }\n\n // Execute transition with race protection\n // state is guaranteed to be defined here because:\n // 1. handleMissingState handles !state case (line 339)\n // 2. shouldSkipTransition returns true when !state (utils.ts:129)\n /* v8 ignore start: defensive guard - state guaranteed defined by control flow above */\n if (!state) {\n return;\n }\n /* v8 ignore stop */\n\n isTransitioning = true;\n\n try {\n // transitionOptions includes replace: true, which is passed to TRANSITION_SUCCESS\n const toState = await router.navigateToState(\n state,\n routerState,\n transitionOptions,\n );\n\n handleTransitionResult(\n undefined,\n toState,\n routerState,\n isNewState,\n router,\n browser,\n options,\n );\n } catch (error) {\n handleTransitionResult(\n error as RouterError,\n undefined,\n routerState,\n isNewState,\n router,\n browser,\n options,\n );\n } finally {\n isTransitioning = false;\n // Process any deferred popstate events after transition completes\n processDeferredEvent();\n }\n } catch (error) {\n isTransitioning = false;\n console.error(\n `[${LOGGER_CONTEXT}] Critical error in onPopState`,\n error,\n );\n\n // Attempt recovery: sync browser with router state\n try {\n const currentState = router.getState();\n\n if (currentState) {\n const url = router.buildUrl(currentState.name, currentState.params);\n\n browser.replaceState(currentState as HistoryState, \"\", url);\n }\n } catch (recoveryError) {\n // If recovery fails, there's nothing more we can do\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n\n // Process any deferred events even after error\n processDeferredEvent();\n }\n }\n\n return {\n /**\n * Called when router.start() is invoked.\n * Sets up browser history integration.\n */\n onStart: () => {\n if (removePopStateListener) {\n removePopStateListener();\n }\n\n removePopStateListener = browser.addPopstateListener(\n (evt: PopStateEvent) => void onPopState(evt),\n options,\n );\n },\n\n /**\n * Called when router.stop() is invoked.\n * Cleans up event listeners.\n */\n onStop: () => {\n if (removePopStateListener) {\n removePopStateListener();\n removePopStateListener = undefined;\n }\n },\n\n /**\n * Called after successful navigation.\n * Updates browser history with new state.\n */\n onTransitionSuccess: (toState, fromState, navOptions) => {\n router.lastKnownState = toState;\n\n // Determine if we should replace or push history entry\n const replaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n router.areStatesEqual(toState, fromState, false));\n\n // Build URL with base and hash prefix\n const url = router.buildUrl(toState.name, toState.params);\n\n // Preserve hash fragment if configured\n // Note: preserveHash is deleted in hash mode, so it's always undefined there\n const shouldPreserveHash =\n options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash ? url + browser.getHash() : url;\n\n // Update browser history\n updateBrowserState(toState, finalUrl, replaceHistory, browser, options);\n },\n\n /**\n * Called when plugin is unsubscribed.\n * Restores original router state for clean teardown.\n */\n teardown: () => {\n // Remove event listeners\n if (removePopStateListener) {\n removePopStateListener();\n removePopStateListener = undefined;\n }\n\n // Restore original router methods\n router.start = routerStart;\n\n // Clean up added properties\n delete (router as Partial<Router>).buildUrl;\n delete (router as Partial<Router>).matchUrl;\n delete (router as Partial<Router>).replaceHistoryState;\n delete (router as Partial<Router>).lastKnownState;\n },\n };\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/utils.ts","../../src/browser.ts","../../src/plugin.ts"],"names":["errorCodes","defaultOptions","rawPath","safePath","logger","getCachedRegExp","getPluginApi","getBase"],"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 mergeState: boolean;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n mergeState: false,\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/modules/utils.ts\n\nimport { errorCodes } from \"@real-router/core\";\nimport { isStateStrict as isState } from \"type-guards\";\n\nimport { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions, HistoryState, Browser } from \"./types\";\nimport type {\n PluginApi,\n Router,\n NavigationOptions,\n RouterError,\n State,\n} from \"@real-router/core\";\n\n/**\n * No-op function for default callbacks\n */\nexport const noop = (): void => undefined;\n\n/**\n * Cache for escaped RegExp strings\n */\nconst escapeRegExpCache = new Map<string, string>();\n\n/**\n * Escapes special RegExp characters in a string.\n * Used to safely build RegExp from user-provided strings (hashPrefix, base).\n *\n * @param str - String to escape\n * @returns Escaped string safe for RegExp construction\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\n/**\n * Creates state from popstate event\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 Router state or undefined\n */\nexport function createStateFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n options: BrowserPluginOptions,\n): State | undefined {\n const isNewState = !isState(evt.state);\n\n if (isNewState) {\n return api.matchPath(browser.getLocation(options));\n }\n\n return api.makeState(\n evt.state.name,\n evt.state.params,\n evt.state.path,\n {\n ...evt.state.meta,\n params: evt.state.meta?.params ?? {},\n options: evt.state.meta?.options ?? {},\n },\n evt.state.meta?.id,\n );\n}\n\n/**\n * Checks if transition should be skipped (same states)\n *\n * @param newState - New state from event\n * @param currentState - Current router state\n * @param router - Router instance\n * @returns true if transition should be skipped\n */\nexport function shouldSkipTransition(\n newState: State | undefined,\n currentState: State | undefined,\n router: Router,\n): boolean {\n if (!newState) {\n return true;\n }\n\n return !!(\n currentState && router.areStatesEqual(newState, currentState, false)\n );\n}\n\n/**\n * Handles missing state by navigating to default route\n *\n * @param router - Router instance\n * @param api - Plugin API instance\n * @param transitionOptions - Options for transition\n * @returns true if handled, false if no default route\n */\nexport function handleMissingState(\n router: Router,\n api: PluginApi,\n transitionOptions: NavigationOptions,\n): boolean {\n const routerOptions = api.getOptions();\n const { defaultRoute } = routerOptions;\n\n if (!defaultRoute) {\n return false;\n }\n\n void router.navigateToDefault({\n ...transitionOptions,\n reload: true,\n replace: true,\n });\n\n return true;\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 * @param options - Browser plugin options\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n options: BrowserPluginOptions,\n): void {\n const trimmedState: HistoryState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n const finalState: HistoryState =\n options.mergeState && browser.getState()\n ? { ...browser.getState(), ...trimmedState }\n : trimmedState;\n\n if (replace) {\n browser.replaceState(finalState, \"\", url);\n } else {\n browser.pushState(finalState, \"\", url);\n }\n}\n\n/**\n * Handles transition result (success or error)\n *\n * Success case is handled by the router FSM chain (TRANSITION_SUCCESS event).\n * This function only handles error cases that need URL restoration.\n *\n * @param err - Router error or undefined if successful\n * @param toState - Target state\n * @param fromState - Source state\n * @param isNewState - Whether this is a new state (not from history)\n * @param router - Router instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n */\nexport function handleTransitionResult(\n err: RouterError | undefined,\n toState: State | undefined,\n fromState: State | undefined,\n isNewState: boolean,\n router: Router,\n browser: Browser,\n options: BrowserPluginOptions,\n): void {\n // Success case handled by the router FSM chain (TRANSITION_SUCCESS event)\n if (!err) {\n return;\n }\n\n // Handle CANNOT_DEACTIVATE - restore previous URL\n if (\n err.code === errorCodes.CANNOT_DEACTIVATE &&\n toState &&\n fromState &&\n !isNewState\n ) {\n const url = router.buildUrl(fromState.name, fromState.params);\n\n updateBrowserState(fromState, url, true, browser, options);\n }\n}\n\n/**\n * Type guard to check if a key exists in default options\n */\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\n/**\n * Validates that an option value has the correct type\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\n/**\n * Validates browser plugin options and warns about conflicting configurations.\n * TypeScript types prevent conflicts at compile-time, but runtime validation\n * is needed for JavaScript users and dynamic configurations.\n *\n * IMPORTANT: This validates only user-provided options, not merged defaults.\n *\n * @returns true if invalid types detected, false otherwise\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 // Validate option types against defaults\n // Using Object.keys ensures we only check properties that actually exist\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 // Check for hash mode conflicts\n // Runtime validation for JS users - TypeScript prevents this at compile time\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n // Check for history mode conflicts\n // Runtime validation for JS users - TypeScript prevents this at compile time\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n // Single type assertion needed: TypeScript narrows opts to HistoryModeOptions\n // where hashPrefix is 'never', but we need to check it at runtime for JS users\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","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\nimport { isHistoryState } from \"type-guards\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { escapeRegExp } from \"./utils\";\n\nimport type { Browser, BrowserPluginOptions, HistoryState } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\n/**\n * Returns current base path from browser location\n */\nconst getBase = () => globalThis.location.pathname;\n\n/**\n * Detects if browser supports popstate events on hash changes.\n * Old IE (Trident engine) doesn't fire popstate on hashchange.\n * Uses memoization based on userAgent for performance while remaining testable.\n */\nconst supportsPopStateOnHashChange = (() => {\n let cachedUserAgent: string | undefined;\n let cachedResult = false;\n\n return (): boolean => {\n // Note: This function is only called from real browser's addPopstateListener,\n // never from fallback browser (SSR), so window is guaranteed to exist\n const currentUserAgent = globalThis.navigator.userAgent;\n\n // Only recalculate if userAgent changed (or first call)\n if (currentUserAgent !== cachedUserAgent) {\n cachedUserAgent = currentUserAgent;\n cachedResult = !currentUserAgent.includes(\"Trident\");\n }\n\n return cachedResult;\n };\n})();\n\n/**\n * Pushes new state to browser history\n */\nconst pushState = (state: State, title: string | null, path: string | URL) => {\n globalThis.history.pushState(state, title ?? \"\", path);\n};\n\n/**\n * Replaces current state in browser history\n */\nconst replaceState = (\n state: State,\n title: string | null,\n path: string | URL,\n) => {\n globalThis.history.replaceState(state, title ?? \"\", path);\n};\n\n/**\n * Adds popstate/hashchange event listeners based on browser capabilities\n *\n * @param fn - Event handler function\n * @param opts - Browser plugin options\n * @returns Cleanup function to remove listeners\n */\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn, opts) => {\n const needsHashChangeListener =\n opts.useHash && !supportsPopStateOnHashChange();\n\n globalThis.addEventListener(\"popstate\", fn as (evt: PopStateEvent) => void);\n\n if (needsHashChangeListener) {\n globalThis.addEventListener(\n \"hashchange\",\n fn as (evt: HashChangeEvent) => void,\n );\n }\n\n return () => {\n globalThis.removeEventListener(\n \"popstate\",\n fn as (evt: PopStateEvent) => void,\n );\n\n if (needsHashChangeListener) {\n globalThis.removeEventListener(\n \"hashchange\",\n fn as (evt: HashChangeEvent) => void,\n );\n }\n };\n};\n\n/**\n * Creates RegExp cache for getLocation optimization\n */\nconst createRegExpCache = () => {\n const cache = new Map<string, RegExp>();\n\n return (pattern: string): RegExp => {\n let regex = cache.get(pattern);\n\n if (!regex) {\n regex = new RegExp(pattern);\n cache.set(pattern, regex);\n }\n\n return regex;\n };\n};\n\nconst getCachedRegExp = createRegExpCache();\n\n/**\n * Gets current location path from browser, respecting plugin options\n *\n * @param opts - Browser plugin options\n * @returns Current path string\n */\nconst getLocation = (opts: BrowserPluginOptions) => {\n const { useHash, hashPrefix = \"\", base = \"\" } = opts;\n\n // Optimization: skip RegExp for empty values\n if (!hashPrefix && !base) {\n const rawPath = useHash\n ? globalThis.location.hash.slice(1)\n : globalThis.location.pathname;\n const safePath = safelyEncodePath(rawPath);\n\n return (safePath || \"/\") + globalThis.location.search;\n }\n\n const escapedHashPrefix = escapeRegExp(hashPrefix);\n const escapedBase = escapeRegExp(base);\n\n const rawPath = useHash\n ? globalThis.location.hash.replace(\n getCachedRegExp(`^#${escapedHashPrefix}`),\n \"\",\n )\n : globalThis.location.pathname.replace(\n getCachedRegExp(`^${escapedBase}`),\n \"\",\n );\n\n const safePath = safelyEncodePath(rawPath);\n\n return (safePath || \"/\") + globalThis.location.search;\n};\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\n/**\n * Gets current history state with validation.\n * Returns undefined instead of throwing for safer error handling.\n *\n * @returns Valid history state or undefined\n */\nconst getState = (): HistoryState | undefined => {\n if (!globalThis.history.state) {\n return undefined;\n }\n\n // Validate state structure instead of throwing\n if (!isHistoryState(globalThis.history.state)) {\n logger.warn(\n LOGGER_CONTEXT,\n \"History state is not a valid state object, ignoring\",\n globalThis.history.state,\n );\n\n return undefined;\n }\n\n return globalThis.history.state as HistoryState;\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 getBase: () => {\n warnOnce(\"getBase\");\n\n return \"\";\n },\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 getState: () => {\n warnOnce(\"getState\");\n\n // eslint-disable-next-line unicorn/no-useless-undefined\n return undefined;\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 getBase,\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getState,\n getHash,\n }\n : createFallbackBrowser();\n}\n","// packages/browser-plugin/modules/plugin.ts\n\nimport { getPluginApi } from \"@real-router/core\";\nimport { isStateStrict as isState } from \"type-guards\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport {\n escapeRegExp,\n createStateFromEvent,\n shouldSkipTransition,\n handleMissingState,\n updateBrowserState,\n handleTransitionResult,\n validateOptions,\n} from \"./utils\";\n\nimport type { BrowserPluginOptions, Browser, HistoryState } from \"./types\";\nimport type {\n PluginFactory,\n Router,\n RouterError,\n State,\n} from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * Features:\n * - Syncs router state with browser history (pushState/replaceState)\n * - Handles popstate events for browser back/forward navigation\n * - Supports hash-based routing for legacy browsers\n * - Provides URL building and matching utilities\n * - SSR-safe with graceful fallbacks\n * - Runtime validation warns about conflicting options\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 // Validate user-provided options before merging with defaults\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n // Skip normalization if invalid types detected (prevents runtime errors)\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 // Remove conflicting properties based on mode to prevent misuse\n // This ensures options object is clean even if JS users pass invalid config\n if (options.useHash === true) {\n // Hash mode: remove history-only options\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n // History mode (default): remove hash-only options\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n // Normalize base path to prevent common configuration errors\n // Type check needed for runtime safety (JS users may pass wrong types)\n if (options.base && typeof options.base === \"string\") {\n // Ensure leading slash for absolute paths\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n // Remove trailing slash to prevent double slashes\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n // Cache RegExp patterns at plugin creation for performance\n const regExpCache = new Map<string, RegExp>();\n const getCachedRegExp = (pattern: string): RegExp => {\n const cached = regExpCache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n regExpCache.set(pattern, newRegExp);\n\n return newRegExp;\n };\n\n // Create transition options with proper typing for exactOptionalPropertyTypes\n // replace: true is needed because popstate means URL already changed (back/forward)\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 }\n : { forceDeactivate, source, replace: true };\n\n let removePopStateListener: (() => void) | undefined;\n\n return function browserPlugin(routerBase) {\n // Cast to augmented Router (class + module augmentation: buildUrl, matchUrl, etc.)\n const router = routerBase as Router;\n const api = getPluginApi(routerBase);\n\n // Store original methods for restoration on teardown\n\n const routerStart = router.start;\n\n // Transition state management\n let isTransitioning = false;\n\n // Deferred popstate event queue (stores only the last event)\n let deferredPopstateEvent: PopStateEvent | null = null;\n\n // Frozen copy of lastKnownState for immutability\n let cachedFrozenState: State | undefined;\n\n // Options can be changed at runtime in onStart\n /* v8 ignore next -- @preserve fallback for undefined base */\n const getBase = () => options.base ?? \"\";\n /* v8 ignore next -- @preserve fallback for undefined hashPrefix */\n const hashPrefix = options.hashPrefix ?? \"\";\n const escapedHashPrefix = escapeRegExp(hashPrefix);\n const prefix = options.useHash ? `#${hashPrefix}` : \"\";\n\n // Pre-compute RegExp patterns\n const hashPrefixRegExp = escapedHashPrefix\n ? getCachedRegExp(`^#${escapedHashPrefix}`)\n : null;\n\n /**\n * Parses URL and extracts path using native URL API.\n * More robust than regex parsing - handles IPv6, Unicode, edge cases.\n *\n * @param url - URL to parse\n * @returns Path string or null on parse error\n */\n const urlToPath = (url: string): string | null => {\n try {\n // Use URL API for reliable parsing\n const parsedUrl = new URL(url, globalThis.location.origin);\n const pathname = parsedUrl.pathname;\n const hash = parsedUrl.hash;\n const search = parsedUrl.search;\n const base = getBase();\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 if (options.useHash) {\n // Use cached RegExp or simple slice if no prefix\n const path = hashPrefixRegExp\n ? hash.replace(hashPrefixRegExp, \"\")\n : hash.slice(1);\n\n return path + search;\n } else if (base) {\n // Remove base prefix\n const escapedBase = escapeRegExp(base);\n const baseRegExp = getCachedRegExp(`^${escapedBase}`);\n const stripped = pathname.replace(baseRegExp, \"\");\n\n return (stripped.startsWith(\"/\") ? \"\" : \"/\") + stripped + search;\n }\n\n return pathname + search;\n } catch (error) {\n // Graceful fallback instead of throw\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n };\n\n /**\n * Overrides router.start to integrate with browser location.\n * When no path is provided, resolves current browser URL automatically.\n */\n router.start = (path?: string) => {\n return routerStart(path ?? browser.getLocation(options));\n };\n\n /**\n * Builds URL from route name and params.\n * Adds base path and hash prefix according to options.\n *\n * @security\n * When using buildUrl output in templates:\n * - ✅ SAFE: Modern frameworks (React, Vue, Angular) auto-escape in templates\n * - ✅ SAFE: Setting href attribute via DOM API (element.href = url)\n * - ❌ UNSAFE: Using innerHTML or similar without escaping\n *\n * @example\n * // Safe - React auto-escapes\n * <Link to={router.buildUrl('users', params)} />\n *\n * // Safe - Vue auto-escapes\n * <router-link :to=\"router.buildUrl('users', params)\" />\n *\n * // Unsafe - manual HTML construction\n * element.innerHTML = `<a href=\"${router.buildUrl('users', params)}\">Link</a>`; // ❌ DON'T\n */\n router.buildUrl = (route, params) => {\n const path = router.buildPath(route, params);\n\n return getBase() + prefix + path;\n };\n\n /**\n * Matches URL and returns corresponding state\n */\n router.matchUrl = (url) => {\n const path = urlToPath(url);\n\n return path ? api.matchPath(path) : undefined;\n };\n\n /**\n * Replaces current history state without triggering navigation.\n * Useful for updating URL without causing a full transition.\n */\n router.replaceHistoryState = (name, params = {}) => {\n const state = 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 = api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n options: {},\n },\n 1, // forceId\n );\n const url = router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, browser, options);\n };\n\n /**\n * lastKnownState: Immutable reference to last successful state.\n * Uses caching to avoid creating new objects on every read.\n * Optimized: Single copy + freeze operation instead of double copying.\n */\n Object.defineProperty(router, \"lastKnownState\", {\n get() {\n // Note: After teardown, this property is deleted from router,\n // so this getter is only called while plugin is active\n return cachedFrozenState;\n },\n set(value?: State) {\n // Create frozen copy in one operation (no double copying)\n cachedFrozenState = value ? Object.freeze({ ...value }) : undefined;\n },\n enumerable: true,\n configurable: true,\n });\n\n /**\n * Processes a deferred popstate event if one exists.\n * Called after transition completes.\n */\n function processDeferredEvent() {\n if (deferredPopstateEvent) {\n const event = deferredPopstateEvent;\n\n deferredPopstateEvent = null; // Clear before processing\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void onPopState(event);\n }\n }\n\n /**\n * Main popstate event handler.\n * Protected against concurrent transitions and handles errors gracefully.\n * Defers events during transitions to prevent browser history desync.\n */\n async function onPopState(evt: PopStateEvent) {\n // Race condition protection: defer event if transition in progress\n if (isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n // Store only the latest event (skip intermediate states)\n deferredPopstateEvent = evt;\n\n return;\n }\n\n // Top-level error recovery\n try {\n const routerState = router.getState();\n const state = createStateFromEvent(evt, api, browser, options);\n const isNewState = !isState(evt.state);\n\n // Handle missing state\n if (!state && handleMissingState(router, api, transitionOptions)) {\n return;\n }\n\n // Skip if states are equal\n if (shouldSkipTransition(state, routerState, router)) {\n return;\n }\n\n // Execute transition with race protection\n // state is guaranteed to be defined here because:\n // 1. handleMissingState handles !state case (line 339)\n // 2. shouldSkipTransition returns true when !state (utils.ts:129)\n /* v8 ignore start: defensive guard - state guaranteed defined by control flow above */\n if (!state) {\n return;\n }\n /* v8 ignore stop */\n\n isTransitioning = true;\n\n try {\n // transitionOptions includes replace: true, which is passed to TRANSITION_SUCCESS\n const toState = await api.navigateToState(\n state,\n routerState,\n transitionOptions,\n );\n\n handleTransitionResult(\n undefined,\n toState,\n routerState,\n isNewState,\n router,\n browser,\n options,\n );\n } catch (error) {\n handleTransitionResult(\n error as RouterError,\n undefined,\n routerState,\n isNewState,\n router,\n browser,\n options,\n );\n } finally {\n isTransitioning = false;\n // Process any deferred popstate events after transition completes\n processDeferredEvent();\n }\n } catch (error) {\n isTransitioning = false;\n console.error(\n `[${LOGGER_CONTEXT}] Critical error in onPopState`,\n error,\n );\n\n // Attempt recovery: sync browser with router state\n try {\n const currentState = router.getState();\n\n if (currentState) {\n const url = router.buildUrl(currentState.name, currentState.params);\n\n browser.replaceState(currentState as HistoryState, \"\", url);\n }\n } catch (recoveryError) {\n // If recovery fails, there's nothing more we can do\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n\n // Process any deferred events even after error\n processDeferredEvent();\n }\n }\n\n return {\n /**\n * Called when router.start() is invoked.\n * Sets up browser history integration.\n */\n onStart: () => {\n if (removePopStateListener) {\n removePopStateListener();\n }\n\n removePopStateListener = browser.addPopstateListener(\n (evt: PopStateEvent) => void onPopState(evt),\n options,\n );\n },\n\n /**\n * Called when router.stop() is invoked.\n * Cleans up event listeners.\n */\n onStop: () => {\n if (removePopStateListener) {\n removePopStateListener();\n removePopStateListener = undefined;\n }\n },\n\n /**\n * Called after successful navigation.\n * Updates browser history with new state.\n */\n onTransitionSuccess: (toState, fromState, navOptions) => {\n router.lastKnownState = toState;\n\n // Determine if we should replace or push history entry\n const replaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n router.areStatesEqual(toState, fromState, false));\n\n // Build URL with base and hash prefix\n const url = router.buildUrl(toState.name, toState.params);\n\n // Preserve hash fragment if configured\n // Note: preserveHash is deleted in hash mode, so it's always undefined there\n const shouldPreserveHash =\n options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash ? url + browser.getHash() : url;\n\n // Update browser history\n updateBrowserState(toState, finalUrl, replaceHistory, browser, options);\n },\n\n /**\n * Called when plugin is unsubscribed.\n * Restores original router state for clean teardown.\n */\n teardown: () => {\n // Remove event listeners\n if (removePopStateListener) {\n removePopStateListener();\n removePopStateListener = undefined;\n }\n\n // Restore original router methods\n router.start = routerStart;\n\n // Clean up added properties\n delete (router as Partial<Router>).buildUrl;\n delete (router as Partial<Router>).matchUrl;\n delete (router as Partial<Router>).replaceHistoryState;\n delete (router as Partial<Router>).lastKnownState;\n },\n };\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"},"../type-guards/dist/esm/index.mjs":{"bytes":3561,"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/constants.ts":{"bytes":1668,"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/utils.ts":{"bytes":7562,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/browser.ts":{"bytes":6860,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/utils.ts","kind":"import-statement","original":"./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/plugin.ts":{"bytes":15342,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/browser.ts","kind":"import-statement","original":"./browser"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/utils.ts","kind":"import-statement","original":"./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/index.ts":{"bytes":1442,"imports":[{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"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":42971},"dist/cjs/index.js":{"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["browserPluginFactory","isHistoryState","isState"],"entryPoint":"src/index.ts","inputs":{"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2444},"src/browser.ts":{"bytesInOutput":4028},"src/constants.ts":{"bytesInOutput":205},"src/utils.ts":{"bytesInOutput":3335},"src/plugin.ts":{"bytesInOutput":8146},"src/index.ts":{"bytesInOutput":0}},"bytes":18576}}}
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"},"../type-guards/dist/esm/index.mjs":{"bytes":3561,"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/constants.ts":{"bytes":1668,"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/utils.ts":{"bytes":7620,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/browser.ts":{"bytes":6860,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/utils.ts","kind":"import-statement","original":"./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/plugin.ts":{"bytes":15544,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/browser.ts","kind":"import-statement","original":"./browser"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/utils.ts","kind":"import-statement","original":"./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/index.ts":{"bytes":1442,"imports":[{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"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":43312},"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","isHistoryState","isState"],"entryPoint":"src/index.ts","inputs":{"src/plugin.ts":{"bytesInOutput":8257},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2444},"src/browser.ts":{"bytesInOutput":4028},"src/constants.ts":{"bytesInOutput":205},"src/utils.ts":{"bytesInOutput":3328},"src/index.ts":{"bytesInOutput":0}},"bytes":18698}}}
@@ -1 +1 @@
1
- import{logger as t}from"@real-router/logger";import{errorCodes as e}from"@real-router/core";var r=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function n(t,e=new WeakSet){if(null==t)return!0;const r=typeof t;if("string"===r||"boolean"===r)return!0;if("number"===r)return Number.isFinite(t);if("function"===r||"symbol"===r)return!1;if(Array.isArray(t))return!e.has(t)&&(e.add(t),t.every(t=>n(t,e)));if("object"===r){if(e.has(t))return!1;e.add(t);const r=Object.getPrototypeOf(t);return(null===r||r===Object.prototype)&&Object.values(t).every(t=>n(t,e))}return!1}function a(t){if(null==t)return!0;const e=typeof t;return"string"===e||"boolean"===e||"number"===e&&Number.isFinite(t)}function o(t){if("object"!=typeof t||null===t||Array.isArray(t))return!1;const e=Object.getPrototypeOf(t);if(null!==e&&e!==Object.prototype)return!1;let r=!1;for(const e in t){if(!Object.hasOwn(t,e))continue;const n=t[e];if(!a(n)){const t=typeof n;if("function"===t||"symbol"===t)return!1;r=!0;break}}return!r||n(t)}function s(t){if(null==t)return!0;const e=typeof t;return"string"===e||"boolean"===e||("number"===e?Number.isFinite(t):!!Array.isArray(t)&&t.every(t=>{const e=typeof t;return"string"===e||"boolean"===e||"number"===e&&Number.isFinite(t)}))}function i(t){if("object"!=typeof t||null===t)return!1;const e=t;return!("params"in e&&!function(t){if("object"!=typeof t||null===t||Array.isArray(t))return!1;for(const e in t)if(Object.hasOwn(t,e)&&!s(t[e]))return!1;return!0}(e.params)||"options"in e&&"object"!=typeof e.options||"id"in e&&"number"!=typeof e.id)}function l(t){return function(t){return"string"==typeof t&&(""===t||!(t.length>1e4)&&(!!t.startsWith("@@")||r.test(t)))}(t.name)&&"string"==typeof t.path&&o(t.params)}function c(t){if("object"!=typeof t||null===t)return!1;const e=t;return!!l(e)&&(void 0===e.meta||i(e.meta))}function u(t){if("object"!=typeof t||null===t)return!1;const e=t;return!!l(e)&&"meta"in e&&i(e.meta)}var h={forceDeactivate:!0,useHash:!1,hashPrefix:"",base:"",mergeState:!1,preserveHash:!0},p="popstate",f="browser-plugin",d=new Map,g=t=>{const e=d.get(t);if(void 0!==e)return e;const r=t.replaceAll(/[$()*+.?[\\\]^{|}-]/g,String.raw`\$&`);return d.set(t,r),r};function b(t,e,r,n,a){const o={meta:t.meta,name:t.name,params:t.params,path:t.path},s=a.mergeState&&n.getState()?{...n.getState(),...o}:o;r?n.replaceState(s,"",e):n.pushState(s,"",e)}function m(t,r,n,a,o,s,i){t&&t.code===e.CANNOT_DEACTIVATE&&r&&n&&!a&&b(n,o.buildUrl(n.name,n.params),!0,s,i)}function y(t,e){return t in e}function v(t,e,r){const n=typeof e;return n===r||void 0===e||(console.warn(`[${f}] Invalid type for '${t}': expected ${r}, got ${n}`),!1)}var S=()=>{},w=()=>globalThis.location.pathname,T=(()=>{let t,e=!1;return()=>{const r=globalThis.navigator.userAgent;return r!==t&&(t=r,e=!r.includes("Trident")),e}})(),$=(t,e,r)=>{globalThis.history.pushState(t,e??"",r)},H=(t,e,r)=>{globalThis.history.replaceState(t,e??"",r)},P=(t,e)=>{const r=e.useHash&&!T();return globalThis.addEventListener("popstate",t),r&&globalThis.addEventListener("hashchange",t),()=>{globalThis.removeEventListener("popstate",t),r&&globalThis.removeEventListener("hashchange",t)}},j=(()=>{const t=new Map;return e=>{let r=t.get(e);return r||(r=new RegExp(e),t.set(e,r)),r}})(),O=t=>{const{useHash:e,hashPrefix:r="",base:n=""}=t;if(!r&&!n){const t=e?globalThis.location.hash.slice(1):globalThis.location.pathname;return(A(t)||"/")+globalThis.location.search}const a=g(r),o=g(n),s=e?globalThis.location.hash.replace(j(`^#${a}`),""):globalThis.location.pathname.replace(j(`^${o}`),"");return(A(s)||"/")+globalThis.location.search},A=e=>{try{return encodeURI(decodeURI(e))}catch(r){return t.warn(f,`Could not encode path "${e}"`,r),e}},L=()=>{if(globalThis.history.state){if(u(globalThis.history.state))return globalThis.history.state;t.warn(f,"History state is not a valid state object, ignoring",globalThis.history.state)}},x=()=>globalThis.location.hash;function U(e,r=function(){return void 0!==globalThis.window&&globalThis.history?{getBase:w,pushState:$,replaceState:H,addPopstateListener:P,getLocation:O,getState:L,getHash:x}:function(){let e=!1;const r=r=>{e||(t.warn(f,`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{getBase:()=>(r("getBase"),""),pushState:()=>{r("pushState")},replaceState:()=>{r("replaceState")},addPopstateListener:()=>(r("addPopstateListener"),S),getLocation:()=>(r("getLocation"),""),getState:()=>{r("getState")},getHash:()=>(r("getHash"),"")}}()}()){const n=function(t,e){if(!t)return!1;let r=!1;for(const n of Object.keys(t))y(n,e)&&(v(n,t[n],typeof e[n])||(r=!0));if(!0===t.useHash&&"preserveHash"in t&&console.warn(`[${f}] preserveHash ignored in hash mode`),!1===t.useHash&&"hashPrefix"in t){const e=t.hashPrefix;void 0!==e&&""!==e&&console.warn(`[${f}] hashPrefix ignored in history mode`)}return r}(e,h);let a={...h,...e};n&&(console.warn(`[${f}] Using default options due to invalid types`),a={...h}),!0===a.useHash?delete a.preserveHash:delete a.hashPrefix,a.base&&"string"==typeof a.base&&(a.base.startsWith("/")||(a.base=`/${a.base}`),a.base.endsWith("/")&&(a.base=a.base.slice(0,-1)));const o=new Map,s=t=>{const e=o.get(t);if(void 0!==e)return e;const r=new RegExp(t);return o.set(t,r),r},i=a.forceDeactivate,l=void 0===i?{source:p,replace:!0}:{forceDeactivate:i,source:p,replace:!0};let u;return function(t){const e=t.start;let n,o=!1,i=null;const h=()=>a.base??"",p=a.hashPrefix??"",d=g(p),y=a.useHash?`#${p}`:"",v=d?s(`^#${d}`):null;function S(){if(i){const t=i;i=null,console.warn(`[${f}] Processing deferred popstate event`),w(t)}}async function w(e){if(o)return console.warn(`[${f}] Transition in progress, deferring popstate event`),void(i=e);try{const n=t.getState(),s=function(t,e,r,n){return c(t.state)?e.makeState(t.state.name,t.state.params,t.state.path,{...t.state.meta,params:t.state.meta?.params??{},options:t.state.meta?.options??{}},t.state.meta?.id):e.matchPath(r.getLocation(n))}(e,t,r,a),i=!c(e.state);if(!s&&function(t,e){const r=t.getOptions(),{defaultRoute:n}=r;return!!n&&(t.navigateToDefault({...e,reload:!0,replace:!0}),!0)}(t,l))return;if(function(t,e,r){return!t||!(!e||!r.areStatesEqual(t,e,!1))}(s,n,t))return;if(!s)return;o=!0;try{m(void 0,await t.navigateToState(s,n,l),n,i,t,r,a)}catch(e){m(e,void 0,n,i,t,r,a)}finally{o=!1,S()}}catch(e){o=!1,console.error(`[${f}] Critical error in onPopState`,e);try{const e=t.getState();if(e){const n=t.buildUrl(e.name,e.params);r.replaceState(e,"",n)}}catch(t){console.error(`[${f}] Failed to recover from critical error`,t)}S()}}return t.start=async t=>e(t??r.getLocation(a)),t.buildUrl=(e,r)=>{const n=t.buildPath(e,r);return h()+y+n},t.matchUrl=e=>{const r=(t=>{try{const e=new URL(t,globalThis.location.origin),r=e.pathname,n=e.hash,o=e.search,i=h();if(!["http:","https:"].includes(e.protocol))return console.warn(`[${f}] Invalid URL protocol in ${t}`),null;if(a.useHash)return(v?n.replace(v,""):n.slice(1))+o;if(i){const t=g(i),e=s(`^${t}`),n=r.replace(e,"");return(n.startsWith("/")?"":"/")+n+o}return r+o}catch(e){return console.warn(`[${f}] Could not parse url ${t}`,e),null}})(e);return r?t.matchPath(r):void 0},t.replaceHistoryState=(e,n={})=>{const o=t.buildState(e,n);if(!o)throw new Error(`[real-router] Cannot replace state: route "${e}" is not found`);b(t.makeState(o.name,o.params,t.buildPath(o.name,o.params),{params:o.meta,options:{}},1),t.buildUrl(e,n),!0,r,a)},Object.defineProperty(t,"lastKnownState",{get:()=>n,set(t){n=t?Object.freeze({...t}):void 0},enumerable:!0,configurable:!0}),{onStart:()=>{u&&u(),u=r.addPopstateListener(t=>{w(t)},a)},onStop:()=>{u&&(u(),u=void 0)},onTransitionSuccess:(e,n,o)=>{t.lastKnownState=e;const s=(o.replace??!n)||!!o.reload&&t.areStatesEqual(e,n,!1),i=t.buildUrl(e.name,e.params);b(e,!a.preserveHash||n&&n.path!==e.path?i:i+r.getHash(),s,r,a)},teardown:()=>{u&&(u(),u=void 0),t.start=e,delete t.buildUrl,delete t.matchUrl,delete t.replaceHistoryState,delete t.lastKnownState}}}}export{U as browserPluginFactory,u as isHistoryState,c as isState};//# sourceMappingURL=index.mjs.map
1
+ import{getPluginApi as t,errorCodes as e}from"@real-router/core";import{logger as r}from"@real-router/logger";var n=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function a(t,e=new WeakSet){if(null==t)return!0;const r=typeof t;if("string"===r||"boolean"===r)return!0;if("number"===r)return Number.isFinite(t);if("function"===r||"symbol"===r)return!1;if(Array.isArray(t))return!e.has(t)&&(e.add(t),t.every(t=>a(t,e)));if("object"===r){if(e.has(t))return!1;e.add(t);const r=Object.getPrototypeOf(t);return(null===r||r===Object.prototype)&&Object.values(t).every(t=>a(t,e))}return!1}function o(t){if(null==t)return!0;const e=typeof t;return"string"===e||"boolean"===e||"number"===e&&Number.isFinite(t)}function s(t){if("object"!=typeof t||null===t||Array.isArray(t))return!1;const e=Object.getPrototypeOf(t);if(null!==e&&e!==Object.prototype)return!1;let r=!1;for(const e in t){if(!Object.hasOwn(t,e))continue;const n=t[e];if(!o(n)){const t=typeof n;if("function"===t||"symbol"===t)return!1;r=!0;break}}return!r||a(t)}function i(t){if(null==t)return!0;const e=typeof t;return"string"===e||"boolean"===e||("number"===e?Number.isFinite(t):!!Array.isArray(t)&&t.every(t=>{const e=typeof t;return"string"===e||"boolean"===e||"number"===e&&Number.isFinite(t)}))}function l(t){if("object"!=typeof t||null===t)return!1;const e=t;return!("params"in e&&!function(t){if("object"!=typeof t||null===t||Array.isArray(t))return!1;for(const e in t)if(Object.hasOwn(t,e)&&!i(t[e]))return!1;return!0}(e.params)||"options"in e&&"object"!=typeof e.options||"id"in e&&"number"!=typeof e.id)}function c(t){return function(t){return"string"==typeof t&&(""===t||!(t.length>1e4)&&(!!t.startsWith("@@")||n.test(t)))}(t.name)&&"string"==typeof t.path&&s(t.params)}function u(t){if("object"!=typeof t||null===t)return!1;const e=t;return!!c(e)&&(void 0===e.meta||l(e.meta))}function h(t){if("object"!=typeof t||null===t)return!1;const e=t;return!!c(e)&&"meta"in e&&l(e.meta)}var p={forceDeactivate:!0,useHash:!1,hashPrefix:"",base:"",mergeState:!1,preserveHash:!0},f="popstate",d="browser-plugin",g=new Map,b=t=>{const e=g.get(t);if(void 0!==e)return e;const r=t.replaceAll(/[$()*+.?[\\\]^{|}-]/g,String.raw`\$&`);return g.set(t,r),r};function m(t,e,r,n,a){const o={meta:t.meta,name:t.name,params:t.params,path:t.path},s=a.mergeState&&n.getState()?{...n.getState(),...o}:o;r?n.replaceState(s,"",e):n.pushState(s,"",e)}function y(t,r,n,a,o,s,i){t&&t.code===e.CANNOT_DEACTIVATE&&r&&n&&!a&&m(n,o.buildUrl(n.name,n.params),!0,s,i)}function v(t,e){return t in e}function S(t,e,r){const n=typeof e;return n===r||void 0===e||(console.warn(`[${d}] Invalid type for '${t}': expected ${r}, got ${n}`),!1)}var w=()=>{},T=()=>globalThis.location.pathname,$=(()=>{let t,e=!1;return()=>{const r=globalThis.navigator.userAgent;return r!==t&&(t=r,e=!r.includes("Trident")),e}})(),H=(t,e,r)=>{globalThis.history.pushState(t,e??"",r)},P=(t,e,r)=>{globalThis.history.replaceState(t,e??"",r)},j=(t,e)=>{const r=e.useHash&&!$();return globalThis.addEventListener("popstate",t),r&&globalThis.addEventListener("hashchange",t),()=>{globalThis.removeEventListener("popstate",t),r&&globalThis.removeEventListener("hashchange",t)}},O=(()=>{const t=new Map;return e=>{let r=t.get(e);return r||(r=new RegExp(e),t.set(e,r)),r}})(),A=t=>{const{useHash:e,hashPrefix:r="",base:n=""}=t;if(!r&&!n){const t=e?globalThis.location.hash.slice(1):globalThis.location.pathname;return(L(t)||"/")+globalThis.location.search}const a=b(r),o=b(n),s=e?globalThis.location.hash.replace(O(`^#${a}`),""):globalThis.location.pathname.replace(O(`^${o}`),"");return(L(s)||"/")+globalThis.location.search},L=t=>{try{return encodeURI(decodeURI(t))}catch(e){return r.warn(d,`Could not encode path "${t}"`,e),t}},x=()=>{if(globalThis.history.state){if(h(globalThis.history.state))return globalThis.history.state;r.warn(d,"History state is not a valid state object, ignoring",globalThis.history.state)}},U=()=>globalThis.location.hash;function E(e,n=function(){return void 0!==globalThis.window&&globalThis.history?{getBase:T,pushState:H,replaceState:P,addPopstateListener:j,getLocation:A,getState:x,getHash:U}:function(){let t=!1;const e=e=>{t||(r.warn(d,`Browser plugin is running in a non-browser environment. Method "${e}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),t=!0)};return{getBase:()=>(e("getBase"),""),pushState:()=>{e("pushState")},replaceState:()=>{e("replaceState")},addPopstateListener:()=>(e("addPopstateListener"),w),getLocation:()=>(e("getLocation"),""),getState:()=>{e("getState")},getHash:()=>(e("getHash"),"")}}()}()){const a=function(t,e){if(!t)return!1;let r=!1;for(const n of Object.keys(t))v(n,e)&&(S(n,t[n],typeof e[n])||(r=!0));if(!0===t.useHash&&"preserveHash"in t&&console.warn(`[${d}] preserveHash ignored in hash mode`),!1===t.useHash&&"hashPrefix"in t){const e=t.hashPrefix;void 0!==e&&""!==e&&console.warn(`[${d}] hashPrefix ignored in history mode`)}return r}(e,p);let o={...p,...e};a&&(console.warn(`[${d}] Using default options due to invalid types`),o={...p}),!0===o.useHash?delete o.preserveHash:delete o.hashPrefix,o.base&&"string"==typeof o.base&&(o.base.startsWith("/")||(o.base=`/${o.base}`),o.base.endsWith("/")&&(o.base=o.base.slice(0,-1)));const s=new Map,i=t=>{const e=s.get(t);if(void 0!==e)return e;const r=new RegExp(t);return s.set(t,r),r},l=o.forceDeactivate,c=void 0===l?{source:f,replace:!0}:{forceDeactivate:l,source:f,replace:!0};let h;return function(e){const r=e,a=t(e),s=r.start;let l,p=!1,f=null;const g=()=>o.base??"",v=o.hashPrefix??"",S=b(v),w=o.useHash?`#${v}`:"",T=S?i(`^#${S}`):null;function $(){if(f){const t=f;f=null,console.warn(`[${d}] Processing deferred popstate event`),H(t)}}async function H(t){if(p)return console.warn(`[${d}] Transition in progress, deferring popstate event`),void(f=t);try{const e=r.getState(),s=function(t,e,r,n){return u(t.state)?e.makeState(t.state.name,t.state.params,t.state.path,{...t.state.meta,params:t.state.meta?.params??{},options:t.state.meta?.options??{}},t.state.meta?.id):e.matchPath(r.getLocation(n))}(t,a,n,o),i=!u(t.state);if(!s&&function(t,e,r){const n=e.getOptions(),{defaultRoute:a}=n;return!!a&&(t.navigateToDefault({...r,reload:!0,replace:!0}),!0)}(r,a,c))return;if(function(t,e,r){return!t||!(!e||!r.areStatesEqual(t,e,!1))}(s,e,r))return;if(!s)return;p=!0;try{y(void 0,await a.navigateToState(s,e,c),e,i,r,n,o)}catch(t){y(t,void 0,e,i,r,n,o)}finally{p=!1,$()}}catch(t){p=!1,console.error(`[${d}] Critical error in onPopState`,t);try{const t=r.getState();if(t){const e=r.buildUrl(t.name,t.params);n.replaceState(t,"",e)}}catch(t){console.error(`[${d}] Failed to recover from critical error`,t)}$()}}return r.start=t=>s(t??n.getLocation(o)),r.buildUrl=(t,e)=>{const n=r.buildPath(t,e);return g()+w+n},r.matchUrl=t=>{const e=(t=>{try{const e=new URL(t,globalThis.location.origin),r=e.pathname,n=e.hash,a=e.search,s=g();if(!["http:","https:"].includes(e.protocol))return console.warn(`[${d}] Invalid URL protocol in ${t}`),null;if(o.useHash)return(T?n.replace(T,""):n.slice(1))+a;if(s){const t=b(s),e=i(`^${t}`),n=r.replace(e,"");return(n.startsWith("/")?"":"/")+n+a}return r+a}catch(e){return console.warn(`[${d}] Could not parse url ${t}`,e),null}})(t);return e?a.matchPath(e):void 0},r.replaceHistoryState=(t,e={})=>{const s=a.buildState(t,e);if(!s)throw new Error(`[real-router] Cannot replace state: route "${t}" is not found`);m(a.makeState(s.name,s.params,r.buildPath(s.name,s.params),{params:s.meta,options:{}},1),r.buildUrl(t,e),!0,n,o)},Object.defineProperty(r,"lastKnownState",{get:()=>l,set(t){l=t?Object.freeze({...t}):void 0},enumerable:!0,configurable:!0}),{onStart:()=>{h&&h(),h=n.addPopstateListener(t=>{H(t)},o)},onStop:()=>{h&&(h(),h=void 0)},onTransitionSuccess:(t,e,a)=>{r.lastKnownState=t;const s=(a.replace??!e)||!!a.reload&&r.areStatesEqual(t,e,!1),i=r.buildUrl(t.name,t.params);m(t,!o.preserveHash||e&&e.path!==t.path?i:i+n.getHash(),s,n,o)},teardown:()=>{h&&(h(),h=void 0),r.start=s,delete r.buildUrl,delete r.matchUrl,delete r.replaceHistoryState,delete r.lastKnownState}}}}export{E as browserPluginFactory,h as isHistoryState,u as isState};//# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constants.ts","../../src/utils.ts","../../src/browser.ts","../../src/plugin.ts"],"names":["defaultOptions","rawPath","safePath","getCachedRegExp","getBase"],"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 mergeState: boolean;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n mergeState: false,\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/modules/utils.ts\n\nimport { errorCodes } from \"@real-router/core\";\nimport { isStateStrict as isState } from \"type-guards\";\n\nimport { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions, HistoryState, Browser } from \"./types\";\nimport type {\n Router,\n NavigationOptions,\n RouterError,\n State,\n} from \"@real-router/core\";\n\n/**\n * No-op function for default callbacks\n */\nexport const noop = (): void => undefined;\n\n/**\n * Cache for escaped RegExp strings\n */\nconst escapeRegExpCache = new Map<string, string>();\n\n/**\n * Escapes special RegExp characters in a string.\n * Used to safely build RegExp from user-provided strings (hashPrefix, base).\n *\n * @param str - String to escape\n * @returns Escaped string safe for RegExp construction\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\n/**\n * Creates state from popstate event\n *\n * @param evt - PopStateEvent from browser\n * @param router - Router instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n * @returns Router state or undefined\n */\nexport function createStateFromEvent(\n evt: PopStateEvent,\n router: Router,\n browser: Browser,\n options: BrowserPluginOptions,\n): State | undefined {\n const isNewState = !isState(evt.state);\n\n if (isNewState) {\n return router.matchPath(browser.getLocation(options));\n }\n\n return router.makeState(\n evt.state.name,\n evt.state.params,\n evt.state.path,\n {\n ...evt.state.meta,\n params: evt.state.meta?.params ?? {},\n options: evt.state.meta?.options ?? {},\n },\n evt.state.meta?.id,\n );\n}\n\n/**\n * Checks if transition should be skipped (same states)\n *\n * @param newState - New state from event\n * @param currentState - Current router state\n * @param router - Router instance\n * @returns true if transition should be skipped\n */\nexport function shouldSkipTransition(\n newState: State | undefined,\n currentState: State | undefined,\n router: Router,\n): boolean {\n if (!newState) {\n return true;\n }\n\n return !!(\n currentState && router.areStatesEqual(newState, currentState, false)\n );\n}\n\n/**\n * Handles missing state by navigating to default route\n *\n * @param router - Router instance\n * @param transitionOptions - Options for transition\n * @returns true if handled, false if no default route\n */\nexport function handleMissingState(\n router: Router,\n transitionOptions: NavigationOptions,\n): boolean {\n const routerOptions = router.getOptions();\n const { defaultRoute } = routerOptions;\n\n if (!defaultRoute) {\n return false;\n }\n\n void router.navigateToDefault({\n ...transitionOptions,\n reload: true,\n replace: true,\n });\n\n return true;\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 * @param options - Browser plugin options\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n options: BrowserPluginOptions,\n): void {\n const trimmedState: HistoryState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n const finalState: HistoryState =\n options.mergeState && browser.getState()\n ? { ...browser.getState(), ...trimmedState }\n : trimmedState;\n\n if (replace) {\n browser.replaceState(finalState, \"\", url);\n } else {\n browser.pushState(finalState, \"\", url);\n }\n}\n\n/**\n * Handles transition result (success or error)\n *\n * Success case is handled by the router FSM chain (TRANSITION_SUCCESS event).\n * This function only handles error cases that need URL restoration.\n *\n * @param err - Router error or undefined if successful\n * @param toState - Target state\n * @param fromState - Source state\n * @param isNewState - Whether this is a new state (not from history)\n * @param router - Router instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n */\nexport function handleTransitionResult(\n err: RouterError | undefined,\n toState: State | undefined,\n fromState: State | undefined,\n isNewState: boolean,\n router: Router,\n browser: Browser,\n options: BrowserPluginOptions,\n): void {\n // Success case handled by the router FSM chain (TRANSITION_SUCCESS event)\n if (!err) {\n return;\n }\n\n // Handle CANNOT_DEACTIVATE - restore previous URL\n if (\n err.code === errorCodes.CANNOT_DEACTIVATE &&\n toState &&\n fromState &&\n !isNewState\n ) {\n const url = router.buildUrl(fromState.name, fromState.params);\n\n updateBrowserState(fromState, url, true, browser, options);\n }\n}\n\n/**\n * Type guard to check if a key exists in default options\n */\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\n/**\n * Validates that an option value has the correct type\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\n/**\n * Validates browser plugin options and warns about conflicting configurations.\n * TypeScript types prevent conflicts at compile-time, but runtime validation\n * is needed for JavaScript users and dynamic configurations.\n *\n * IMPORTANT: This validates only user-provided options, not merged defaults.\n *\n * @returns true if invalid types detected, false otherwise\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 // Validate option types against defaults\n // Using Object.keys ensures we only check properties that actually exist\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 // Check for hash mode conflicts\n // Runtime validation for JS users - TypeScript prevents this at compile time\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n // Check for history mode conflicts\n // Runtime validation for JS users - TypeScript prevents this at compile time\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n // Single type assertion needed: TypeScript narrows opts to HistoryModeOptions\n // where hashPrefix is 'never', but we need to check it at runtime for JS users\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","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\nimport { isHistoryState } from \"type-guards\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { escapeRegExp } from \"./utils\";\n\nimport type { Browser, BrowserPluginOptions, HistoryState } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\n/**\n * Returns current base path from browser location\n */\nconst getBase = () => globalThis.location.pathname;\n\n/**\n * Detects if browser supports popstate events on hash changes.\n * Old IE (Trident engine) doesn't fire popstate on hashchange.\n * Uses memoization based on userAgent for performance while remaining testable.\n */\nconst supportsPopStateOnHashChange = (() => {\n let cachedUserAgent: string | undefined;\n let cachedResult = false;\n\n return (): boolean => {\n // Note: This function is only called from real browser's addPopstateListener,\n // never from fallback browser (SSR), so window is guaranteed to exist\n const currentUserAgent = globalThis.navigator.userAgent;\n\n // Only recalculate if userAgent changed (or first call)\n if (currentUserAgent !== cachedUserAgent) {\n cachedUserAgent = currentUserAgent;\n cachedResult = !currentUserAgent.includes(\"Trident\");\n }\n\n return cachedResult;\n };\n})();\n\n/**\n * Pushes new state to browser history\n */\nconst pushState = (state: State, title: string | null, path: string | URL) => {\n globalThis.history.pushState(state, title ?? \"\", path);\n};\n\n/**\n * Replaces current state in browser history\n */\nconst replaceState = (\n state: State,\n title: string | null,\n path: string | URL,\n) => {\n globalThis.history.replaceState(state, title ?? \"\", path);\n};\n\n/**\n * Adds popstate/hashchange event listeners based on browser capabilities\n *\n * @param fn - Event handler function\n * @param opts - Browser plugin options\n * @returns Cleanup function to remove listeners\n */\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn, opts) => {\n const needsHashChangeListener =\n opts.useHash && !supportsPopStateOnHashChange();\n\n globalThis.addEventListener(\"popstate\", fn as (evt: PopStateEvent) => void);\n\n if (needsHashChangeListener) {\n globalThis.addEventListener(\n \"hashchange\",\n fn as (evt: HashChangeEvent) => void,\n );\n }\n\n return () => {\n globalThis.removeEventListener(\n \"popstate\",\n fn as (evt: PopStateEvent) => void,\n );\n\n if (needsHashChangeListener) {\n globalThis.removeEventListener(\n \"hashchange\",\n fn as (evt: HashChangeEvent) => void,\n );\n }\n };\n};\n\n/**\n * Creates RegExp cache for getLocation optimization\n */\nconst createRegExpCache = () => {\n const cache = new Map<string, RegExp>();\n\n return (pattern: string): RegExp => {\n let regex = cache.get(pattern);\n\n if (!regex) {\n regex = new RegExp(pattern);\n cache.set(pattern, regex);\n }\n\n return regex;\n };\n};\n\nconst getCachedRegExp = createRegExpCache();\n\n/**\n * Gets current location path from browser, respecting plugin options\n *\n * @param opts - Browser plugin options\n * @returns Current path string\n */\nconst getLocation = (opts: BrowserPluginOptions) => {\n const { useHash, hashPrefix = \"\", base = \"\" } = opts;\n\n // Optimization: skip RegExp for empty values\n if (!hashPrefix && !base) {\n const rawPath = useHash\n ? globalThis.location.hash.slice(1)\n : globalThis.location.pathname;\n const safePath = safelyEncodePath(rawPath);\n\n return (safePath || \"/\") + globalThis.location.search;\n }\n\n const escapedHashPrefix = escapeRegExp(hashPrefix);\n const escapedBase = escapeRegExp(base);\n\n const rawPath = useHash\n ? globalThis.location.hash.replace(\n getCachedRegExp(`^#${escapedHashPrefix}`),\n \"\",\n )\n : globalThis.location.pathname.replace(\n getCachedRegExp(`^${escapedBase}`),\n \"\",\n );\n\n const safePath = safelyEncodePath(rawPath);\n\n return (safePath || \"/\") + globalThis.location.search;\n};\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\n/**\n * Gets current history state with validation.\n * Returns undefined instead of throwing for safer error handling.\n *\n * @returns Valid history state or undefined\n */\nconst getState = (): HistoryState | undefined => {\n if (!globalThis.history.state) {\n return undefined;\n }\n\n // Validate state structure instead of throwing\n if (!isHistoryState(globalThis.history.state)) {\n logger.warn(\n LOGGER_CONTEXT,\n \"History state is not a valid state object, ignoring\",\n globalThis.history.state,\n );\n\n return undefined;\n }\n\n return globalThis.history.state as HistoryState;\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 getBase: () => {\n warnOnce(\"getBase\");\n\n return \"\";\n },\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 getState: () => {\n warnOnce(\"getState\");\n\n // eslint-disable-next-line unicorn/no-useless-undefined\n return undefined;\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 getBase,\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getState,\n getHash,\n }\n : createFallbackBrowser();\n}\n","// packages/browser-plugin/modules/plugin.ts\n\nimport { isStateStrict as isState } from \"type-guards\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport {\n escapeRegExp,\n createStateFromEvent,\n shouldSkipTransition,\n handleMissingState,\n updateBrowserState,\n handleTransitionResult,\n validateOptions,\n} from \"./utils\";\n\nimport type { BrowserPluginOptions, Browser, HistoryState } from \"./types\";\nimport type {\n PluginFactory,\n Router,\n RouterError,\n State,\n} from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * Features:\n * - Syncs router state with browser history (pushState/replaceState)\n * - Handles popstate events for browser back/forward navigation\n * - Supports hash-based routing for legacy browsers\n * - Provides URL building and matching utilities\n * - SSR-safe with graceful fallbacks\n * - Runtime validation warns about conflicting options\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 // Validate user-provided options before merging with defaults\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n // Skip normalization if invalid types detected (prevents runtime errors)\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 // Remove conflicting properties based on mode to prevent misuse\n // This ensures options object is clean even if JS users pass invalid config\n if (options.useHash === true) {\n // Hash mode: remove history-only options\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n // History mode (default): remove hash-only options\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n // Normalize base path to prevent common configuration errors\n // Type check needed for runtime safety (JS users may pass wrong types)\n if (options.base && typeof options.base === \"string\") {\n // Ensure leading slash for absolute paths\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n // Remove trailing slash to prevent double slashes\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n // Cache RegExp patterns at plugin creation for performance\n const regExpCache = new Map<string, RegExp>();\n const getCachedRegExp = (pattern: string): RegExp => {\n const cached = regExpCache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n regExpCache.set(pattern, newRegExp);\n\n return newRegExp;\n };\n\n // Create transition options with proper typing for exactOptionalPropertyTypes\n // replace: true is needed because popstate means URL already changed (back/forward)\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 }\n : { forceDeactivate, source, replace: true };\n\n let removePopStateListener: (() => void) | undefined;\n\n return function browserPlugin(router: Router) {\n // Store original methods for restoration on teardown\n\n const routerStart = router.start;\n\n // Transition state management\n let isTransitioning = false;\n\n // Deferred popstate event queue (stores only the last event)\n let deferredPopstateEvent: PopStateEvent | null = null;\n\n // Frozen copy of lastKnownState for immutability\n let cachedFrozenState: State | undefined;\n\n // Options can be changed at runtime in onStart\n /* v8 ignore next -- @preserve fallback for undefined base */\n const getBase = () => options.base ?? \"\";\n /* v8 ignore next -- @preserve fallback for undefined hashPrefix */\n const hashPrefix = options.hashPrefix ?? \"\";\n const escapedHashPrefix = escapeRegExp(hashPrefix);\n const prefix = options.useHash ? `#${hashPrefix}` : \"\";\n\n // Pre-compute RegExp patterns\n const hashPrefixRegExp = escapedHashPrefix\n ? getCachedRegExp(`^#${escapedHashPrefix}`)\n : null;\n\n /**\n * Parses URL and extracts path using native URL API.\n * More robust than regex parsing - handles IPv6, Unicode, edge cases.\n *\n * @param url - URL to parse\n * @returns Path string or null on parse error\n */\n const urlToPath = (url: string): string | null => {\n try {\n // Use URL API for reliable parsing\n const parsedUrl = new URL(url, globalThis.location.origin);\n const pathname = parsedUrl.pathname;\n const hash = parsedUrl.hash;\n const search = parsedUrl.search;\n const base = getBase();\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 if (options.useHash) {\n // Use cached RegExp or simple slice if no prefix\n const path = hashPrefixRegExp\n ? hash.replace(hashPrefixRegExp, \"\")\n : hash.slice(1);\n\n return path + search;\n } else if (base) {\n // Remove base prefix\n const escapedBase = escapeRegExp(base);\n const baseRegExp = getCachedRegExp(`^${escapedBase}`);\n const stripped = pathname.replace(baseRegExp, \"\");\n\n return (stripped.startsWith(\"/\") ? \"\" : \"/\") + stripped + search;\n }\n\n return pathname + search;\n } catch (error) {\n // Graceful fallback instead of throw\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n };\n\n /**\n * Overrides router.start to integrate with browser location.\n * When no path is provided, resolves current browser URL automatically.\n */\n router.start = async (path?: string) => {\n return routerStart(path ?? browser.getLocation(options));\n };\n\n /**\n * Builds URL from route name and params.\n * Adds base path and hash prefix according to options.\n *\n * @security\n * When using buildUrl output in templates:\n * - ✅ SAFE: Modern frameworks (React, Vue, Angular) auto-escape in templates\n * - ✅ SAFE: Setting href attribute via DOM API (element.href = url)\n * - ❌ UNSAFE: Using innerHTML or similar without escaping\n *\n * @example\n * // Safe - React auto-escapes\n * <Link to={router.buildUrl('users', params)} />\n *\n * // Safe - Vue auto-escapes\n * <router-link :to=\"router.buildUrl('users', params)\" />\n *\n * // Unsafe - manual HTML construction\n * element.innerHTML = `<a href=\"${router.buildUrl('users', params)}\">Link</a>`; // ❌ DON'T\n */\n router.buildUrl = (route, params) => {\n const path = router.buildPath(route, params);\n\n return getBase() + prefix + path;\n };\n\n /**\n * Matches URL and returns corresponding state\n */\n router.matchUrl = (url) => {\n const path = urlToPath(url);\n\n return path ? router.matchPath(path) : undefined;\n };\n\n /**\n * Replaces current history state without triggering navigation.\n * Useful for updating URL without causing a full transition.\n */\n router.replaceHistoryState = (name, params = {}) => {\n const state = router.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 = router.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n options: {},\n },\n 1, // forceId\n );\n const url = router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, browser, options);\n };\n\n /**\n * lastKnownState: Immutable reference to last successful state.\n * Uses caching to avoid creating new objects on every read.\n * Optimized: Single copy + freeze operation instead of double copying.\n */\n Object.defineProperty(router, \"lastKnownState\", {\n get() {\n // Note: After teardown, this property is deleted from router,\n // so this getter is only called while plugin is active\n return cachedFrozenState;\n },\n set(value?: State) {\n // Create frozen copy in one operation (no double copying)\n cachedFrozenState = value ? Object.freeze({ ...value }) : undefined;\n },\n enumerable: true,\n configurable: true,\n });\n\n /**\n * Processes a deferred popstate event if one exists.\n * Called after transition completes.\n */\n function processDeferredEvent() {\n if (deferredPopstateEvent) {\n const event = deferredPopstateEvent;\n\n deferredPopstateEvent = null; // Clear before processing\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void onPopState(event);\n }\n }\n\n /**\n * Main popstate event handler.\n * Protected against concurrent transitions and handles errors gracefully.\n * Defers events during transitions to prevent browser history desync.\n */\n async function onPopState(evt: PopStateEvent) {\n // Race condition protection: defer event if transition in progress\n if (isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n // Store only the latest event (skip intermediate states)\n deferredPopstateEvent = evt;\n\n return;\n }\n\n // Top-level error recovery\n try {\n const routerState = router.getState();\n const state = createStateFromEvent(evt, router, browser, options);\n const isNewState = !isState(evt.state);\n\n // Handle missing state\n if (!state && handleMissingState(router, transitionOptions)) {\n return;\n }\n\n // Skip if states are equal\n if (shouldSkipTransition(state, routerState, router)) {\n return;\n }\n\n // Execute transition with race protection\n // state is guaranteed to be defined here because:\n // 1. handleMissingState handles !state case (line 339)\n // 2. shouldSkipTransition returns true when !state (utils.ts:129)\n /* v8 ignore start: defensive guard - state guaranteed defined by control flow above */\n if (!state) {\n return;\n }\n /* v8 ignore stop */\n\n isTransitioning = true;\n\n try {\n // transitionOptions includes replace: true, which is passed to TRANSITION_SUCCESS\n const toState = await router.navigateToState(\n state,\n routerState,\n transitionOptions,\n );\n\n handleTransitionResult(\n undefined,\n toState,\n routerState,\n isNewState,\n router,\n browser,\n options,\n );\n } catch (error) {\n handleTransitionResult(\n error as RouterError,\n undefined,\n routerState,\n isNewState,\n router,\n browser,\n options,\n );\n } finally {\n isTransitioning = false;\n // Process any deferred popstate events after transition completes\n processDeferredEvent();\n }\n } catch (error) {\n isTransitioning = false;\n console.error(\n `[${LOGGER_CONTEXT}] Critical error in onPopState`,\n error,\n );\n\n // Attempt recovery: sync browser with router state\n try {\n const currentState = router.getState();\n\n if (currentState) {\n const url = router.buildUrl(currentState.name, currentState.params);\n\n browser.replaceState(currentState as HistoryState, \"\", url);\n }\n } catch (recoveryError) {\n // If recovery fails, there's nothing more we can do\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n\n // Process any deferred events even after error\n processDeferredEvent();\n }\n }\n\n return {\n /**\n * Called when router.start() is invoked.\n * Sets up browser history integration.\n */\n onStart: () => {\n if (removePopStateListener) {\n removePopStateListener();\n }\n\n removePopStateListener = browser.addPopstateListener(\n (evt: PopStateEvent) => void onPopState(evt),\n options,\n );\n },\n\n /**\n * Called when router.stop() is invoked.\n * Cleans up event listeners.\n */\n onStop: () => {\n if (removePopStateListener) {\n removePopStateListener();\n removePopStateListener = undefined;\n }\n },\n\n /**\n * Called after successful navigation.\n * Updates browser history with new state.\n */\n onTransitionSuccess: (toState, fromState, navOptions) => {\n router.lastKnownState = toState;\n\n // Determine if we should replace or push history entry\n const replaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n router.areStatesEqual(toState, fromState, false));\n\n // Build URL with base and hash prefix\n const url = router.buildUrl(toState.name, toState.params);\n\n // Preserve hash fragment if configured\n // Note: preserveHash is deleted in hash mode, so it's always undefined there\n const shouldPreserveHash =\n options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash ? url + browser.getHash() : url;\n\n // Update browser history\n updateBrowserState(toState, finalUrl, replaceHistory, browser, options);\n },\n\n /**\n * Called when plugin is unsubscribed.\n * Restores original router state for clean teardown.\n */\n teardown: () => {\n // Remove event listeners\n if (removePopStateListener) {\n removePopStateListener();\n removePopStateListener = undefined;\n }\n\n // Restore original router methods\n router.start = routerStart;\n\n // Clean up added properties\n delete (router as Partial<Router>).buildUrl;\n delete (router as Partial<Router>).matchUrl;\n delete (router as Partial<Router>).replaceHistoryState;\n delete (router as Partial<Router>).lastKnownState;\n },\n };\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/utils.ts","../../src/browser.ts","../../src/plugin.ts"],"names":["defaultOptions","rawPath","safePath","getCachedRegExp","getBase"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCO,IAAM,cAAA,GAA8C;AAAA,EACzD,eAAA,EAAiB,IAAA;AAAA,EACjB,OAAA,EAAS,KAAA;AAAA,EACT,UAAA,EAAY,EAAA;AAAA,EACZ,IAAA,EAAM,EAAA;AAAA,EACN,UAAA,EAAY,KAAA;AAAA,EACZ,YAAA,EAAc;AAChB,CAAA;AAOO,IAAM,MAAA,GAAS,UAAA;AAEf,IAAM,cAAA,GAAiB,gBAAA;AC5B9B,IAAM,iBAAA,uBAAwB,GAAA,EAAoB;AAS3C,IAAM,YAAA,GAAe,CAAC,GAAA,KAAwB;AACnD,EAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,GAAA,CAAI,GAAG,CAAA;AAExC,EAAA,IAAI,WAAW,MAAA,EAAW;AACxB,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,UAAA,CAAW,sBAAA,EAAwB,OAAO,GAAA,CAAA,GAAA,CAAQ,CAAA;AAEtE,EAAA,iBAAA,CAAkB,GAAA,CAAI,KAAK,OAAO,CAAA;AAElC,EAAA,OAAO,OAAA;AACT,CAAA;AAWO,SAAS,oBAAA,CACd,GAAA,EACA,GAAA,EACA,OAAA,EACA,OAAA,EACmB;AACnB,EAAA,MAAM,UAAA,GAAa,CAAC,CAAA,CAAQ,GAAA,CAAI,KAAK,CAAA;AAErC,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,OAAO,GAAA,CAAI,SAAA,CAAU,OAAA,CAAQ,WAAA,CAAY,OAAO,CAAC,CAAA;AAAA,EACnD;AAEA,EAAA,OAAO,GAAA,CAAI,SAAA;AAAA,IACT,IAAI,KAAA,CAAM,IAAA;AAAA,IACV,IAAI,KAAA,CAAM,MAAA;AAAA,IACV,IAAI,KAAA,CAAM,IAAA;AAAA,IACV;AAAA,MACE,GAAG,IAAI,KAAA,CAAM,IAAA;AAAA,MACb,MAAA,EAAQ,GAAA,CAAI,KAAA,CAAM,IAAA,EAAM,UAAU,EAAC;AAAA,MACnC,OAAA,EAAS,GAAA,CAAI,KAAA,CAAM,IAAA,EAAM,WAAW;AAAC,KACvC;AAAA,IACA,GAAA,CAAI,MAAM,IAAA,EAAM;AAAA,GAClB;AACF;AAUO,SAAS,oBAAA,CACd,QAAA,EACA,YAAA,EACA,MAAA,EACS;AACT,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO,CAAC,EACN,YAAA,IAAgB,OAAO,cAAA,CAAe,QAAA,EAAU,cAAc,KAAK,CAAA,CAAA;AAEvE;AAUO,SAAS,kBAAA,CACd,MAAA,EACA,GAAA,EACA,iBAAA,EACS;AACT,EAAA,MAAM,aAAA,GAAgB,IAAI,UAAA,EAAW;AACrC,EAAA,MAAM,EAAE,cAAa,GAAI,aAAA;AAEzB,EAAA,IAAI,CAAC,YAAA,EAAc;AACjB,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,KAAK,OAAO,iBAAA,CAAkB;AAAA,IAC5B,GAAG,iBAAA;AAAA,IACH,MAAA,EAAQ,IAAA;AAAA,IACR,OAAA,EAAS;AAAA,GACV,CAAA;AAED,EAAA,OAAO,IAAA;AACT;AAWO,SAAS,kBAAA,CACd,KAAA,EACA,GAAA,EACA,OAAA,EACA,SACA,OAAA,EACM;AACN,EAAA,MAAM,YAAA,GAA6B;AAAA,IACjC,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,MAAM,KAAA,CAAM;AAAA,GACd;AAEA,EAAA,MAAM,UAAA,GACJ,OAAA,CAAQ,UAAA,IAAc,OAAA,CAAQ,QAAA,EAAS,GACnC,EAAE,GAAG,OAAA,CAAQ,QAAA,EAAS,EAAG,GAAG,cAAa,GACzC,YAAA;AAEN,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,OAAA,CAAQ,YAAA,CAAa,UAAA,EAAY,EAAA,EAAI,GAAG,CAAA;AAAA,EAC1C,CAAA,MAAO;AACL,IAAA,OAAA,CAAQ,SAAA,CAAU,UAAA,EAAY,EAAA,EAAI,GAAG,CAAA;AAAA,EACvC;AACF;AAgBO,SAAS,uBACd,GAAA,EACA,OAAA,EACA,WACA,UAAA,EACA,MAAA,EACA,SACA,OAAA,EACM;AAEN,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA;AAAA,EACF;AAGA,EAAA,IACE,IAAI,IAAA,KAAS,UAAA,CAAW,qBACxB,OAAA,IACA,SAAA,IACA,CAAC,UAAA,EACD;AACA,IAAA,MAAM,MAAM,MAAA,CAAO,QAAA,CAAS,SAAA,CAAU,IAAA,EAAM,UAAU,MAAM,CAAA;AAE5D,IAAA,kBAAA,CAAmB,SAAA,EAAW,GAAA,EAAK,IAAA,EAAM,OAAA,EAAS,OAAO,CAAA;AAAA,EAC3D;AACF;AAKA,SAAS,kBAAA,CACP,KACA,QAAA,EAC0C;AAC1C,EAAA,OAAO,GAAA,IAAO,QAAA;AAChB;AAKA,SAAS,kBAAA,CACP,GAAA,EACA,KAAA,EACA,YAAA,EACS;AACT,EAAA,MAAM,aAAa,OAAO,KAAA;AAE1B,EAAA,IAAI,UAAA,KAAe,YAAA,IAAgB,KAAA,KAAU,MAAA,EAAW;AACtD,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,IAAI,cAAc,CAAA,oBAAA,EAAuB,GAAG,CAAA,YAAA,EAAe,YAAY,SAAS,UAAU,CAAA;AAAA,KAC5F;AAEA,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,OAAO,IAAA;AACT;AAWO,SAAS,eAAA,CACd,MACAA,eAAAA,EACS;AACT,EAAA,IAAI,CAAC,IAAA,EAAM;AACT,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,eAAA,GAAkB,KAAA;AAItB,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,EAAG;AACnC,IAAA,IAAI,kBAAA,CAAmB,GAAA,EAAKA,eAAc,CAAA,EAAG;AAC3C,MAAA,MAAM,YAAA,GAAe,OAAOA,eAAAA,CAAe,GAAG,CAAA;AAC9C,MAAA,MAAM,KAAA,GAAQ,KAAK,GAAG,CAAA;AACtB,MAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,GAAA,EAAK,KAAA,EAAO,YAAY,CAAA;AAE3D,MAAA,IAAI,CAAC,OAAA,EAAS;AACZ,QAAA,eAAA,GAAkB,IAAA;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAKA,EAAA,IAAI,IAAA,CAAK,OAAA,KAAY,IAAA,IAAQ,cAAA,IAAkB,IAAA,EAAM;AACnD,IAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,CAAA,EAAI,cAAc,CAAA,mCAAA,CAAqC,CAAA;AAAA,EACtE;AAKA,EAAA,IAAI,IAAA,CAAK,OAAA,KAAY,KAAA,IAAS,YAAA,IAAgB,IAAA,EAAM;AAGlD,IAAA,MAAM,UAAA,GAAa,IAAA;AACnB,IAAA,MAAM,aAAa,UAAA,CAAW,UAAA;AAE9B,IAAA,IAAI,UAAA,KAAe,MAAA,IAAa,UAAA,KAAe,EAAA,EAAI;AACjD,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,CAAA,EAAI,cAAc,CAAA,oCAAA,CAAsC,CAAA;AAAA,IACvE;AAAA,EACF;AAEA,EAAA,OAAO,eAAA;AACT;;;AC1RA,IAAM,OAAO,MAAY;AAAC,CAAA;AAK1B,IAAM,OAAA,GAAU,MAAM,UAAA,CAAW,QAAA,CAAS,QAAA;AAO1C,IAAM,+CAAgC,CAAA,MAAM;AAC1C,EAAA,IAAI,eAAA;AACJ,EAAA,IAAI,YAAA,GAAe,KAAA;AAEnB,EAAA,OAAO,MAAe;AAGpB,IAAA,MAAM,gBAAA,GAAmB,WAAW,SAAA,CAAU,SAAA;AAG9C,IAAA,IAAI,qBAAqB,eAAA,EAAiB;AACxC,MAAA,eAAA,GAAkB,gBAAA;AAClB,MAAA,YAAA,GAAe,CAAC,gBAAA,CAAiB,QAAA,CAAS,SAAS,CAAA;AAAA,IACrD;AAEA,IAAA,OAAO,YAAA;AAAA,EACT,CAAA;AACF,CAAA,GAAG;AAKH,IAAM,SAAA,GAAY,CAAC,KAAA,EAAc,KAAA,EAAsB,IAAA,KAAuB;AAC5E,EAAA,UAAA,CAAW,OAAA,CAAQ,SAAA,CAAU,KAAA,EAAO,KAAA,IAAS,IAAI,IAAI,CAAA;AACvD,CAAA;AAKA,IAAM,YAAA,GAAe,CACnB,KAAA,EACA,KAAA,EACA,IAAA,KACG;AACH,EAAA,UAAA,CAAW,OAAA,CAAQ,YAAA,CAAa,KAAA,EAAO,KAAA,IAAS,IAAI,IAAI,CAAA;AAC1D,CAAA;AASA,IAAM,mBAAA,GAAsD,CAAC,EAAA,EAAI,IAAA,KAAS;AACxE,EAAA,MAAM,uBAAA,GACJ,IAAA,CAAK,OAAA,IAAW,CAAC,4BAAA,EAA6B;AAEhD,EAAA,UAAA,CAAW,gBAAA,CAAiB,YAAY,EAAkC,CAAA;AAE1E,EAAA,IAAI,uBAAA,EAAyB;AAC3B,IAAA,UAAA,CAAW,gBAAA;AAAA,MACT,YAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAM;AACX,IAAA,UAAA,CAAW,mBAAA;AAAA,MACT,UAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,IAAI,uBAAA,EAAyB;AAC3B,MAAA,UAAA,CAAW,mBAAA;AAAA,QACT,YAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AAAA,EACF,CAAA;AACF,CAAA;AAKA,IAAM,oBAAoB,MAAM;AAC9B,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAoB;AAEtC,EAAA,OAAO,CAAC,OAAA,KAA4B;AAClC,IAAA,IAAI,KAAA,GAAQ,KAAA,CAAM,GAAA,CAAI,OAAO,CAAA;AAE7B,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,KAAA,GAAQ,IAAI,OAAO,OAAO,CAAA;AAC1B,MAAA,KAAA,CAAM,GAAA,CAAI,SAAS,KAAK,CAAA;AAAA,IAC1B;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF,CAAA;AAEA,IAAM,kBAAkB,iBAAA,EAAkB;AAQ1C,IAAM,WAAA,GAAc,CAAC,IAAA,KAA+B;AAClD,EAAA,MAAM,EAAE,OAAA,EAAS,UAAA,GAAa,EAAA,EAAI,IAAA,GAAO,IAAG,GAAI,IAAA;AAGhD,EAAA,IAAI,CAAC,UAAA,IAAc,CAAC,IAAA,EAAM;AACxB,IAAA,MAAMC,QAAAA,GAAU,UACZ,UAAA,CAAW,QAAA,CAAS,KAAK,KAAA,CAAM,CAAC,CAAA,GAChC,UAAA,CAAW,QAAA,CAAS,QAAA;AACxB,IAAA,MAAMC,SAAAA,GAAW,iBAAiBD,QAAO,CAAA;AAEzC,IAAA,OAAA,CAAQC,SAAAA,IAAY,GAAA,IAAO,UAAA,CAAW,QAAA,CAAS,MAAA;AAAA,EACjD;AAEA,EAAA,MAAM,iBAAA,GAAoB,aAAa,UAAU,CAAA;AACjD,EAAA,MAAM,WAAA,GAAc,aAAa,IAAI,CAAA;AAErC,EAAA,MAAM,OAAA,GAAU,OAAA,GACZ,UAAA,CAAW,QAAA,CAAS,IAAA,CAAK,OAAA;AAAA,IACvB,eAAA,CAAgB,CAAA,EAAA,EAAK,iBAAiB,CAAA,CAAE,CAAA;AAAA,IACxC;AAAA,GACF,GACA,UAAA,CAAW,QAAA,CAAS,QAAA,CAAS,OAAA;AAAA,IAC3B,eAAA,CAAgB,CAAA,CAAA,EAAI,WAAW,CAAA,CAAE,CAAA;AAAA,IACjC;AAAA,GACF;AAEJ,EAAA,MAAM,QAAA,GAAW,iBAAiB,OAAO,CAAA;AAEzC,EAAA,OAAA,CAAQ,QAAA,IAAY,GAAA,IAAO,UAAA,CAAW,QAAA,CAAS,MAAA;AACjD,CAAA;AAQA,IAAM,gBAAA,GAAmB,CAAC,IAAA,KAAyB;AACjD,EAAA,IAAI;AACF,IAAA,OAAO,SAAA,CAAU,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,EAClC,SAAS,KAAA,EAAO;AACd,IAAA,MAAA,CAAO,IAAA,CAAK,cAAA,EAAgB,CAAA,uBAAA,EAA0B,IAAI,KAAK,KAAK,CAAA;AAEpE,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAA;AAQA,IAAM,WAAW,MAAgC;AAC/C,EAAA,IAAI,CAAC,UAAA,CAAW,OAAA,CAAQ,KAAA,EAAO;AAC7B,IAAA,OAAO,MAAA;AAAA,EACT;AAGA,EAAA,IAAI,CAAC,CAAA,CAAe,UAAA,CAAW,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC7C,IAAA,MAAA,CAAO,IAAA;AAAA,MACL,cAAA;AAAA,MACA,qDAAA;AAAA,MACA,WAAW,OAAA,CAAQ;AAAA,KACrB;AAEA,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,WAAW,OAAA,CAAQ,KAAA;AAC5B,CAAA;AAKA,IAAM,OAAA,GAAU,MAAM,UAAA,CAAW,QAAA,CAAS,IAAA;AAQ1C,SAAS,qBAAA,GAAiC;AACxC,EAAA,IAAI,SAAA,GAAY,KAAA;AAEhB,EAAA,MAAM,QAAA,GAAW,CAAC,MAAA,KAAmB;AACnC,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,MAAA,CAAO,IAAA;AAAA,QACL,cAAA;AAAA,QACA,mEACa,MAAM,CAAA,2GAAA;AAAA,OAErB;AACA,MAAA,SAAA,GAAY,IAAA;AAAA,IACd;AAAA,EACF,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,SAAS,MAAM;AACb,MAAA,QAAA,CAAS,SAAS,CAAA;AAElB,MAAA,OAAO,EAAA;AAAA,IACT,CAAA;AAAA,IACA,WAAW,MAAM;AACf,MAAA,QAAA,CAAS,WAAW,CAAA;AAAA,IACtB,CAAA;AAAA,IACA,cAAc,MAAM;AAClB,MAAA,QAAA,CAAS,cAAc,CAAA;AAAA,IACzB,CAAA;AAAA,IACA,qBAAqB,MAAM;AACzB,MAAA,QAAA,CAAS,qBAAqB,CAAA;AAE9B,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA,IACA,aAAa,MAAM;AACjB,MAAA,QAAA,CAAS,aAAa,CAAA;AAEtB,MAAA,OAAO,EAAA;AAAA,IACT,CAAA;AAAA,IACA,UAAU,MAAM;AACd,MAAA,QAAA,CAAS,UAAU,CAAA;AAGnB,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IACA,SAAS,MAAM;AACb,MAAA,QAAA,CAAS,SAAS,CAAA;AAElB,MAAA,OAAO,EAAA;AAAA,IACT;AAAA,GACF;AACF;AAOO,SAAS,iBAAA,GAA6B;AAC3C,EAAA,MAAM,YACJ,OAAO,UAAA,CAAW,WAAW,WAAA,IAAe,CAAC,CAAC,UAAA,CAAW,OAAA;AAE3D,EAAA,OAAO,SAAA,GACH;AAAA,IACE,OAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,mBAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,MAEF,qBAAA,EAAsB;AAC5B;;;AClOO,SAAS,oBAAA,CACd,IAAA,EACA,OAAA,GAAmB,iBAAA,EAAkB,EACtB;AAEf,EAAA,MAAM,eAAA,GAAkB,eAAA,CAAgB,IAAA,EAAM,cAAc,CAAA;AAE5D,EAAA,IAAI,OAAA,GAAU,EAAE,GAAG,cAAA,EAAgB,GAAG,IAAA,EAAK;AAG3C,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN,IAAI,cAAc,CAAA,4CAAA;AAAA,KACpB;AACA,IAAA,OAAA,GAAU,EAAE,GAAG,cAAA,EAAe;AAAA,EAChC;AAIA,EAAA,IAAI,OAAA,CAAQ,YAAY,IAAA,EAAM;AAE5B,IAAA,OAAQ,OAAA,CAA+C,YAAA;AAAA,EACzD,CAAA,MAAO;AAEL,IAAA,OAAQ,OAAA,CAA+C,UAAA;AAAA,EACzD;AAIA,EAAA,IAAI,OAAA,CAAQ,IAAA,IAAQ,OAAO,OAAA,CAAQ,SAAS,QAAA,EAAU;AAEpD,IAAA,IAAI,CAAC,OAAA,CAAQ,IAAA,CAAK,UAAA,CAAW,GAAG,CAAA,EAAG;AACjC,MAAA,OAAA,CAAQ,IAAA,GAAO,CAAA,CAAA,EAAI,OAAA,CAAQ,IAAI,CAAA,CAAA;AAAA,IACjC;AAGA,IAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,QAAA,CAAS,GAAG,CAAA,EAAG;AAC9B,MAAA,OAAA,CAAQ,IAAA,GAAO,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,EAAE,CAAA;AAAA,IACzC;AAAA,EACF;AAGA,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAC5C,EAAA,MAAMC,gBAAAA,GAAkB,CAAC,OAAA,KAA4B;AACnD,IAAA,MAAM,MAAA,GAAS,WAAA,CAAY,GAAA,CAAI,OAAO,CAAA;AAEtC,IAAA,IAAI,WAAW,MAAA,EAAW;AACxB,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,SAAA,GAAY,IAAI,MAAA,CAAO,OAAO,CAAA;AAEpC,IAAA,WAAA,CAAY,GAAA,CAAI,SAAS,SAAS,CAAA;AAElC,IAAA,OAAO,SAAA;AAAA,EACT,CAAA;AAIA,EAAA,MAAM,kBAAkB,OAAA,CAAQ,eAAA;AAEhC,EAAA,MAAM,iBAAA,GACJ,eAAA,KAAoB,MAAA,GAChB,EAAE,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAK,GACxB,EAAE,eAAA,EAAiB,MAAA,EAAQ,OAAA,EAAS,IAAA,EAAK;AAE/C,EAAA,IAAI,sBAAA;AAEJ,EAAA,OAAO,SAAS,cAAc,UAAA,EAAY;AAExC,IAAA,MAAM,MAAA,GAAS,UAAA;AACf,IAAA,MAAM,GAAA,GAAM,aAAa,UAAU,CAAA;AAInC,IAAA,MAAM,cAAc,MAAA,CAAO,KAAA;AAG3B,IAAA,IAAI,eAAA,GAAkB,KAAA;AAGtB,IAAA,IAAI,qBAAA,GAA8C,IAAA;AAGlD,IAAA,IAAI,iBAAA;AAIJ,IAAA,MAAMC,QAAAA,GAAU,MAAM,OAAA,CAAQ,IAAA,IAAQ,EAAA;AAEtC,IAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,EAAA;AACzC,IAAA,MAAM,iBAAA,GAAoB,aAAa,UAAU,CAAA;AACjD,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,OAAA,GAAU,CAAA,CAAA,EAAI,UAAU,CAAA,CAAA,GAAK,EAAA;AAGpD,IAAA,MAAM,mBAAmB,iBAAA,GACrBD,gBAAAA,CAAgB,CAAA,EAAA,EAAK,iBAAiB,EAAE,CAAA,GACxC,IAAA;AASJ,IAAA,MAAM,SAAA,GAAY,CAAC,GAAA,KAA+B;AAChD,MAAA,IAAI;AAEF,QAAA,MAAM,YAAY,IAAI,GAAA,CAAI,GAAA,EAAK,UAAA,CAAW,SAAS,MAAM,CAAA;AACzD,QAAA,MAAM,WAAW,SAAA,CAAU,QAAA;AAC3B,QAAA,MAAM,OAAO,SAAA,CAAU,IAAA;AACvB,QAAA,MAAM,SAAS,SAAA,CAAU,MAAA;AACzB,QAAA,MAAM,OAAOC,QAAAA,EAAQ;AAErB,QAAA,IAAI,CAAC,CAAC,OAAA,EAAS,QAAQ,EAAE,QAAA,CAAS,SAAA,CAAU,QAAQ,CAAA,EAAG;AACrD,UAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,CAAA,EAAI,cAAc,CAAA,0BAAA,EAA6B,GAAG,CAAA,CAAE,CAAA;AAEjE,UAAA,OAAO,IAAA;AAAA,QACT;AAEA,QAAA,IAAI,QAAQ,OAAA,EAAS;AAEnB,UAAA,MAAM,IAAA,GAAO,mBACT,IAAA,CAAK,OAAA,CAAQ,kBAAkB,EAAE,CAAA,GACjC,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA;AAEhB,UAAA,OAAO,IAAA,GAAO,MAAA;AAAA,QAChB,WAAW,IAAA,EAAM;AAEf,UAAA,MAAM,WAAA,GAAc,aAAa,IAAI,CAAA;AACrC,UAAA,MAAM,UAAA,GAAaD,gBAAAA,CAAgB,CAAA,CAAA,EAAI,WAAW,CAAA,CAAE,CAAA;AACpD,UAAA,MAAM,QAAA,GAAW,QAAA,CAAS,OAAA,CAAQ,UAAA,EAAY,EAAE,CAAA;AAEhD,UAAA,OAAA,CAAQ,SAAS,UAAA,CAAW,GAAG,CAAA,GAAI,EAAA,GAAK,OAAO,QAAA,GAAW,MAAA;AAAA,QAC5D;AAEA,QAAA,OAAO,QAAA,GAAW,MAAA;AAAA,MACpB,SAAS,KAAA,EAAO;AAEd,QAAA,OAAA,CAAQ,KAAK,CAAA,CAAA,EAAI,cAAc,CAAA,sBAAA,EAAyB,GAAG,IAAI,KAAK,CAAA;AAEpE,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF,CAAA;AAMA,IAAA,MAAA,CAAO,KAAA,GAAQ,CAAC,IAAA,KAAkB;AAChC,MAAA,OAAO,WAAA,CAAY,IAAA,IAAQ,OAAA,CAAQ,WAAA,CAAY,OAAO,CAAC,CAAA;AAAA,IACzD,CAAA;AAsBA,IAAA,MAAA,CAAO,QAAA,GAAW,CAAC,KAAA,EAAO,MAAA,KAAW;AACnC,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,SAAA,CAAU,KAAA,EAAO,MAAM,CAAA;AAE3C,MAAA,OAAOC,QAAAA,KAAY,MAAA,GAAS,IAAA;AAAA,IAC9B,CAAA;AAKA,IAAA,MAAA,CAAO,QAAA,GAAW,CAAC,GAAA,KAAQ;AACzB,MAAA,MAAM,IAAA,GAAO,UAAU,GAAG,CAAA;AAE1B,MAAA,OAAO,IAAA,GAAO,GAAA,CAAI,SAAA,CAAU,IAAI,CAAA,GAAI,MAAA;AAAA,IACtC,CAAA;AAMA,IAAA,MAAA,CAAO,mBAAA,GAAsB,CAAC,IAAA,EAAM,MAAA,GAAS,EAAC,KAAM;AAClD,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,UAAA,CAAW,IAAA,EAAM,MAAM,CAAA;AAEzC,MAAA,IAAI,CAAC,KAAA,EAAO;AACV,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,8CAA8C,IAAI,CAAA,cAAA;AAAA,SACpD;AAAA,MACF;AAEA,MAAA,MAAM,aAAa,GAAA,CAAI,SAAA;AAAA,QACrB,KAAA,CAAM,IAAA;AAAA,QACN,KAAA,CAAM,MAAA;AAAA,QACN,MAAA,CAAO,SAAA,CAAU,KAAA,CAAM,IAAA,EAAM,MAAM,MAAM,CAAA;AAAA,QACzC;AAAA,UACE,QAAQ,KAAA,CAAM,IAAA;AAAA,UACd,SAAS;AAAC,SACZ;AAAA,QACA;AAAA;AAAA,OACF;AACA,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA;AAExC,MAAA,kBAAA,CAAmB,UAAA,EAAY,GAAA,EAAK,IAAA,EAAM,OAAA,EAAS,OAAO,CAAA;AAAA,IAC5D,CAAA;AAOA,IAAA,MAAA,CAAO,cAAA,CAAe,QAAQ,gBAAA,EAAkB;AAAA,MAC9C,GAAA,GAAM;AAGJ,QAAA,OAAO,iBAAA;AAAA,MACT,CAAA;AAAA,MACA,IAAI,KAAA,EAAe;AAEjB,QAAA,iBAAA,GAAoB,QAAQ,MAAA,CAAO,MAAA,CAAO,EAAE,GAAG,KAAA,EAAO,CAAA,GAAI,MAAA;AAAA,MAC5D,CAAA;AAAA,MACA,UAAA,EAAY,IAAA;AAAA,MACZ,YAAA,EAAc;AAAA,KACf,CAAA;AAMD,IAAA,SAAS,oBAAA,GAAuB;AAC9B,MAAA,IAAI,qBAAA,EAAuB;AACzB,QAAA,MAAM,KAAA,GAAQ,qBAAA;AAEd,QAAA,qBAAA,GAAwB,IAAA;AACxB,QAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,CAAA,EAAI,cAAc,CAAA,oCAAA,CAAsC,CAAA;AACrE,QAAA,KAAK,WAAW,KAAK,CAAA;AAAA,MACvB;AAAA,IACF;AAOA,IAAA,eAAe,WAAW,GAAA,EAAoB;AAE5C,MAAA,IAAI,eAAA,EAAiB;AACnB,QAAA,OAAA,CAAQ,IAAA;AAAA,UACN,IAAI,cAAc,CAAA,kDAAA;AAAA,SACpB;AAEA,QAAA,qBAAA,GAAwB,GAAA;AAExB,QAAA;AAAA,MACF;AAGA,MAAA,IAAI;AACF,QAAA,MAAM,WAAA,GAAc,OAAO,QAAA,EAAS;AACpC,QAAA,MAAM,KAAA,GAAQ,oBAAA,CAAqB,GAAA,EAAK,GAAA,EAAK,SAAS,OAAO,CAAA;AAC7D,QAAA,MAAM,UAAA,GAAa,CAAC,CAAA,CAAQ,GAAA,CAAI,KAAK,CAAA;AAGrC,QAAA,IAAI,CAAC,KAAA,IAAS,kBAAA,CAAmB,MAAA,EAAQ,GAAA,EAAK,iBAAiB,CAAA,EAAG;AAChE,UAAA;AAAA,QACF;AAGA,QAAA,IAAI,oBAAA,CAAqB,KAAA,EAAO,WAAA,EAAa,MAAM,CAAA,EAAG;AACpD,UAAA;AAAA,QACF;AAOA,QAAA,IAAI,CAAC,KAAA,EAAO;AACV,UAAA;AAAA,QACF;AAGA,QAAA,eAAA,GAAkB,IAAA;AAElB,QAAA,IAAI;AAEF,UAAA,MAAM,OAAA,GAAU,MAAM,GAAA,CAAI,eAAA;AAAA,YACxB,KAAA;AAAA,YACA,WAAA;AAAA,YACA;AAAA,WACF;AAEA,UAAA,sBAAA;AAAA,YACE,KAAA,CAAA;AAAA,YACA,OAAA;AAAA,YACA,WAAA;AAAA,YACA,UAAA;AAAA,YACA,MAAA;AAAA,YACA,OAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF,SAAS,KAAA,EAAO;AACd,UAAA,sBAAA;AAAA,YACE,KAAA;AAAA,YACA,KAAA,CAAA;AAAA,YACA,WAAA;AAAA,YACA,UAAA;AAAA,YACA,MAAA;AAAA,YACA,OAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF,CAAA,SAAE;AACA,UAAA,eAAA,GAAkB,KAAA;AAElB,UAAA,oBAAA,EAAqB;AAAA,QACvB;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,eAAA,GAAkB,KAAA;AAClB,QAAA,OAAA,CAAQ,KAAA;AAAA,UACN,IAAI,cAAc,CAAA,8BAAA,CAAA;AAAA,UAClB;AAAA,SACF;AAGA,QAAA,IAAI;AACF,UAAA,MAAM,YAAA,GAAe,OAAO,QAAA,EAAS;AAErC,UAAA,IAAI,YAAA,EAAc;AAChB,YAAA,MAAM,MAAM,MAAA,CAAO,QAAA,CAAS,YAAA,CAAa,IAAA,EAAM,aAAa,MAAM,CAAA;AAElE,YAAA,OAAA,CAAQ,YAAA,CAAa,YAAA,EAA8B,EAAA,EAAI,GAAG,CAAA;AAAA,UAC5D;AAAA,QACF,SAAS,aAAA,EAAe;AAEtB,UAAA,OAAA,CAAQ,KAAA;AAAA,YACN,IAAI,cAAc,CAAA,uCAAA,CAAA;AAAA,YAClB;AAAA,WACF;AAAA,QACF;AAGA,QAAA,oBAAA,EAAqB;AAAA,MACvB;AAAA,IACF;AAEA,IAAA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAKL,SAAS,MAAM;AACb,QAAA,IAAI,sBAAA,EAAwB;AAC1B,UAAA,sBAAA,EAAuB;AAAA,QACzB;AAEA,QAAA,sBAAA,GAAyB,OAAA,CAAQ,mBAAA;AAAA,UAC/B,CAAC,GAAA,KAAuB,KAAK,UAAA,CAAW,GAAG,CAAA;AAAA,UAC3C;AAAA,SACF;AAAA,MACF,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,QAAQ,MAAM;AACZ,QAAA,IAAI,sBAAA,EAAwB;AAC1B,UAAA,sBAAA,EAAuB;AACvB,UAAA,sBAAA,GAAyB,MAAA;AAAA,QAC3B;AAAA,MACF,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,mBAAA,EAAqB,CAAC,OAAA,EAAS,SAAA,EAAW,UAAA,KAAe;AACvD,QAAA,MAAA,CAAO,cAAA,GAAiB,OAAA;AAGxB,QAAA,MAAM,cAAA,GAAA,CACH,UAAA,CAAW,OAAA,IAAW,CAAC,SAAA,KACvB,CAAC,CAAC,UAAA,CAAW,MAAA,IACZ,MAAA,CAAO,cAAA,CAAe,OAAA,EAAS,WAAW,KAAK,CAAA;AAGnD,QAAA,MAAM,MAAM,MAAA,CAAO,QAAA,CAAS,OAAA,CAAQ,IAAA,EAAM,QAAQ,MAAM,CAAA;AAIxD,QAAA,MAAM,qBACJ,OAAA,CAAQ,YAAA,KACP,CAAC,SAAA,IAAa,SAAA,CAAU,SAAS,OAAA,CAAQ,IAAA,CAAA;AAE5C,QAAA,MAAM,QAAA,GAAW,kBAAA,GAAqB,GAAA,GAAM,OAAA,CAAQ,SAAQ,GAAI,GAAA;AAGhE,QAAA,kBAAA,CAAmB,OAAA,EAAS,QAAA,EAAU,cAAA,EAAgB,OAAA,EAAS,OAAO,CAAA;AAAA,MACxE,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,UAAU,MAAM;AAEd,QAAA,IAAI,sBAAA,EAAwB;AAC1B,UAAA,sBAAA,EAAuB;AACvB,UAAA,sBAAA,GAAyB,MAAA;AAAA,QAC3B;AAGA,QAAA,MAAA,CAAO,KAAA,GAAQ,WAAA;AAGf,QAAA,OAAQ,MAAA,CAA2B,QAAA;AACnC,QAAA,OAAQ,MAAA,CAA2B,QAAA;AACnC,QAAA,OAAQ,MAAA,CAA2B,mBAAA;AACnC,QAAA,OAAQ,MAAA,CAA2B,cAAA;AAAA,MACrC;AAAA,KACF;AAAA,EACF,CAAA;AACF","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 mergeState: boolean;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n mergeState: false,\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/modules/utils.ts\n\nimport { errorCodes } from \"@real-router/core\";\nimport { isStateStrict as isState } from \"type-guards\";\n\nimport { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions, HistoryState, Browser } from \"./types\";\nimport type {\n PluginApi,\n Router,\n NavigationOptions,\n RouterError,\n State,\n} from \"@real-router/core\";\n\n/**\n * No-op function for default callbacks\n */\nexport const noop = (): void => undefined;\n\n/**\n * Cache for escaped RegExp strings\n */\nconst escapeRegExpCache = new Map<string, string>();\n\n/**\n * Escapes special RegExp characters in a string.\n * Used to safely build RegExp from user-provided strings (hashPrefix, base).\n *\n * @param str - String to escape\n * @returns Escaped string safe for RegExp construction\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\n/**\n * Creates state from popstate event\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 Router state or undefined\n */\nexport function createStateFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n options: BrowserPluginOptions,\n): State | undefined {\n const isNewState = !isState(evt.state);\n\n if (isNewState) {\n return api.matchPath(browser.getLocation(options));\n }\n\n return api.makeState(\n evt.state.name,\n evt.state.params,\n evt.state.path,\n {\n ...evt.state.meta,\n params: evt.state.meta?.params ?? {},\n options: evt.state.meta?.options ?? {},\n },\n evt.state.meta?.id,\n );\n}\n\n/**\n * Checks if transition should be skipped (same states)\n *\n * @param newState - New state from event\n * @param currentState - Current router state\n * @param router - Router instance\n * @returns true if transition should be skipped\n */\nexport function shouldSkipTransition(\n newState: State | undefined,\n currentState: State | undefined,\n router: Router,\n): boolean {\n if (!newState) {\n return true;\n }\n\n return !!(\n currentState && router.areStatesEqual(newState, currentState, false)\n );\n}\n\n/**\n * Handles missing state by navigating to default route\n *\n * @param router - Router instance\n * @param api - Plugin API instance\n * @param transitionOptions - Options for transition\n * @returns true if handled, false if no default route\n */\nexport function handleMissingState(\n router: Router,\n api: PluginApi,\n transitionOptions: NavigationOptions,\n): boolean {\n const routerOptions = api.getOptions();\n const { defaultRoute } = routerOptions;\n\n if (!defaultRoute) {\n return false;\n }\n\n void router.navigateToDefault({\n ...transitionOptions,\n reload: true,\n replace: true,\n });\n\n return true;\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 * @param options - Browser plugin options\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n options: BrowserPluginOptions,\n): void {\n const trimmedState: HistoryState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n const finalState: HistoryState =\n options.mergeState && browser.getState()\n ? { ...browser.getState(), ...trimmedState }\n : trimmedState;\n\n if (replace) {\n browser.replaceState(finalState, \"\", url);\n } else {\n browser.pushState(finalState, \"\", url);\n }\n}\n\n/**\n * Handles transition result (success or error)\n *\n * Success case is handled by the router FSM chain (TRANSITION_SUCCESS event).\n * This function only handles error cases that need URL restoration.\n *\n * @param err - Router error or undefined if successful\n * @param toState - Target state\n * @param fromState - Source state\n * @param isNewState - Whether this is a new state (not from history)\n * @param router - Router instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n */\nexport function handleTransitionResult(\n err: RouterError | undefined,\n toState: State | undefined,\n fromState: State | undefined,\n isNewState: boolean,\n router: Router,\n browser: Browser,\n options: BrowserPluginOptions,\n): void {\n // Success case handled by the router FSM chain (TRANSITION_SUCCESS event)\n if (!err) {\n return;\n }\n\n // Handle CANNOT_DEACTIVATE - restore previous URL\n if (\n err.code === errorCodes.CANNOT_DEACTIVATE &&\n toState &&\n fromState &&\n !isNewState\n ) {\n const url = router.buildUrl(fromState.name, fromState.params);\n\n updateBrowserState(fromState, url, true, browser, options);\n }\n}\n\n/**\n * Type guard to check if a key exists in default options\n */\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\n/**\n * Validates that an option value has the correct type\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\n/**\n * Validates browser plugin options and warns about conflicting configurations.\n * TypeScript types prevent conflicts at compile-time, but runtime validation\n * is needed for JavaScript users and dynamic configurations.\n *\n * IMPORTANT: This validates only user-provided options, not merged defaults.\n *\n * @returns true if invalid types detected, false otherwise\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 // Validate option types against defaults\n // Using Object.keys ensures we only check properties that actually exist\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 // Check for hash mode conflicts\n // Runtime validation for JS users - TypeScript prevents this at compile time\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n // Check for history mode conflicts\n // Runtime validation for JS users - TypeScript prevents this at compile time\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n // Single type assertion needed: TypeScript narrows opts to HistoryModeOptions\n // where hashPrefix is 'never', but we need to check it at runtime for JS users\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","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\nimport { isHistoryState } from \"type-guards\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { escapeRegExp } from \"./utils\";\n\nimport type { Browser, BrowserPluginOptions, HistoryState } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\n/**\n * Returns current base path from browser location\n */\nconst getBase = () => globalThis.location.pathname;\n\n/**\n * Detects if browser supports popstate events on hash changes.\n * Old IE (Trident engine) doesn't fire popstate on hashchange.\n * Uses memoization based on userAgent for performance while remaining testable.\n */\nconst supportsPopStateOnHashChange = (() => {\n let cachedUserAgent: string | undefined;\n let cachedResult = false;\n\n return (): boolean => {\n // Note: This function is only called from real browser's addPopstateListener,\n // never from fallback browser (SSR), so window is guaranteed to exist\n const currentUserAgent = globalThis.navigator.userAgent;\n\n // Only recalculate if userAgent changed (or first call)\n if (currentUserAgent !== cachedUserAgent) {\n cachedUserAgent = currentUserAgent;\n cachedResult = !currentUserAgent.includes(\"Trident\");\n }\n\n return cachedResult;\n };\n})();\n\n/**\n * Pushes new state to browser history\n */\nconst pushState = (state: State, title: string | null, path: string | URL) => {\n globalThis.history.pushState(state, title ?? \"\", path);\n};\n\n/**\n * Replaces current state in browser history\n */\nconst replaceState = (\n state: State,\n title: string | null,\n path: string | URL,\n) => {\n globalThis.history.replaceState(state, title ?? \"\", path);\n};\n\n/**\n * Adds popstate/hashchange event listeners based on browser capabilities\n *\n * @param fn - Event handler function\n * @param opts - Browser plugin options\n * @returns Cleanup function to remove listeners\n */\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn, opts) => {\n const needsHashChangeListener =\n opts.useHash && !supportsPopStateOnHashChange();\n\n globalThis.addEventListener(\"popstate\", fn as (evt: PopStateEvent) => void);\n\n if (needsHashChangeListener) {\n globalThis.addEventListener(\n \"hashchange\",\n fn as (evt: HashChangeEvent) => void,\n );\n }\n\n return () => {\n globalThis.removeEventListener(\n \"popstate\",\n fn as (evt: PopStateEvent) => void,\n );\n\n if (needsHashChangeListener) {\n globalThis.removeEventListener(\n \"hashchange\",\n fn as (evt: HashChangeEvent) => void,\n );\n }\n };\n};\n\n/**\n * Creates RegExp cache for getLocation optimization\n */\nconst createRegExpCache = () => {\n const cache = new Map<string, RegExp>();\n\n return (pattern: string): RegExp => {\n let regex = cache.get(pattern);\n\n if (!regex) {\n regex = new RegExp(pattern);\n cache.set(pattern, regex);\n }\n\n return regex;\n };\n};\n\nconst getCachedRegExp = createRegExpCache();\n\n/**\n * Gets current location path from browser, respecting plugin options\n *\n * @param opts - Browser plugin options\n * @returns Current path string\n */\nconst getLocation = (opts: BrowserPluginOptions) => {\n const { useHash, hashPrefix = \"\", base = \"\" } = opts;\n\n // Optimization: skip RegExp for empty values\n if (!hashPrefix && !base) {\n const rawPath = useHash\n ? globalThis.location.hash.slice(1)\n : globalThis.location.pathname;\n const safePath = safelyEncodePath(rawPath);\n\n return (safePath || \"/\") + globalThis.location.search;\n }\n\n const escapedHashPrefix = escapeRegExp(hashPrefix);\n const escapedBase = escapeRegExp(base);\n\n const rawPath = useHash\n ? globalThis.location.hash.replace(\n getCachedRegExp(`^#${escapedHashPrefix}`),\n \"\",\n )\n : globalThis.location.pathname.replace(\n getCachedRegExp(`^${escapedBase}`),\n \"\",\n );\n\n const safePath = safelyEncodePath(rawPath);\n\n return (safePath || \"/\") + globalThis.location.search;\n};\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\n/**\n * Gets current history state with validation.\n * Returns undefined instead of throwing for safer error handling.\n *\n * @returns Valid history state or undefined\n */\nconst getState = (): HistoryState | undefined => {\n if (!globalThis.history.state) {\n return undefined;\n }\n\n // Validate state structure instead of throwing\n if (!isHistoryState(globalThis.history.state)) {\n logger.warn(\n LOGGER_CONTEXT,\n \"History state is not a valid state object, ignoring\",\n globalThis.history.state,\n );\n\n return undefined;\n }\n\n return globalThis.history.state as HistoryState;\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 getBase: () => {\n warnOnce(\"getBase\");\n\n return \"\";\n },\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 getState: () => {\n warnOnce(\"getState\");\n\n // eslint-disable-next-line unicorn/no-useless-undefined\n return undefined;\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 getBase,\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getState,\n getHash,\n }\n : createFallbackBrowser();\n}\n","// packages/browser-plugin/modules/plugin.ts\n\nimport { getPluginApi } from \"@real-router/core\";\nimport { isStateStrict as isState } from \"type-guards\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport {\n escapeRegExp,\n createStateFromEvent,\n shouldSkipTransition,\n handleMissingState,\n updateBrowserState,\n handleTransitionResult,\n validateOptions,\n} from \"./utils\";\n\nimport type { BrowserPluginOptions, Browser, HistoryState } from \"./types\";\nimport type {\n PluginFactory,\n Router,\n RouterError,\n State,\n} from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * Features:\n * - Syncs router state with browser history (pushState/replaceState)\n * - Handles popstate events for browser back/forward navigation\n * - Supports hash-based routing for legacy browsers\n * - Provides URL building and matching utilities\n * - SSR-safe with graceful fallbacks\n * - Runtime validation warns about conflicting options\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 // Validate user-provided options before merging with defaults\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n // Skip normalization if invalid types detected (prevents runtime errors)\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 // Remove conflicting properties based on mode to prevent misuse\n // This ensures options object is clean even if JS users pass invalid config\n if (options.useHash === true) {\n // Hash mode: remove history-only options\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n // History mode (default): remove hash-only options\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n // Normalize base path to prevent common configuration errors\n // Type check needed for runtime safety (JS users may pass wrong types)\n if (options.base && typeof options.base === \"string\") {\n // Ensure leading slash for absolute paths\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n // Remove trailing slash to prevent double slashes\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n // Cache RegExp patterns at plugin creation for performance\n const regExpCache = new Map<string, RegExp>();\n const getCachedRegExp = (pattern: string): RegExp => {\n const cached = regExpCache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n regExpCache.set(pattern, newRegExp);\n\n return newRegExp;\n };\n\n // Create transition options with proper typing for exactOptionalPropertyTypes\n // replace: true is needed because popstate means URL already changed (back/forward)\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 }\n : { forceDeactivate, source, replace: true };\n\n let removePopStateListener: (() => void) | undefined;\n\n return function browserPlugin(routerBase) {\n // Cast to augmented Router (class + module augmentation: buildUrl, matchUrl, etc.)\n const router = routerBase as Router;\n const api = getPluginApi(routerBase);\n\n // Store original methods for restoration on teardown\n\n const routerStart = router.start;\n\n // Transition state management\n let isTransitioning = false;\n\n // Deferred popstate event queue (stores only the last event)\n let deferredPopstateEvent: PopStateEvent | null = null;\n\n // Frozen copy of lastKnownState for immutability\n let cachedFrozenState: State | undefined;\n\n // Options can be changed at runtime in onStart\n /* v8 ignore next -- @preserve fallback for undefined base */\n const getBase = () => options.base ?? \"\";\n /* v8 ignore next -- @preserve fallback for undefined hashPrefix */\n const hashPrefix = options.hashPrefix ?? \"\";\n const escapedHashPrefix = escapeRegExp(hashPrefix);\n const prefix = options.useHash ? `#${hashPrefix}` : \"\";\n\n // Pre-compute RegExp patterns\n const hashPrefixRegExp = escapedHashPrefix\n ? getCachedRegExp(`^#${escapedHashPrefix}`)\n : null;\n\n /**\n * Parses URL and extracts path using native URL API.\n * More robust than regex parsing - handles IPv6, Unicode, edge cases.\n *\n * @param url - URL to parse\n * @returns Path string or null on parse error\n */\n const urlToPath = (url: string): string | null => {\n try {\n // Use URL API for reliable parsing\n const parsedUrl = new URL(url, globalThis.location.origin);\n const pathname = parsedUrl.pathname;\n const hash = parsedUrl.hash;\n const search = parsedUrl.search;\n const base = getBase();\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 if (options.useHash) {\n // Use cached RegExp or simple slice if no prefix\n const path = hashPrefixRegExp\n ? hash.replace(hashPrefixRegExp, \"\")\n : hash.slice(1);\n\n return path + search;\n } else if (base) {\n // Remove base prefix\n const escapedBase = escapeRegExp(base);\n const baseRegExp = getCachedRegExp(`^${escapedBase}`);\n const stripped = pathname.replace(baseRegExp, \"\");\n\n return (stripped.startsWith(\"/\") ? \"\" : \"/\") + stripped + search;\n }\n\n return pathname + search;\n } catch (error) {\n // Graceful fallback instead of throw\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n };\n\n /**\n * Overrides router.start to integrate with browser location.\n * When no path is provided, resolves current browser URL automatically.\n */\n router.start = (path?: string) => {\n return routerStart(path ?? browser.getLocation(options));\n };\n\n /**\n * Builds URL from route name and params.\n * Adds base path and hash prefix according to options.\n *\n * @security\n * When using buildUrl output in templates:\n * - ✅ SAFE: Modern frameworks (React, Vue, Angular) auto-escape in templates\n * - ✅ SAFE: Setting href attribute via DOM API (element.href = url)\n * - ❌ UNSAFE: Using innerHTML or similar without escaping\n *\n * @example\n * // Safe - React auto-escapes\n * <Link to={router.buildUrl('users', params)} />\n *\n * // Safe - Vue auto-escapes\n * <router-link :to=\"router.buildUrl('users', params)\" />\n *\n * // Unsafe - manual HTML construction\n * element.innerHTML = `<a href=\"${router.buildUrl('users', params)}\">Link</a>`; // ❌ DON'T\n */\n router.buildUrl = (route, params) => {\n const path = router.buildPath(route, params);\n\n return getBase() + prefix + path;\n };\n\n /**\n * Matches URL and returns corresponding state\n */\n router.matchUrl = (url) => {\n const path = urlToPath(url);\n\n return path ? api.matchPath(path) : undefined;\n };\n\n /**\n * Replaces current history state without triggering navigation.\n * Useful for updating URL without causing a full transition.\n */\n router.replaceHistoryState = (name, params = {}) => {\n const state = 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 = api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n options: {},\n },\n 1, // forceId\n );\n const url = router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, browser, options);\n };\n\n /**\n * lastKnownState: Immutable reference to last successful state.\n * Uses caching to avoid creating new objects on every read.\n * Optimized: Single copy + freeze operation instead of double copying.\n */\n Object.defineProperty(router, \"lastKnownState\", {\n get() {\n // Note: After teardown, this property is deleted from router,\n // so this getter is only called while plugin is active\n return cachedFrozenState;\n },\n set(value?: State) {\n // Create frozen copy in one operation (no double copying)\n cachedFrozenState = value ? Object.freeze({ ...value }) : undefined;\n },\n enumerable: true,\n configurable: true,\n });\n\n /**\n * Processes a deferred popstate event if one exists.\n * Called after transition completes.\n */\n function processDeferredEvent() {\n if (deferredPopstateEvent) {\n const event = deferredPopstateEvent;\n\n deferredPopstateEvent = null; // Clear before processing\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void onPopState(event);\n }\n }\n\n /**\n * Main popstate event handler.\n * Protected against concurrent transitions and handles errors gracefully.\n * Defers events during transitions to prevent browser history desync.\n */\n async function onPopState(evt: PopStateEvent) {\n // Race condition protection: defer event if transition in progress\n if (isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n // Store only the latest event (skip intermediate states)\n deferredPopstateEvent = evt;\n\n return;\n }\n\n // Top-level error recovery\n try {\n const routerState = router.getState();\n const state = createStateFromEvent(evt, api, browser, options);\n const isNewState = !isState(evt.state);\n\n // Handle missing state\n if (!state && handleMissingState(router, api, transitionOptions)) {\n return;\n }\n\n // Skip if states are equal\n if (shouldSkipTransition(state, routerState, router)) {\n return;\n }\n\n // Execute transition with race protection\n // state is guaranteed to be defined here because:\n // 1. handleMissingState handles !state case (line 339)\n // 2. shouldSkipTransition returns true when !state (utils.ts:129)\n /* v8 ignore start: defensive guard - state guaranteed defined by control flow above */\n if (!state) {\n return;\n }\n /* v8 ignore stop */\n\n isTransitioning = true;\n\n try {\n // transitionOptions includes replace: true, which is passed to TRANSITION_SUCCESS\n const toState = await api.navigateToState(\n state,\n routerState,\n transitionOptions,\n );\n\n handleTransitionResult(\n undefined,\n toState,\n routerState,\n isNewState,\n router,\n browser,\n options,\n );\n } catch (error) {\n handleTransitionResult(\n error as RouterError,\n undefined,\n routerState,\n isNewState,\n router,\n browser,\n options,\n );\n } finally {\n isTransitioning = false;\n // Process any deferred popstate events after transition completes\n processDeferredEvent();\n }\n } catch (error) {\n isTransitioning = false;\n console.error(\n `[${LOGGER_CONTEXT}] Critical error in onPopState`,\n error,\n );\n\n // Attempt recovery: sync browser with router state\n try {\n const currentState = router.getState();\n\n if (currentState) {\n const url = router.buildUrl(currentState.name, currentState.params);\n\n browser.replaceState(currentState as HistoryState, \"\", url);\n }\n } catch (recoveryError) {\n // If recovery fails, there's nothing more we can do\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n\n // Process any deferred events even after error\n processDeferredEvent();\n }\n }\n\n return {\n /**\n * Called when router.start() is invoked.\n * Sets up browser history integration.\n */\n onStart: () => {\n if (removePopStateListener) {\n removePopStateListener();\n }\n\n removePopStateListener = browser.addPopstateListener(\n (evt: PopStateEvent) => void onPopState(evt),\n options,\n );\n },\n\n /**\n * Called when router.stop() is invoked.\n * Cleans up event listeners.\n */\n onStop: () => {\n if (removePopStateListener) {\n removePopStateListener();\n removePopStateListener = undefined;\n }\n },\n\n /**\n * Called after successful navigation.\n * Updates browser history with new state.\n */\n onTransitionSuccess: (toState, fromState, navOptions) => {\n router.lastKnownState = toState;\n\n // Determine if we should replace or push history entry\n const replaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n router.areStatesEqual(toState, fromState, false));\n\n // Build URL with base and hash prefix\n const url = router.buildUrl(toState.name, toState.params);\n\n // Preserve hash fragment if configured\n // Note: preserveHash is deleted in hash mode, so it's always undefined there\n const shouldPreserveHash =\n options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash ? url + browser.getHash() : url;\n\n // Update browser history\n updateBrowserState(toState, finalUrl, replaceHistory, browser, options);\n },\n\n /**\n * Called when plugin is unsubscribed.\n * Restores original router state for clean teardown.\n */\n teardown: () => {\n // Remove event listeners\n if (removePopStateListener) {\n removePopStateListener();\n removePopStateListener = undefined;\n }\n\n // Restore original router methods\n router.start = routerStart;\n\n // Clean up added properties\n delete (router as Partial<Router>).buildUrl;\n delete (router as Partial<Router>).matchUrl;\n delete (router as Partial<Router>).replaceHistoryState;\n delete (router as Partial<Router>).lastKnownState;\n },\n };\n };\n}\n"]}
@@ -1 +1 @@
1
- {"inputs":{"../type-guards/dist/esm/index.mjs":{"bytes":3561,"imports":[],"format":"esm"},"src/constants.ts":{"bytes":1668,"imports":[],"format":"esm"},"src/utils.ts":{"bytes":7562,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"}],"format":"esm"},"src/browser.ts":{"bytes":6860,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/utils.ts","kind":"import-statement","original":"./utils"}],"format":"esm"},"src/plugin.ts":{"bytes":15342,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/browser.ts","kind":"import-statement","original":"./browser"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/utils.ts","kind":"import-statement","original":"./utils"}],"format":"esm"},"src/index.ts":{"bytes":1442,"imports":[{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"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":42971},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["browserPluginFactory","isHistoryState","isState"],"entryPoint":"src/index.ts","inputs":{"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2444},"src/browser.ts":{"bytesInOutput":4028},"src/constants.ts":{"bytesInOutput":205},"src/utils.ts":{"bytesInOutput":3335},"src/plugin.ts":{"bytesInOutput":8146},"src/index.ts":{"bytesInOutput":0}},"bytes":18576}}}
1
+ {"inputs":{"../type-guards/dist/esm/index.mjs":{"bytes":3561,"imports":[],"format":"esm"},"src/constants.ts":{"bytes":1668,"imports":[],"format":"esm"},"src/utils.ts":{"bytes":7620,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"}],"format":"esm"},"src/browser.ts":{"bytes":6860,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/utils.ts","kind":"import-statement","original":"./utils"}],"format":"esm"},"src/plugin.ts":{"bytes":15544,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"src/browser.ts","kind":"import-statement","original":"./browser"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/utils.ts","kind":"import-statement","original":"./utils"}],"format":"esm"},"src/index.ts":{"bytes":1442,"imports":[{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"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":43312},"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","isHistoryState","isState"],"entryPoint":"src/index.ts","inputs":{"src/plugin.ts":{"bytesInOutput":8257},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2444},"src/browser.ts":{"bytesInOutput":4028},"src/constants.ts":{"bytesInOutput":205},"src/utils.ts":{"bytesInOutput":3328},"src/index.ts":{"bytesInOutput":0}},"bytes":18698}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/browser-plugin",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
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,15 +45,13 @@
45
45
  },
46
46
  "sideEffects": false,
47
47
  "dependencies": {
48
- "@real-router/core": "^0.25.0",
48
+ "@real-router/core": "^0.26.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
- "@real-router/logger-plugin": "^0.2.28",
55
- "@real-router/persistent-params-plugin": "^0.1.28",
56
- "type-guards": "^0.2.2"
54
+ "type-guards": "^0.2.3"
57
55
  },
58
56
  "scripts": {
59
57
  "test": "vitest",
package/src/plugin.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // packages/browser-plugin/modules/plugin.ts
2
2
 
3
+ import { getPluginApi } from "@real-router/core";
3
4
  import { isStateStrict as isState } from "type-guards";
4
5
 
5
6
  import { createSafeBrowser } from "./browser";
@@ -115,7 +116,11 @@ export function browserPluginFactory(
115
116
 
116
117
  let removePopStateListener: (() => void) | undefined;
117
118
 
118
- return function browserPlugin(router: Router) {
119
+ return function browserPlugin(routerBase) {
120
+ // Cast to augmented Router (class + module augmentation: buildUrl, matchUrl, etc.)
121
+ const router = routerBase as Router;
122
+ const api = getPluginApi(routerBase);
123
+
119
124
  // Store original methods for restoration on teardown
120
125
 
121
126
  const routerStart = router.start;
@@ -193,7 +198,7 @@ export function browserPluginFactory(
193
198
  * Overrides router.start to integrate with browser location.
194
199
  * When no path is provided, resolves current browser URL automatically.
195
200
  */
196
- router.start = async (path?: string) => {
201
+ router.start = (path?: string) => {
197
202
  return routerStart(path ?? browser.getLocation(options));
198
203
  };
199
204
 
@@ -229,7 +234,7 @@ export function browserPluginFactory(
229
234
  router.matchUrl = (url) => {
230
235
  const path = urlToPath(url);
231
236
 
232
- return path ? router.matchPath(path) : undefined;
237
+ return path ? api.matchPath(path) : undefined;
233
238
  };
234
239
 
235
240
  /**
@@ -237,7 +242,7 @@ export function browserPluginFactory(
237
242
  * Useful for updating URL without causing a full transition.
238
243
  */
239
244
  router.replaceHistoryState = (name, params = {}) => {
240
- const state = router.buildState(name, params);
245
+ const state = api.buildState(name, params);
241
246
 
242
247
  if (!state) {
243
248
  throw new Error(
@@ -245,7 +250,7 @@ export function browserPluginFactory(
245
250
  );
246
251
  }
247
252
 
248
- const builtState = router.makeState(
253
+ const builtState = api.makeState(
249
254
  state.name,
250
255
  state.params,
251
256
  router.buildPath(state.name, state.params),
@@ -313,11 +318,11 @@ export function browserPluginFactory(
313
318
  // Top-level error recovery
314
319
  try {
315
320
  const routerState = router.getState();
316
- const state = createStateFromEvent(evt, router, browser, options);
321
+ const state = createStateFromEvent(evt, api, browser, options);
317
322
  const isNewState = !isState(evt.state);
318
323
 
319
324
  // Handle missing state
320
- if (!state && handleMissingState(router, transitionOptions)) {
325
+ if (!state && handleMissingState(router, api, transitionOptions)) {
321
326
  return;
322
327
  }
323
328
 
@@ -340,7 +345,7 @@ export function browserPluginFactory(
340
345
 
341
346
  try {
342
347
  // transitionOptions includes replace: true, which is passed to TRANSITION_SUCCESS
343
- const toState = await router.navigateToState(
348
+ const toState = await api.navigateToState(
344
349
  state,
345
350
  routerState,
346
351
  transitionOptions,
package/src/utils.ts CHANGED
@@ -7,6 +7,7 @@ import { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from "./constants";
7
7
 
8
8
  import type { BrowserPluginOptions, HistoryState, Browser } from "./types";
9
9
  import type {
10
+ PluginApi,
10
11
  Router,
11
12
  NavigationOptions,
12
13
  RouterError,
@@ -48,24 +49,24 @@ export const escapeRegExp = (str: string): string => {
48
49
  * Creates state from popstate event
49
50
  *
50
51
  * @param evt - PopStateEvent from browser
51
- * @param router - Router instance
52
+ * @param api - PluginApi instance
52
53
  * @param browser - Browser API instance
53
54
  * @param options - Browser plugin options
54
55
  * @returns Router state or undefined
55
56
  */
56
57
  export function createStateFromEvent(
57
58
  evt: PopStateEvent,
58
- router: Router,
59
+ api: PluginApi,
59
60
  browser: Browser,
60
61
  options: BrowserPluginOptions,
61
62
  ): State | undefined {
62
63
  const isNewState = !isState(evt.state);
63
64
 
64
65
  if (isNewState) {
65
- return router.matchPath(browser.getLocation(options));
66
+ return api.matchPath(browser.getLocation(options));
66
67
  }
67
68
 
68
- return router.makeState(
69
+ return api.makeState(
69
70
  evt.state.name,
70
71
  evt.state.params,
71
72
  evt.state.path,
@@ -104,14 +105,16 @@ export function shouldSkipTransition(
104
105
  * Handles missing state by navigating to default route
105
106
  *
106
107
  * @param router - Router instance
108
+ * @param api - Plugin API instance
107
109
  * @param transitionOptions - Options for transition
108
110
  * @returns true if handled, false if no default route
109
111
  */
110
112
  export function handleMissingState(
111
113
  router: Router,
114
+ api: PluginApi,
112
115
  transitionOptions: NavigationOptions,
113
116
  ): boolean {
114
- const routerOptions = router.getOptions();
117
+ const routerOptions = api.getOptions();
115
118
  const { defaultRoute } = routerOptions;
116
119
 
117
120
  if (!defaultRoute) {