@real-router/browser-plugin 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,7 +35,6 @@ router.usePlugin(browserPluginFactory());
35
35
  // With options
36
36
  router.usePlugin(
37
37
  browserPluginFactory({
38
- useHash: false,
39
38
  base: "/app",
40
39
  }),
41
40
  );
@@ -50,24 +49,21 @@ await router.start();
50
49
  ```typescript
51
50
  router.usePlugin(
52
51
  browserPluginFactory({
53
- useHash: true, // Required for hashPrefix
54
- hashPrefix: "!",
52
+ base: "/app",
53
+ forceDeactivate: true,
55
54
  }),
56
55
  );
57
56
 
58
57
  router.navigate("products", { id: "123" });
59
- // URL: http://example.com/#!/products/123
58
+ // URL: http://example.com/app/products/123
60
59
  ```
61
60
 
62
- | Option | Type | Default | Description |
63
- | ----------------- | --------- | ------- | -------------------------------------------------------------------- |
64
- | `useHash` | `boolean` | `false` | Use hash routing (`#/path`) instead of History API |
65
- | `hashPrefix` | `string` | `""` | Hash prefix (e.g., `"!"` `#!/path`). Only with `useHash: true` |
66
- | `preserveHash` | `boolean` | `true` | Keep URL hash fragment during navigation. Only with `useHash: false` |
67
- | `base` | `string` | `""` | Base path for all routes (e.g., `"/app"`) |
68
- | `forceDeactivate` | `boolean` | `true` | Bypass `canDeactivate` guards on browser back/forward |
61
+ | Option | Type | Default | Description |
62
+ | ----------------- | --------- | ------- | ----------------------------------------------------- |
63
+ | `base` | `string` | `""` | Base path for all routes (e.g., `"/app"`) |
64
+ | `forceDeactivate` | `boolean` | `true` | Bypass `canDeactivate` guards on browser back/forward |
69
65
 
70
- **Type Safety:** Options use discriminated union — `hashPrefix` and `preserveHash` are mutually exclusive at compile time.
66
+ > **Looking for hash routing?** Use [`@real-router/hash-plugin`](https://www.npmjs.com/package/@real-router/hash-plugin) instead.
71
67
 
72
68
  See [Wiki](https://github.com/greydragon888/real-router/wiki/browser-plugin#3-configuration-options) for detailed option descriptions and examples.
73
69
 
@@ -75,11 +71,11 @@ See [Wiki](https://github.com/greydragon888/real-router/wiki/browser-plugin#3-co
75
71
 
76
72
  ## Added Router Methods
77
73
 
78
- The plugin extends the router with browser-specific methods:
74
+ The plugin extends the router instance with browser-specific methods (via [`extendRouter()`](https://github.com/greydragon888/real-router/wiki/core#extendrouter)):
79
75
 
80
76
  #### `router.buildUrl(name: string, params?: Params): string`
81
77
 
82
- Build full URL with base path and hash prefix.\
78
+ Build full URL with base path.\
83
79
  `name: string` — route name\
84
80
  `params?: Params` — route parameters\
85
81
  Returns: `string` — full URL\
@@ -124,13 +120,12 @@ router.replaceHistoryState("users", { id: "456" });
124
120
 
125
121
  ## Usage Examples
126
122
 
127
- ### History Mode (default)
123
+ ### With Base Path
128
124
 
129
125
  ```typescript
130
126
  router.usePlugin(
131
127
  browserPluginFactory({
132
128
  base: "/app",
133
- preserveHash: true,
134
129
  }),
135
130
  );
136
131
 
@@ -138,20 +133,6 @@ router.navigate("users", { id: "123" });
138
133
  // URL: /app/users/123
139
134
  ```
140
135
 
141
- ### Hash Mode
142
-
143
- ```typescript
144
- router.usePlugin(
145
- browserPluginFactory({
146
- useHash: true,
147
- hashPrefix: "!",
148
- }),
149
- );
150
-
151
- router.navigate("users", { id: "123" });
152
- // URL: #!/users/123
153
- ```
154
-
155
136
  ### Form Protection
156
137
 
157
138
  ```typescript
@@ -199,6 +180,7 @@ Full documentation available on the [Wiki](https://github.com/greydragon888/real
199
180
  ## Related Packages
200
181
 
201
182
  - [@real-router/core](https://www.npmjs.com/package/@real-router/core) — Core router
183
+ - [@real-router/hash-plugin](https://www.npmjs.com/package/@real-router/hash-plugin) — Hash-based routing (`#/path`)
202
184
  - [@real-router/react](https://www.npmjs.com/package/@real-router/react) — React integration
203
185
  - [@real-router/logger-plugin](https://www.npmjs.com/package/@real-router/logger-plugin) — Debug logging
204
186
 
@@ -1,9 +1,9 @@
1
1
  import { State as State$1, PluginFactory, Params as Params$1 } from '@real-router/core';
2
2
 
3
3
  /**
4
- * Common options shared between hash and history modes
4
+ * Browser plugin configuration.
5
5
  */
6
- interface BaseBrowserPluginOptions {
6
+ interface BrowserPluginOptions {
7
7
  /**
8
8
  * Force deactivation of current route even if canDeactivate returns false.
9
9
  *
@@ -17,163 +17,17 @@ interface BaseBrowserPluginOptions {
17
17
  */
18
18
  base?: string;
19
19
  }
20
- /**
21
- * Hash-based routing configuration.
22
- * Uses URL hash for navigation (e.g., example.com/#/path).
23
- *
24
- * @example
25
- * ```ts
26
- * // Standard hash routing
27
- * browserPluginFactory({ useHash: true })
28
- * // → example.com/#/users
29
- *
30
- * // Hash routing with prefix
31
- * browserPluginFactory({ useHash: true, hashPrefix: "!" })
32
- * // → example.com/#!/users
33
- * ```
34
- */
35
- interface HashModeOptions extends BaseBrowserPluginOptions {
36
- /**
37
- * Enable hash-based routing
38
- */
39
- useHash: true;
40
- /**
41
- * Prefix for hash (e.g., "!" for "#!/path").
42
- * Only valid when useHash is true.
43
- *
44
- * @default ""
45
- */
46
- hashPrefix?: string;
47
- /**
48
- * Not available in hash mode.
49
- * Hash preservation only works with HTML5 History API.
50
- * Use `useHash: false` to enable this option.
51
- */
52
- preserveHash?: never;
53
- }
54
- /**
55
- * HTML5 History API routing configuration.
56
- * Uses pushState/replaceState for navigation (e.g., example.com/path).
57
- *
58
- * @example
59
- * ```ts
60
- * // Standard history routing
61
- * browserPluginFactory({ useHash: false })
62
- * // → example.com/users
63
- *
64
- * // Preserve URL hash fragments
65
- * browserPluginFactory({ useHash: false, preserveHash: true })
66
- * // → example.com/users#section
67
- * ```
68
- */
69
- interface HistoryModeOptions extends BaseBrowserPluginOptions {
70
- /**
71
- * Disable hash-based routing (use HTML5 History API)
72
- *
73
- * @default false
74
- */
75
- useHash?: false;
76
- /**
77
- * Preserve URL hash fragment on initial navigation.
78
- * Only valid when useHash is false.
79
- *
80
- * @default true
81
- */
82
- preserveHash?: boolean;
83
- /**
84
- * Not available in history mode.
85
- * Hash prefix only works with hash-based routing.
86
- * Use `useHash: true` to enable this option.
87
- */
88
- hashPrefix?: never;
89
- }
90
- /**
91
- * Type-safe browser plugin configuration.
92
- *
93
- * Uses discriminated union to prevent conflicting options:
94
- * - Hash mode (useHash: true): allows hashPrefix, forbids preserveHash
95
- * - History mode (useHash: false): allows preserveHash, forbids hashPrefix
96
- *
97
- * @example
98
- * ```ts
99
- * // ✅ Valid: Hash mode with prefix
100
- * const config1: BrowserPluginOptions = {
101
- * useHash: true,
102
- * hashPrefix: "!"
103
- * };
104
- *
105
- * // ✅ Valid: History mode with hash preservation
106
- * const config2: BrowserPluginOptions = {
107
- * useHash: false,
108
- * preserveHash: true
109
- * };
110
- *
111
- * // ❌ Error: Cannot use preserveHash with hash mode
112
- * const config3: BrowserPluginOptions = {
113
- * useHash: true,
114
- * preserveHash: true // Type error!
115
- * };
116
- *
117
- * // ❌ Error: Cannot use hashPrefix with history mode
118
- * const config4: BrowserPluginOptions = {
119
- * useHash: false,
120
- * hashPrefix: "!" // Type error!
121
- * };
122
- * ```
123
- */
124
- type BrowserPluginOptions = HashModeOptions | HistoryModeOptions;
125
- /**
126
- * Browser API abstraction for cross-environment compatibility.
127
- * Provides same interface in browser and SSR contexts.
128
- */
129
- interface Browser {
130
- /**
131
- * Pushes new state to browser history
132
- *
133
- * @param state - History state object
134
- * @param path - URL path
135
- */
20
+
21
+ interface HistoryBrowser {
136
22
  pushState: (state: State$1, path: string) => void;
137
- /**
138
- * Replaces current history state
139
- *
140
- * @param state - History state object
141
- * @param path - URL path
142
- */
143
23
  replaceState: (state: State$1, path: string) => void;
144
24
  addPopstateListener: (fn: (evt: PopStateEvent) => void) => () => void;
145
- /**
146
- * Gets current location path respecting plugin options
147
- *
148
- * @param opts - Plugin options
149
- * @returns Current path string
150
- */
151
- getLocation: (opts: BrowserPluginOptions) => string;
152
- /**
153
- * Gets current URL hash
154
- *
155
- * @returns Hash string (including #)
156
- */
157
25
  getHash: () => string;
158
26
  }
27
+ interface Browser extends HistoryBrowser {
28
+ getLocation: () => string;
29
+ }
159
30
 
160
- /**
161
- * Browser plugin factory for real-router.
162
- * Integrates router with browser history API.
163
- *
164
- * @param opts - Plugin configuration options
165
- * @param browser - Browser API abstraction (for testing/SSR)
166
- * @returns Plugin factory function
167
- *
168
- * @example
169
- * ```ts
170
- * // Hash routing
171
- * router.usePlugin(browserPluginFactory({ useHash: true, hashPrefix: "!" }));
172
- *
173
- * // History routing with hash preservation
174
- * router.usePlugin(browserPluginFactory({ useHash: false, preserveHash: true }));
175
- * ```
176
- */
177
31
  declare function browserPluginFactory(opts?: Partial<BrowserPluginOptions>, browser?: Browser): PluginFactory;
178
32
 
179
33
  type TransitionPhase = "deactivating" | "activating";
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var e=require("@real-router/core"),t=require("@real-router/logger"),r={forceDeactivate:!0,useHash:!1,hashPrefix:"",base:"",preserveHash:!0},n="popstate",o="browser-plugin",s=new Map,i=e=>{const t=s.get(e);if(void 0!==t)return t;const r=e.replaceAll(/[$()*+.?[\\\]^{|}-]/g,String.raw`\$&`);return s.set(e,r),r};function a(e,t,r,n){if(r.useHash){const e=i(r.hashPrefix);return(e?t.replace(n.get(`^#${e}`),""):t.slice(1))||"/"}if(r.base){const t=i(r.base),o=e.replace(n.get(`^${t}`),"");return o.startsWith("/")?o:`/${o}`}return e}function c(){const e=new Map;return{get(t){const r=e.get(t);if(void 0!==r)return r;const n=new RegExp(t);return e.set(t,n),n}}}var h=()=>{},u=(e,t)=>{globalThis.history.pushState(e,"",t)},p=(e,t)=>{globalThis.history.replaceState(e,"",t)},l=e=>(globalThis.addEventListener("popstate",e),()=>{globalThis.removeEventListener("popstate",e)}),f=c(),d=e=>(e=>{try{return encodeURI(decodeURI(e))}catch(r){return t.logger.warn(o,`Could not encode path "${e}"`,r),e}})(a(globalThis.location.pathname,globalThis.location.hash,e,f))+globalThis.location.search,g=()=>globalThis.location.hash;var m=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function b(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=>b(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=>b(e,t))}return!1}function v(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}function y(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;const t=Object.getPrototypeOf(e);if(null!==t&&t!==Object.prototype)return!1;let r=!1;for(const t in e){if(!Object.hasOwn(e,t))continue;const n=e[t];if(!v(n)){const e=typeof n;if("function"===e||"symbol"===e)return!1;r=!0;break}}return!r||b(e)}function w(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||("number"===t?Number.isFinite(e):!!Array.isArray(e)&&e.every(e=>{const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}))}function S(e){if("object"!=typeof e||null===e)return!1;const t=e;return!!function(e){return function(e){return"string"==typeof e&&(""===e||!(e.length>1e4)&&(!!e.startsWith("@@")||m.test(e)))}(e.name)&&"string"==typeof e.path&&y(e.params)}(t)&&(void 0===t.meta||function(e){if("object"!=typeof e||null===e)return!1;const t=e;return!("params"in t&&!function(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;for(const t in e)if(Object.hasOwn(e,t)&&!w(e[t]))return!1;return!0}(t.params)||"id"in t&&"number"!=typeof t.id)}(t.meta))}function P(e,t,r,n){const o={meta:e.meta,name:e.name,params:e.params,path:e.path};r?n.replaceState(o,t):n.pushState(o,t)}var $=class{#e;#t;#r;#n;#o;#s;#i;#a;#c=!1;#h=null;#u;constructor(e,t,r,n,o,s,i){this.#e=e,this.#t=t,this.#r=r,this.#n=n,this.#o=o,this.#i=s,this.#a=i,this.#s=r.useHash?`#${r.hashPrefix}`:"",this.#u=this.#t.addInterceptor("start",(e,t)=>e(t??this.#n.getLocation(this.#r))),this.#p()}getPlugin(){return{onStart:()=>{this.#a.removePopStateListener&&this.#a.removePopStateListener(),this.#a.removePopStateListener=this.#n.addPopstateListener(e=>{this.#l(e)})},onStop:()=>{this.#a.removePopStateListener&&(this.#a.removePopStateListener(),this.#a.removePopStateListener=void 0)},onTransitionSuccess:(e,t,r)=>{const n=(r.replace??!t)||!!r.reload&&this.#e.areStatesEqual(e,t,!1),o=this.#e.buildUrl(e.name,e.params);P(e,!this.#r.preserveHash||t&&t.path!==e.path?o:o+this.#n.getHash(),n,this.#n)},teardown:()=>{this.#f()}}}#p(){const e=this.#e;e.buildUrl=(t,r)=>{return n=e.buildPath(t,r),o=this.#r.base,s=this.#s,o+s+n;var n,o,s},e.matchUrl=e=>{const t=function(e,t,r){try{const n=new URL(e,globalThis.location.origin);return["http:","https:"].includes(n.protocol)?a(n.pathname,n.hash,t,r)+n.search:(console.warn(`[${o}] Invalid URL protocol in ${e}`),null)}catch(t){return console.warn(`[${o}] Could not parse url ${e}`,t),null}}(e,this.#r,this.#o);return t?this.#t.matchPath(t):void 0},e.replaceHistoryState=(t,r={})=>{const n=this.#t.buildState(t,r);if(!n)throw new Error(`[real-router] Cannot replace state: route "${t}" is not found`);P(this.#t.makeState(n.name,n.params,e.buildPath(n.name,n.params),{params:n.meta},1),e.buildUrl(t,r),!0,this.#n)}}#f(){this.#a.removePopStateListener&&(this.#a.removePopStateListener(),this.#a.removePopStateListener=void 0),this.#u(),delete this.#e.buildUrl,delete this.#e.matchUrl,delete this.#e.replaceHistoryState}#d(){if(this.#h){const e=this.#h;this.#h=null,console.warn(`[${o}] Processing deferred popstate event`),this.#l(e)}}async#l(t){if(this.#c)return console.warn(`[${o}] Transition in progress, deferring popstate event`),void(this.#h=t);this.#c=!0;try{const e=function(e,t,r,n){if(S(e.state))return{name:e.state.name,params:e.state.params};const o=t.matchPath(r.getLocation(n));return o?{name:o.name,params:o.params}:void 0}(t,this.#t,this.#n,this.#r);e?await this.#e.navigate(e.name,e.params,this.#i):await this.#e.navigateToDefault({...this.#i,reload:!0,replace:!0})}catch(t){t instanceof e.RouterError||this.#g(t)}finally{this.#c=!1,this.#d()}}#g(e){console.error(`[${o}] Critical error in onPopState`,e);try{const e=this.#e.getState();if(e){const t=this.#e.buildUrl(e.name,e.params);this.#n.replaceState(e,t)}}catch(e){console.error(`[${o}] Failed to recover from critical error`,e)}}};function L(e,t){return e in t}function x(e,t,r){const n=typeof t;return n===r||void 0===t||(console.warn(`[${o}] Invalid type for '${e}': expected ${r}, got ${n}`),!1)}exports.browserPluginFactory=function(s,i=function(){return void 0!==globalThis.window&&globalThis.history?{pushState:u,replaceState:p,addPopstateListener:l,getLocation:d,getHash:g}:function(){let e=!1;const r=r=>{e||(t.logger.warn(o,`Browser plugin is running in a non-browser environment. Method "${r}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),e=!0)};return{pushState:()=>{r("pushState")},replaceState:()=>{r("replaceState")},addPopstateListener:()=>(r("addPopstateListener"),h),getLocation:()=>(r("getLocation"),""),getHash:()=>(r("getHash"),"")}}()}()){const a=function(e,t){if(!e)return!1;let r=!1;for(const n of Object.keys(e))L(n,t)&&(x(n,e[n],typeof t[n])||(r=!0));if(!0===e.useHash&&"preserveHash"in e&&console.warn(`[${o}] preserveHash ignored in hash mode`),!1===e.useHash&&"hashPrefix"in e){const t=e.hashPrefix;void 0!==t&&""!==t&&console.warn(`[${o}] hashPrefix ignored in history mode`)}return r}(s,r);let f={...r,...s};a&&(console.warn(`[${o}] Using default options due to invalid types`),f={...r}),!0===f.useHash?delete f.preserveHash:delete f.hashPrefix,f.base&&(f.base.startsWith("/")||(f.base=`/${f.base}`),f.base.endsWith("/")&&(f.base=f.base.slice(0,-1)));const m=c(),b=f.forceDeactivate,v=void 0===b?{source:n,replace:!0}:{forceDeactivate:b,source:n,replace:!0},y={removePopStateListener:void 0};return function(t){return new $(t,e.getPluginApi(t),f,i,m,v,y).getPlugin()}},exports.isState=S;//# sourceMappingURL=index.js.map
1
+ var e=require("@real-router/core"),t=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function r(e,t=new WeakSet){if(null==e)return!0;const n=typeof e;if("string"===n||"boolean"===n)return!0;if("number"===n)return Number.isFinite(e);if("function"===n||"symbol"===n)return!1;if(Array.isArray(e))return!t.has(e)&&(t.add(e),e.every(e=>r(e,t)));if("object"===n){if(t.has(e))return!1;t.add(e);const n=Object.getPrototypeOf(e);return(null===n||n===Object.prototype)&&Object.values(e).every(e=>r(e,t))}return!1}function n(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 o=!1;for(const t in e){if(!Object.hasOwn(e,t))continue;const r=e[t];if(!n(r)){const e=typeof r;if("function"===e||"symbol"===e)return!1;o=!0;break}}return!o||r(e)}function a(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 r=e;return!!function(e){return function(e){return"string"==typeof e&&(""===e||!(e.length>1e4)&&(!!e.startsWith("@@")||t.test(e)))}(e.name)&&"string"==typeof e.path&&o(e.params)}(r)&&(void 0===r.meta||function(e){if("object"!=typeof e||null===e)return!1;const t=e;return!("params"in t&&!function(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;for(const t in e)if(Object.hasOwn(e,t)&&!a(e[t]))return!1;return!0}(t.params)||"id"in t&&"number"!=typeof t.id)}(r.meta))}var s=(e,t)=>{globalThis.history.pushState(e,"",t)},c=(e,t)=>{globalThis.history.replaceState(e,"",t)},u=e=>(globalThis.addEventListener("popstate",e),()=>{globalThis.removeEventListener("popstate",e)}),l=()=>globalThis.location.hash,p=()=>{},f=e=>{let t=!1;return r=>{t||(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: "${e}"). Method "${r}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),t=!0)}},h=e=>{const t=f(e);return{pushState:()=>{t("pushState")},replaceState:()=>{t("replaceState")},addPopstateListener:()=>(t("addPopstateListener"),p),getHash:()=>(t("getHash"),"")}};function b(e,t,r,n){const o={meta:e.meta,name:e.name,params:e.params,path:e.path};r?n.replaceState(o,t):n.pushState(o,t)}function d(t){let r=!1,n=null;async function o(a){if(r)return console.warn(`[${t.loggerContext}] Transition in progress, deferring popstate event`),void(n=a);r=!0;try{const e=function(e,t,r){if(i(e.state))return{name:e.state.name,params:e.state.params};const n=t.matchPath(r.getLocation());return n?{name:n.name,params:n.params}:void 0}(a,t.api,t.browser);e?await t.router.navigate(e.name,e.params,t.transitionOptions):await t.router.navigateToDefault({...t.transitionOptions,reload:!0,replace:!0})}catch(r){r instanceof e.RouterError||function(e){console.error(`[${t.loggerContext}] Critical error in onPopState`,e);try{const e=t.router.getState();if(e){const r=t.buildUrl(e.name,e.params);t.browser.replaceState(e,r)}}catch(e){console.error(`[${t.loggerContext}] Failed to recover from critical error`,e)}}(r)}finally{r=!1,function(){if(n){const e=n;n=null,console.warn(`[${t.loggerContext}] Processing deferred popstate event`),o(e)}}()}}return e=>{o(e)}}function m(e,t,r,n){return(o,a={})=>{const i=e.buildState(o,a);if(!i)throw new Error(`[real-router] Cannot replace state: route "${o}" is not found`);b(e.makeState(i.name,i.params,t.buildPath(i.name,i.params),{params:i.meta},1),n(o,a),!0,r)}}var g={forceDeactivate:!0,base:""},v="browser-plugin";function y(e,t){if(t&&e.startsWith(t)){const r=e.slice(t.length);return r.startsWith("/")?r:`/${r}`}return e}var w,S,P=class{#e;#t;#r;#n;#o;constructor(e,t,r,n,o,a){var i;this.#e=e,this.#t=n,this.#r=(i=n,t.addInterceptor("start",(e,t)=>e(t??i.getLocation())));const s=(t,n)=>{return o=e.buildPath(t,n),r.base+o;var o};this.#n=t.extendRouter({buildUrl:s,matchUrl:e=>{const n=function(e,t){const r=function(e,t){try{const r=new URL(e,globalThis.location.origin);return["http:","https:"].includes(r.protocol)?r:(console.warn(`[${t}] Invalid URL protocol in ${e}`),null)}catch(r){return console.warn(`[${t}] Could not parse url ${e}`,r),null}}(e,v);return r?y(r.pathname,t)+r.search:null}(e,r.base);return n?t.matchPath(n):void 0},replaceHistoryState:m(t,e,n,s)});const c=d({router:e,api:t,browser:n,transitionOptions:o,loggerContext:"browser-plugin",buildUrl:(t,r)=>e.buildUrl(t,r)});this.#o=function(e){return{onStart:()=>{e.shared.removePopStateListener&&e.shared.removePopStateListener(),e.shared.removePopStateListener=e.browser.addPopstateListener(e.handler)},onStop:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0)},teardown:()=>{e.shared.removePopStateListener&&(e.shared.removePopStateListener(),e.shared.removePopStateListener=void 0),e.cleanup()}}}({browser:n,shared:a,handler:c,cleanup:()=>{this.#r(),this.#n()}})}getPlugin(){return{...this.#o,onTransitionSuccess:(e,t,r)=>{const n=(a=t,i=this.#e,((o=r).replace??!a)||!!o.reload&&i.areStatesEqual(e,a,!1));var o,a,i;const s=this.#e.buildUrl(e.name,e.params);b(e,t&&t.path!==e.path?s:s+this.#t.getHash(),n,this.#t)}}}},L=(w=g,S=v,e=>{if(e)for(const t of Object.keys(e))if(t in w){const r=e[t],n=typeof w[t],o=typeof r;if(void 0!==r&&o!==n)throw new Error(`[${S}] Invalid type for '${t}': expected ${n}, got ${o}`)}});exports.browserPluginFactory=function(t,r){L(t);const n={...g,...t};n.base=function(e){if(!e)return e;let t=e;return t.startsWith("/")||(t=`/${t}`),t.endsWith("/")&&(t=t.slice(0,-1)),t}(n.base);const o=r??function(e,t){if(void 0!==globalThis.window&&globalThis.history)return{pushState:s,replaceState:c,addPopstateListener:u,getLocation:e,getHash:l};const r=f(t);return{...h(t),getLocation:()=>(r("getLocation"),"")}}(()=>(e=>{try{return encodeURI(decodeURI(e))}catch(t){return console.warn(`[browser-env] Could not encode path "${e}"`,t),e}})(y(globalThis.location.pathname,n.base))+globalThis.location.search,"browser-plugin"),a={forceDeactivate:n.forceDeactivate,source:"popstate",replace:!0},i={removePopStateListener:void 0};return function(t){return new P(t,e.getPluginApi(t),n,o,a,i).getPlugin()}},exports.isState=i;//# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/browser.ts","../../src/popstate-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["regExpCache","logger","RouterError","defaultOptions","getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/browser-plugin/modules/constants.ts\n\n/**\n * Internal type for default options.\n *\n * Why separate type instead of BrowserPluginOptions?\n *\n * BrowserPluginOptions is a discriminated union:\n * - HashModeOptions: allows hashPrefix, forbids preserveHash (never)\n * - HistoryModeOptions: allows preserveHash, forbids hashPrefix (never)\n *\n * We cannot create a single object of type BrowserPluginOptions that contains\n * BOTH hashPrefix and preserveHash - one will always be 'never' depending on useHash.\n *\n * Example - this would fail TypeScript:\n * const defaults: BrowserPluginOptions = {\n * useHash: false, // → HistoryModeOptions branch\n * preserveHash: true, // ✅ OK\n * hashPrefix: \"\" // ❌ Error: Type 'string' is not assignable to type 'never'\n * };\n *\n * DefaultBrowserPluginOptions solves this by containing ALL options,\n * enabling:\n * - Default values for every option\n * - Type validation via typeof defaultOptions\n * - Runtime validation of user-provided option types\n */\nexport interface DefaultBrowserPluginOptions {\n forceDeactivate: boolean;\n useHash: boolean;\n base: string;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n preserveHash: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n * Used to distinguish browser-initiated navigation (back/forward buttons)\n * from programmatic navigation (router.navigate()).\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"browser-plugin\";\n","// packages/browser-plugin/src/url-utils.ts\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nimport type { URLParseOptions, RegExpCache } from \"./types\";\n\nconst escapeRegExpCache = new Map<string, string>();\n\nexport const escapeRegExp = (str: string): string => {\n const cached = escapeRegExpCache.get(str);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const escaped = str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n\n escapeRegExpCache.set(str, escaped);\n\n return escaped;\n};\n\nexport function extractPath(\n pathname: string,\n hash: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string {\n if (options.useHash) {\n const escapedHashPrefix = escapeRegExp(options.hashPrefix);\n const path = escapedHashPrefix\n ? hash.replace(regExpCache.get(`^#${escapedHashPrefix}`), \"\")\n : hash.slice(1);\n\n return path || \"/\";\n }\n\n if (options.base) {\n const escapedBase = escapeRegExp(options.base);\n const stripped = pathname.replace(regExpCache.get(`^${escapedBase}`), \"\");\n\n return stripped.startsWith(\"/\") ? stripped : `/${stripped}`;\n }\n\n return pathname;\n}\n\nexport function urlToPath(\n url: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${LOGGER_CONTEXT}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return (\n extractPath(parsedUrl.pathname, parsedUrl.hash, options, regExpCache) +\n parsedUrl.search\n );\n } catch (error) {\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n\nexport function buildUrl(path: string, base: string, prefix: string): string {\n return base + prefix + path;\n}\n\nexport function createRegExpCache(): RegExpCache {\n const cache = new Map<string, RegExp>();\n\n return {\n get(pattern: string): RegExp {\n const cached = cache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n cache.set(pattern, newRegExp);\n\n return newRegExp;\n },\n };\n}\n","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { createRegExpCache, extractPath } from \"./url-utils\";\n\nimport type { Browser, BrowserPluginOptions, URLParseOptions } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\nconst pushState = (state: State, path: string) => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nconst replaceState = (state: State, path: string) => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nconst regExpCache = createRegExpCache();\n\n/**\n * Safely encodes/decodes path to normalize URL encoding\n *\n * @param path - Path to normalize\n * @returns Normalized path or original on error\n */\nconst safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n logger.warn(LOGGER_CONTEXT, `Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n\nconst getLocation = (opts: BrowserPluginOptions) => {\n const rawPath = extractPath(\n globalThis.location.pathname,\n globalThis.location.hash,\n opts as URLParseOptions,\n regExpCache,\n );\n\n return safelyEncodePath(rawPath) + globalThis.location.search;\n};\n\n/**\n * Gets current URL hash\n */\nconst getHash = () => globalThis.location.hash;\n\n/**\n * Creates a fallback browser for non-browser environments (SSR).\n * Logs warning on first method call to help diagnose misconfiguration.\n *\n * @returns Browser API with no-op implementations\n */\nfunction createFallbackBrowser(): Browser {\n let hasWarned = false;\n\n const warnOnce = (method: string) => {\n if (!hasWarned) {\n logger.warn(\n LOGGER_CONTEXT,\n `Browser plugin is running in a non-browser environment. ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n}\n\n/**\n * Creates browser API abstraction that works in both browser and SSR environments\n *\n * @returns Browser API object\n */\nexport function createSafeBrowser(): Browser {\n const isBrowser =\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n\n return isBrowser\n ? {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n }\n : createFallbackBrowser();\n}\n","import { isStateStrict as isState } from \"type-guards\";\n\nimport type { BrowserPluginOptions, Browser } from \"./types\";\nimport type { PluginApi, State, Params } from \"@real-router/core\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n options: BrowserPluginOptions,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation(options));\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { getRouteFromEvent, updateBrowserState } from \"./popstate-utils\";\nimport { buildUrl, urlToPath } from \"./url-utils\";\n\nimport type {\n Browser,\n BrowserPluginOptions,\n RegExpCache,\n SharedFactoryState,\n URLParseOptions,\n} from \"./types\";\nimport type {\n NavigationOptions,\n PluginApi,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\n\nexport class BrowserPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: BrowserPluginOptions;\n readonly #browser: Browser;\n readonly #regExpCache: RegExpCache;\n readonly #prefix: string;\n readonly #transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n readonly #shared: SharedFactoryState;\n\n #isTransitioning = false;\n #deferredPopstateEvent: PopStateEvent | null = null;\n readonly #removeStartInterceptor: () => void;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: BrowserPluginOptions,\n browser: Browser,\n regExpCache: RegExpCache,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#browser = browser;\n this.#regExpCache = regExpCache;\n this.#transitionOptions = transitionOptions;\n this.#shared = shared;\n\n const normalizedOptions = options as URLParseOptions;\n\n this.#prefix = options.useHash ? `#${normalizedOptions.hashPrefix}` : \"\";\n\n this.#removeStartInterceptor = this.#api.addInterceptor(\n \"start\",\n (next, path) => next(path ?? this.#browser.getLocation(this.#options)),\n );\n\n this.#augmentRouter();\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n }\n\n this.#shared.removePopStateListener = this.#browser.addPopstateListener(\n (evt: PopStateEvent) => void this.#onPopState(evt),\n );\n },\n\n onStop: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n },\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const shouldReplaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n this.#router.areStatesEqual(toState, fromState, false));\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n const shouldPreserveHash =\n !!this.#options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash\n ? url + this.#browser.getHash()\n : url;\n\n updateBrowserState(\n toState,\n finalUrl,\n shouldReplaceHistory,\n this.#browser,\n );\n },\n\n teardown: () => {\n this.#cleanupAugmentation();\n },\n };\n }\n\n #augmentRouter(): void {\n const router = this.#router;\n\n router.buildUrl = (route, params) => {\n const path = router.buildPath(route, params);\n\n return buildUrl(\n path,\n (this.#options as URLParseOptions).base,\n this.#prefix,\n );\n };\n\n router.matchUrl = (url) => {\n const path = urlToPath(\n url,\n this.#options as URLParseOptions,\n this.#regExpCache,\n );\n\n return path ? this.#api.matchPath(path) : undefined;\n };\n\n router.replaceHistoryState = (name, params = {}) => {\n const state = this.#api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = this.#api.makeState(\n state.name,\n state.params,\n router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n 1, // forceId\n );\n const url = router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, this.#browser);\n };\n }\n\n #cleanupAugmentation(): void {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n\n this.#removeStartInterceptor();\n\n delete (this.#router as Partial<Router>).buildUrl;\n delete (this.#router as Partial<Router>).matchUrl;\n delete (this.#router as Partial<Router>).replaceHistoryState;\n }\n\n #processDeferredEvent(): void {\n if (this.#deferredPopstateEvent) {\n const event = this.#deferredPopstateEvent;\n\n this.#deferredPopstateEvent = null;\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void this.#onPopState(event);\n }\n }\n\n async #onPopState(evt: PopStateEvent): Promise<void> {\n if (this.#isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n this.#deferredPopstateEvent = evt;\n\n return;\n }\n\n this.#isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(\n evt,\n this.#api,\n this.#browser,\n this.#options,\n );\n\n // eslint-disable-next-line unicorn/prefer-ternary\n if (route) {\n await this.#router.navigate(\n route.name,\n route.params,\n this.#transitionOptions,\n );\n } else {\n await this.#router.navigateToDefault({\n ...this.#transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n this.#recoverFromCriticalError(error);\n }\n } finally {\n this.#isTransitioning = false;\n this.#processDeferredEvent();\n }\n }\n\n #recoverFromCriticalError(error: unknown): void {\n console.error(`[${LOGGER_CONTEXT}] Critical error in onPopState`, error);\n\n try {\n const currentState = this.#router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = this.#router.buildUrl(\n currentState.name,\n currentState.params,\n );\n\n this.#browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n}\n","import { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions } from \"./types\";\n\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\nfunction validateOptionType(\n key: keyof DefaultBrowserPluginOptions,\n value: unknown,\n expectedType: string,\n): boolean {\n const actualType = typeof value;\n\n if (actualType !== expectedType && value !== undefined) {\n console.warn(\n `[${LOGGER_CONTEXT}] Invalid type for '${key}': expected ${expectedType}, got ${actualType}`,\n );\n\n return false;\n }\n\n return true;\n}\n\nexport function validateOptions(\n opts: Partial<BrowserPluginOptions> | undefined,\n defaultOptions: DefaultBrowserPluginOptions,\n): boolean {\n if (!opts) {\n return false;\n }\n\n let hasInvalidTypes = false;\n\n for (const key of Object.keys(opts)) {\n if (isDefaultOptionKey(key, defaultOptions)) {\n const expectedType = typeof defaultOptions[key];\n const value = opts[key];\n const isValid = validateOptionType(key, value, expectedType);\n\n if (!isValid) {\n hasInvalidTypes = true;\n }\n }\n }\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n const optsRecord = opts as unknown as Record<string, unknown>;\n const hashPrefix = optsRecord.hashPrefix;\n\n if (hashPrefix !== undefined && hashPrefix !== \"\") {\n console.warn(`[${LOGGER_CONTEXT}] hashPrefix ignored in history mode`);\n }\n }\n\n return hasInvalidTypes;\n}\n","import { getPluginApi } from \"@real-router/core\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport { BrowserPlugin } from \"./plugin\";\nimport { createRegExpCache } from \"./url-utils\";\nimport { validateOptions } from \"./validation\";\n\nimport type {\n BrowserPluginOptions,\n Browser,\n SharedFactoryState,\n} from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * @param opts - Plugin configuration options\n * @param browser - Browser API abstraction (for testing/SSR)\n * @returns Plugin factory function\n *\n * @example\n * ```ts\n * // Hash routing\n * router.usePlugin(browserPluginFactory({ useHash: true, hashPrefix: \"!\" }));\n *\n * // History routing with hash preservation\n * router.usePlugin(browserPluginFactory({ useHash: false, preserveHash: true }));\n * ```\n */\nexport function browserPluginFactory(\n opts?: Partial<BrowserPluginOptions>,\n browser: Browser = createSafeBrowser(),\n): PluginFactory {\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n if (hasInvalidTypes) {\n console.warn(\n `[${LOGGER_CONTEXT}] Using default options due to invalid types`,\n );\n options = { ...defaultOptions } as BrowserPluginOptions;\n }\n\n if (options.useHash === true) {\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n if (options.base) {\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n const regExpCache = createRegExpCache();\n\n const forceDeactivate = options.forceDeactivate;\n /* v8 ignore next 4 -- @preserve both branches tested, coverage tool limitation */\n const transitionOptions =\n forceDeactivate === undefined\n ? { source, replace: true as const }\n : { forceDeactivate, source, replace: true as const };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function browserPlugin(routerBase) {\n const plugin = new BrowserPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n browser,\n regExpCache,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
1
+ {"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["i","f","c","getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/browser-plugin/modules/constants.ts\n\nimport type { BrowserPluginOptions } from \"./types\";\n\nexport const defaultOptions: Required<BrowserPluginOptions> = {\n forceDeactivate: true,\n base: \"\",\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n * Used to distinguish browser-initiated navigation (back/forward buttons)\n * from programmatic navigation (router.navigate()).\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"browser-plugin\";\n","// packages/browser-plugin/src/url-utils.ts\n\nimport { safeParseUrl } from \"browser-env\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nexport function extractPath(pathname: string, base: string): string {\n if (base && pathname.startsWith(base)) {\n const stripped = pathname.slice(base.length);\n\n return stripped.startsWith(\"/\") ? stripped : `/${stripped}`;\n }\n\n return pathname;\n}\n\nexport function buildUrl(path: string, base: string): string {\n return base + path;\n}\n\nexport function urlToPath(url: string, base: string): string | null {\n const parsedUrl = safeParseUrl(url, LOGGER_CONTEXT);\n\n return parsedUrl\n ? extractPath(parsedUrl.pathname, base) + parsedUrl.search\n : null;\n}\n","import {\n createPopstateHandler,\n createPopstateLifecycle,\n createStartInterceptor,\n createReplaceHistoryState,\n shouldReplaceHistory,\n updateBrowserState,\n} from \"browser-env\";\n\nimport { buildUrl, urlToPath } from \"./url-utils\";\n\nimport type { BrowserPluginOptions } from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n PluginApi,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport class BrowserPlugin {\n readonly #router: Router;\n readonly #browser: Browser;\n readonly #removeStartInterceptor: () => void;\n readonly #removeExtensions: () => void;\n readonly #lifecycle: Pick<Plugin, \"onStart\" | \"onStop\" | \"teardown\">;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: Required<BrowserPluginOptions>,\n browser: Browser,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#browser = browser;\n\n this.#removeStartInterceptor = createStartInterceptor(api, browser);\n\n const pluginBuildUrl = (route: string, params?: Params) => {\n const path = router.buildPath(route, params);\n\n return buildUrl(path, options.base);\n };\n\n this.#removeExtensions = api.extendRouter({\n buildUrl: pluginBuildUrl,\n matchUrl: (url: string) => {\n const path = urlToPath(url, options.base);\n\n return path ? api.matchPath(path) : undefined;\n },\n replaceHistoryState: createReplaceHistoryState(\n api,\n router,\n browser,\n pluginBuildUrl,\n ),\n });\n\n const handler = createPopstateHandler({\n router,\n api,\n browser,\n transitionOptions,\n loggerContext: \"browser-plugin\",\n buildUrl: (name: string, params?: Params) =>\n router.buildUrl(name, params),\n });\n\n this.#lifecycle = createPopstateLifecycle({\n browser,\n shared,\n handler,\n cleanup: () => {\n this.#removeStartInterceptor();\n this.#removeExtensions();\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n ...this.#lifecycle,\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const replaceHistory = shouldReplaceHistory(\n navOptions,\n toState,\n fromState,\n this.#router,\n );\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n const shouldPreserveHash =\n !fromState || fromState.path === toState.path;\n\n const finalUrl = shouldPreserveHash\n ? url + this.#browser.getHash()\n : url;\n\n updateBrowserState(toState, finalUrl, replaceHistory, this.#browser);\n },\n };\n }\n}\n","import { createOptionsValidator } from \"browser-env\";\n\nimport { LOGGER_CONTEXT, defaultOptions } from \"./constants\";\n\nimport type { BrowserPluginOptions } from \"./types\";\n\nexport const validateOptions = createOptionsValidator<BrowserPluginOptions>(\n defaultOptions,\n LOGGER_CONTEXT,\n);\n","import { getPluginApi } from \"@real-router/core\";\nimport {\n createSafeBrowser,\n normalizeBase,\n safelyEncodePath,\n} from \"browser-env\";\n\nimport { defaultOptions, source } from \"./constants\";\nimport { BrowserPlugin } from \"./plugin\";\nimport { extractPath } from \"./url-utils\";\nimport { validateOptions } from \"./validation\";\n\nimport type { BrowserPluginOptions } from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\nimport type { Browser, SharedFactoryState } from \"browser-env\";\n\nexport function browserPluginFactory(\n opts?: Partial<BrowserPluginOptions>,\n browser?: Browser,\n): PluginFactory {\n validateOptions(opts);\n\n const options: Required<BrowserPluginOptions> = {\n ...defaultOptions,\n ...opts,\n };\n\n options.base = normalizeBase(options.base);\n\n const resolvedBrowser =\n browser ??\n createSafeBrowser(\n () =>\n safelyEncodePath(\n extractPath(globalThis.location.pathname, options.base),\n ) + globalThis.location.search,\n \"browser-plugin\",\n );\n\n const forceDeactivate = options.forceDeactivate;\n const transitionOptions = { forceDeactivate, source, replace: true as const };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function browserPlugin(routerBase) {\n const plugin = new BrowserPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n resolvedBrowser,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
@@ -1 +1 @@
1
- {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/constants.ts":{"bytes":1624,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/url-utils.ts":{"bytes":2202,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/browser.ts":{"bytes":3076,"imports":[{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/popstate-utils.ts":{"bytes":1657,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/plugin.ts":{"bytes":6714,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/popstate-utils.ts","kind":"import-statement","original":"./popstate-utils"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/validation.ts":{"bytes":1699,"imports":[{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/factory.ts":{"bytes":2537,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"src/browser.ts","kind":"import-statement","original":"./browser"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":1275,"imports":[{"path":"src/factory.ts","kind":"import-statement","original":"./factory"},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":28994},"dist/cjs/index.js":{"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/logger","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["browserPluginFactory","isState"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":1257},"src/browser.ts":{"bytesInOutput":1962},"src/constants.ts":{"bytesInOutput":184},"src/url-utils.ts":{"bytesInOutput":1749},"src/plugin.ts":{"bytesInOutput":5248},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2275},"src/popstate-utils.ts":{"bytesInOutput":581},"src/validation.ts":{"bytesInOutput":1259},"src/index.ts":{"bytesInOutput":0}},"bytes":14993}}}
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":3451,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"../browser-env/dist/esm/index.mjs":{"bytes":4084,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"@real-router/core","kind":"import-statement","external":true},{"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":493,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/url-utils.ts":{"bytes":703,"imports":[{"path":"../browser-env/dist/esm/index.mjs","kind":"import-statement","original":"browser-env"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/plugin.ts":{"bytes":2848,"imports":[{"path":"../browser-env/dist/esm/index.mjs","kind":"import-statement","original":"browser-env"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/validation.ts":{"bytes":288,"imports":[{"path":"../browser-env/dist/esm/index.mjs","kind":"import-statement","original":"browser-env"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/factory.ts":{"bytes":1513,"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"../browser-env/dist/esm/index.mjs","kind":"import-statement","original":"browser-env"},{"path":"src/constants.ts","kind":"import-statement","original":"./constants"},{"path":"src/plugin.ts","kind":"import-statement","original":"./plugin"},{"path":"src/url-utils.ts","kind":"import-statement","original":"./url-utils"},{"path":"src/validation.ts","kind":"import-statement","original":"./validation"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/index.ts":{"bytes":1311,"imports":[{"path":"src/factory.ts","kind":"import-statement","original":"./factory"},{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":8787},"dist/cjs/index.js":{"imports":[{"path":"@real-router/core","kind":"import-statement","external":true},{"path":"@real-router/core","kind":"import-statement","external":true}],"exports":["browserPluginFactory","isState"],"entryPoint":"src/index.ts","inputs":{"src/factory.ts":{"bytesInOutput":827},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2337},"../browser-env/dist/esm/index.mjs":{"bytesInOutput":4688},"src/constants.ts":{"bytesInOutput":126},"src/url-utils.ts":{"bytesInOutput":442},"src/plugin.ts":{"bytesInOutput":1737},"src/validation.ts":{"bytesInOutput":63},"src/index.ts":{"bytesInOutput":0}},"bytes":10466}}}
@@ -1,9 +1,9 @@
1
1
  import { State as State$1, PluginFactory, Params as Params$1 } from '@real-router/core';
2
2
 
3
3
  /**
4
- * Common options shared between hash and history modes
4
+ * Browser plugin configuration.
5
5
  */
6
- interface BaseBrowserPluginOptions {
6
+ interface BrowserPluginOptions {
7
7
  /**
8
8
  * Force deactivation of current route even if canDeactivate returns false.
9
9
  *
@@ -17,163 +17,17 @@ interface BaseBrowserPluginOptions {
17
17
  */
18
18
  base?: string;
19
19
  }
20
- /**
21
- * Hash-based routing configuration.
22
- * Uses URL hash for navigation (e.g., example.com/#/path).
23
- *
24
- * @example
25
- * ```ts
26
- * // Standard hash routing
27
- * browserPluginFactory({ useHash: true })
28
- * // → example.com/#/users
29
- *
30
- * // Hash routing with prefix
31
- * browserPluginFactory({ useHash: true, hashPrefix: "!" })
32
- * // → example.com/#!/users
33
- * ```
34
- */
35
- interface HashModeOptions extends BaseBrowserPluginOptions {
36
- /**
37
- * Enable hash-based routing
38
- */
39
- useHash: true;
40
- /**
41
- * Prefix for hash (e.g., "!" for "#!/path").
42
- * Only valid when useHash is true.
43
- *
44
- * @default ""
45
- */
46
- hashPrefix?: string;
47
- /**
48
- * Not available in hash mode.
49
- * Hash preservation only works with HTML5 History API.
50
- * Use `useHash: false` to enable this option.
51
- */
52
- preserveHash?: never;
53
- }
54
- /**
55
- * HTML5 History API routing configuration.
56
- * Uses pushState/replaceState for navigation (e.g., example.com/path).
57
- *
58
- * @example
59
- * ```ts
60
- * // Standard history routing
61
- * browserPluginFactory({ useHash: false })
62
- * // → example.com/users
63
- *
64
- * // Preserve URL hash fragments
65
- * browserPluginFactory({ useHash: false, preserveHash: true })
66
- * // → example.com/users#section
67
- * ```
68
- */
69
- interface HistoryModeOptions extends BaseBrowserPluginOptions {
70
- /**
71
- * Disable hash-based routing (use HTML5 History API)
72
- *
73
- * @default false
74
- */
75
- useHash?: false;
76
- /**
77
- * Preserve URL hash fragment on initial navigation.
78
- * Only valid when useHash is false.
79
- *
80
- * @default true
81
- */
82
- preserveHash?: boolean;
83
- /**
84
- * Not available in history mode.
85
- * Hash prefix only works with hash-based routing.
86
- * Use `useHash: true` to enable this option.
87
- */
88
- hashPrefix?: never;
89
- }
90
- /**
91
- * Type-safe browser plugin configuration.
92
- *
93
- * Uses discriminated union to prevent conflicting options:
94
- * - Hash mode (useHash: true): allows hashPrefix, forbids preserveHash
95
- * - History mode (useHash: false): allows preserveHash, forbids hashPrefix
96
- *
97
- * @example
98
- * ```ts
99
- * // ✅ Valid: Hash mode with prefix
100
- * const config1: BrowserPluginOptions = {
101
- * useHash: true,
102
- * hashPrefix: "!"
103
- * };
104
- *
105
- * // ✅ Valid: History mode with hash preservation
106
- * const config2: BrowserPluginOptions = {
107
- * useHash: false,
108
- * preserveHash: true
109
- * };
110
- *
111
- * // ❌ Error: Cannot use preserveHash with hash mode
112
- * const config3: BrowserPluginOptions = {
113
- * useHash: true,
114
- * preserveHash: true // Type error!
115
- * };
116
- *
117
- * // ❌ Error: Cannot use hashPrefix with history mode
118
- * const config4: BrowserPluginOptions = {
119
- * useHash: false,
120
- * hashPrefix: "!" // Type error!
121
- * };
122
- * ```
123
- */
124
- type BrowserPluginOptions = HashModeOptions | HistoryModeOptions;
125
- /**
126
- * Browser API abstraction for cross-environment compatibility.
127
- * Provides same interface in browser and SSR contexts.
128
- */
129
- interface Browser {
130
- /**
131
- * Pushes new state to browser history
132
- *
133
- * @param state - History state object
134
- * @param path - URL path
135
- */
20
+
21
+ interface HistoryBrowser {
136
22
  pushState: (state: State$1, path: string) => void;
137
- /**
138
- * Replaces current history state
139
- *
140
- * @param state - History state object
141
- * @param path - URL path
142
- */
143
23
  replaceState: (state: State$1, path: string) => void;
144
24
  addPopstateListener: (fn: (evt: PopStateEvent) => void) => () => void;
145
- /**
146
- * Gets current location path respecting plugin options
147
- *
148
- * @param opts - Plugin options
149
- * @returns Current path string
150
- */
151
- getLocation: (opts: BrowserPluginOptions) => string;
152
- /**
153
- * Gets current URL hash
154
- *
155
- * @returns Hash string (including #)
156
- */
157
25
  getHash: () => string;
158
26
  }
27
+ interface Browser extends HistoryBrowser {
28
+ getLocation: () => string;
29
+ }
159
30
 
160
- /**
161
- * Browser plugin factory for real-router.
162
- * Integrates router with browser history API.
163
- *
164
- * @param opts - Plugin configuration options
165
- * @param browser - Browser API abstraction (for testing/SSR)
166
- * @returns Plugin factory function
167
- *
168
- * @example
169
- * ```ts
170
- * // Hash routing
171
- * router.usePlugin(browserPluginFactory({ useHash: true, hashPrefix: "!" }));
172
- *
173
- * // History routing with hash preservation
174
- * router.usePlugin(browserPluginFactory({ useHash: false, preserveHash: true }));
175
- * ```
176
- */
177
31
  declare function browserPluginFactory(opts?: Partial<BrowserPluginOptions>, browser?: Browser): PluginFactory;
178
32
 
179
33
  type TransitionPhase = "deactivating" | "activating";