@real-router/browser-plugin 0.10.0 → 0.10.2

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
@@ -1,22 +1,20 @@
1
1
  # @real-router/browser-plugin
2
2
 
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
3
+ [![npm](https://img.shields.io/npm/v/@real-router/browser-plugin.svg?style=flat-square)](https://www.npmjs.com/package/@real-router/browser-plugin)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@real-router/browser-plugin.svg?style=flat-square)](https://www.npmjs.com/package/@real-router/browser-plugin)
5
+ [![bundle size](https://deno.bundlejs.com/?q=@real-router/browser-plugin&treeshake=[*]&badge=detailed)](https://bundlejs.com/?q=@real-router/browser-plugin&treeshake=[*])
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](../../LICENSE)
5
7
 
6
- Browser History API integration for Real-Router. Synchronizes router state with browser URL and handles back/forward navigation.
8
+ > Browser History API integration for [Real-Router](https://github.com/greydragon888/real-router). Synchronizes router state with browser URL and handles back/forward navigation.
7
9
 
8
10
  ## Installation
9
11
 
10
12
  ```bash
11
13
  npm install @real-router/browser-plugin
12
- # or
13
- pnpm add @real-router/browser-plugin
14
- # or
15
- yarn add @real-router/browser-plugin
16
- # or
17
- bun add @real-router/browser-plugin
18
14
  ```
19
15
 
16
+ **Peer dependency:** `@real-router/core`
17
+
20
18
  ## Quick Start
21
19
 
22
20
  ```typescript
@@ -25,165 +23,112 @@ import { browserPluginFactory } from "@real-router/browser-plugin";
25
23
 
26
24
  const router = createRouter([
27
25
  { name: "home", path: "/" },
28
- { name: "products", path: "/products/:id" },
29
- { name: "cart", path: "/cart" },
26
+ { name: "users", path: "/users/:id" },
30
27
  ]);
31
28
 
32
- // Basic usage
33
29
  router.usePlugin(browserPluginFactory());
34
-
35
- // With options
36
- router.usePlugin(
37
- browserPluginFactory({
38
- base: "/app",
39
- }),
40
- );
41
-
42
- await router.start();
30
+ await router.start(); // path inferred from browser location
43
31
  ```
44
32
 
45
- ---
46
-
47
- ## Configuration
33
+ ## Options
48
34
 
49
35
  ```typescript
50
- router.usePlugin(
51
- browserPluginFactory({
52
- base: "/app",
53
- forceDeactivate: true,
54
- }),
55
- );
56
-
57
- router.navigate("products", { id: "123" });
58
- // URL: http://example.com/app/products/123
36
+ router.usePlugin(browserPluginFactory({
37
+ base: "/app", // Base path prefix for all routes
38
+ forceDeactivate: true, // Bypass canDeactivate guards on back/forward
39
+ }));
59
40
  ```
60
41
 
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 |
65
-
66
- > **Looking for hash routing?** Use [`@real-router/hash-plugin`](https://www.npmjs.com/package/@real-router/hash-plugin) instead.
67
-
68
- See [Wiki](https://github.com/greydragon888/real-router/wiki/browser-plugin#3-configuration-options) for detailed option descriptions and examples.
42
+ | Option | Type | Default | Description |
43
+ |--------|------|---------|-------------|
44
+ | `base` | `string` | `""` | Base path for all routes (e.g., `"/app"` → URLs start with `/app/...`) |
45
+ | `forceDeactivate` | `boolean` | `true` | Bypass `canDeactivate` guards on browser back/forward |
69
46
 
70
- ---
47
+ > **Hash routing?** Use [`@real-router/hash-plugin`](https://www.npmjs.com/package/@real-router/hash-plugin) instead.
71
48
 
72
- ## Added Router Methods
49
+ ## Router Extensions
73
50
 
74
- The plugin extends the router instance with browser-specific methods (via [`extendRouter()`](https://github.com/greydragon888/real-router/wiki/core#extendrouter)):
51
+ The plugin extends the router instance with three methods via [`extendRouter()`](https://github.com/greydragon888/real-router/wiki/plugin-architecture):
75
52
 
76
- #### `router.buildUrl(name: string, params?: Params): string`
77
-
78
- Build full URL with base path.\
79
- `name: string` route name\
80
- `params?: Params` route parameters\
81
- Returns: `string` — full URL\
82
- [Wiki](https://github.com/greydragon888/real-router/wiki/browser-plugin#5-router-interaction)
53
+ | Method | Returns | Description |
54
+ |--------|---------|-------------|
55
+ | `buildUrl(name, params?)` | `string` | Build full URL with base path |
56
+ | `matchUrl(url)` | `State \| undefined` | Parse URL to router state |
57
+ | `replaceHistoryState(name, params?, title?)` | `void` | Update browser URL without triggering navigation |
83
58
 
84
59
  ```typescript
85
60
  router.buildUrl("users", { id: "123" });
86
61
  // => "/app/users/123" (with base "/app")
87
- ```
88
-
89
- #### `router.matchUrl(url: string): State | undefined`
90
62
 
91
- Parse URL to router state.\
92
- `url: string` URL to parse\
93
- Returns: `State | undefined`\
94
- [Wiki](https://github.com/greydragon888/real-router/wiki/browser-plugin#5-router-interaction)
95
-
96
- ```typescript
97
- router.navigate("page1");
98
- router.navigate("page2");
99
- router.navigate("page3");
63
+ router.matchUrl("/app/users/123");
64
+ // => { name: "users", params: { id: "123" }, path: "/users/123" }
100
65
 
101
- // User clicks back twice rapidly
102
- // Plugin ensures router ends at page1
103
- // URL and router state remain synchronized
66
+ // Update URL silently (no transition, no guards)
67
+ router.replaceHistoryState("users", { id: "456" });
104
68
  ```
105
69
 
106
- #### `router.replaceHistoryState(name: string, params?: Params, title?: string): void`
107
-
108
- Update browser URL without triggering navigation.\
109
- `name: string` — route name\
110
- `params?: Params` — route parameters\
111
- `title?: string` — page title\
112
- Returns: `void`\
113
- [Wiki](https://github.com/greydragon888/real-router/wiki/browser-plugin#5-router-interaction)
70
+ ### `buildUrl` vs `buildPath`
114
71
 
115
72
  ```typescript
116
- router.replaceHistoryState("users", { id: "456" });
73
+ router.buildPath("users", { id: 1 }); // "/users/1" — core, no base
74
+ router.buildUrl("users", { id: 1 }); // "/app/users/1" — plugin, with base
117
75
  ```
118
76
 
119
- ---
120
-
121
- ## Usage Examples
122
-
123
- ### With Base Path
77
+ ### `replaceHistoryState` vs `navigate({ replace: true })`
124
78
 
125
79
  ```typescript
126
- router.usePlugin(
127
- browserPluginFactory({
128
- base: "/app",
129
- }),
130
- );
131
-
132
- router.navigate("users", { id: "123" });
133
- // URL: /app/users/123
80
+ router.replaceHistoryState(name, params); // URL only, no transition
81
+ router.navigate(name, params, { replace: true }); // Full transition + URL update
134
82
  ```
135
83
 
136
- ### Form Protection
84
+ ## Form Protection
85
+
86
+ Set `forceDeactivate: false` to respect `canDeactivate` guards on back/forward:
137
87
 
138
88
  ```typescript
139
- router.usePlugin(
140
- browserPluginFactory({
141
- forceDeactivate: false,
142
- }),
143
- );
89
+ router.usePlugin(browserPluginFactory({ forceDeactivate: false }));
144
90
 
145
- import { getLifecycleApi } from "@real-router/core";
91
+ import { getLifecycleApi } from "@real-router/core/api";
146
92
 
147
93
  const lifecycle = getLifecycleApi(router);
148
94
  lifecycle.addDeactivateGuard("checkout", () => (toState, fromState) => {
149
- return !hasUnsavedChanges(); // false blocks navigation
95
+ return !hasUnsavedChanges(); // false blocks back/forward
150
96
  });
151
97
  ```
152
98
 
153
- ---
154
-
155
99
  ## SSR Support
156
100
 
157
- The plugin is SSR-safe with automatic fallback:
101
+ The plugin is SSR-safe automatically detects the environment and falls back to no-ops:
158
102
 
159
103
  ```typescript
160
104
  // Server-side — no errors, methods return safe defaults
161
105
  router.usePlugin(browserPluginFactory());
162
- router.buildUrl("home"); // Works
163
- router.matchUrl("/path"); // Returns undefined
106
+ router.buildUrl("home"); // returns path without base
107
+ router.matchUrl("/path"); // returns undefined
164
108
  ```
165
109
 
166
- ---
167
-
168
110
  ## Documentation
169
111
 
170
- Full documentation available on the [Wiki](https://github.com/greydragon888/real-router/wiki/browser-plugin):
112
+ Full documentation: [Wiki browser-plugin](https://github.com/greydragon888/real-router/wiki/browser-plugin)
171
113
 
172
114
  - [Configuration Options](https://github.com/greydragon888/real-router/wiki/browser-plugin#3-configuration-options)
173
115
  - [Lifecycle Hooks](https://github.com/greydragon888/real-router/wiki/browser-plugin#4-lifecycle-hooks)
174
- - [Router Methods](https://github.com/greydragon888/real-router/wiki/browser-plugin#5-router-interaction)
175
116
  - [Behavior & Edge Cases](https://github.com/greydragon888/real-router/wiki/browser-plugin#8-behavior)
176
117
  - [Migration from router5](https://github.com/greydragon888/real-router/wiki/browser-plugin#11-migration-from-router5)
177
118
 
178
- ---
179
-
180
119
  ## Related Packages
181
120
 
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`)
184
- - [@real-router/react](https://www.npmjs.com/package/@real-router/react) React integration
185
- - [@real-router/logger-plugin](https://www.npmjs.com/package/@real-router/logger-plugin) Debug logging
121
+ | Package | Description |
122
+ |---------|-------------|
123
+ | [@real-router/core](https://www.npmjs.com/package/@real-router/core) | Core router (required peer dependency) |
124
+ | [@real-router/hash-plugin](https://www.npmjs.com/package/@real-router/hash-plugin) | Hash-based routing (`#/path`) |
125
+ | [@real-router/react](https://www.npmjs.com/package/@real-router/react) | React integration |
126
+ | [@real-router/logger-plugin](https://www.npmjs.com/package/@real-router/logger-plugin) | Development logging |
127
+
128
+ ## Contributing
129
+
130
+ See [contributing guidelines](../../CONTRIBUTING.md) for development setup and PR process.
186
131
 
187
132
  ## License
188
133
 
189
- MIT © [Oleg Ivanov](https://github.com/greydragon888)
134
+ [MIT](../../LICENSE) © [Oleg Ivanov](https://github.com/greydragon888)
@@ -33,11 +33,11 @@ declare function browserPluginFactory(opts?: Partial<BrowserPluginOptions>, brow
33
33
  type TransitionPhase = "deactivating" | "activating";
34
34
  type TransitionReason = "success" | "blocked" | "cancelled" | "error";
35
35
  interface TransitionMeta {
36
- readonly reload?: boolean;
37
- readonly redirected?: boolean;
38
36
  phase: TransitionPhase;
39
- from?: string;
40
37
  reason: TransitionReason;
38
+ reload?: boolean;
39
+ redirected?: boolean;
40
+ from?: string;
41
41
  blocker?: string;
42
42
  segments: {
43
43
  deactivated: string[];
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- var t=require("@real-router/core"),e=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function r(t,e=new WeakSet){if(null==t)return!0;const n=typeof t;if("string"===n||"boolean"===n)return!0;if("number"===n)return Number.isFinite(t);if("function"===n||"symbol"===n)return!1;if(Array.isArray(t))return!e.has(t)&&(e.add(t),t.every(t=>r(t,e)));if("object"===n){if(e.has(t))return!1;e.add(t);const n=Object.getPrototypeOf(t);return(null===n||n===Object.prototype)&&Object.values(t).every(t=>r(t,e))}return!1}function n(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 o=!1;for(const e in t){if(!Object.hasOwn(t,e))continue;const r=t[e];if(!n(r)){const t=typeof r;if("function"===t||"symbol"===t)return!1;o=!0;break}}return!o||r(t)}function a(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 r=t;return!!function(t){return function(t){return"string"==typeof t&&(""===t||!(t.length>1e4)&&(!!t.startsWith("@@")||e.test(t)))}(t.name)&&"string"==typeof t.path&&o(t.params)}(r)&&(void 0===r.meta||function(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)&&!a(t[e]))return!1;return!0}(e.params)||"id"in e&&"number"!=typeof e.id)}(r.meta))}var s=(t,e)=>{globalThis.history.pushState(t,"",e)},c=(t,e)=>{globalThis.history.replaceState(t,"",e)},u=t=>(globalThis.addEventListener("popstate",t),()=>{globalThis.removeEventListener("popstate",t)}),l=()=>globalThis.location.hash,p=()=>{},f=t=>{let e=!1;return r=>{e||(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: "${t}"). Method "${r}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),e=!0)}},h=t=>{const e=f(t);return{pushState:()=>{e("pushState")},replaceState:()=>{e("replaceState")},addPopstateListener:()=>(e("addPopstateListener"),p),getHash:()=>(e("getHash"),"")}};function d(t,e,r,n){const o={meta:t.meta,name:t.name,params:t.params,path:t.path};r?n.replaceState(o,e):n.pushState(o,e)}function b(e){let r=!1,n=null;async function o(a){if(r)return console.warn(`[${e.loggerContext}] Transition in progress, deferring popstate event`),void(n=a);r=!0;try{const t=function(t,e,r){if(i(t.state))return{name:t.state.name,params:t.state.params};const n=e.matchPath(r.getLocation());return n?{name:n.name,params:n.params}:void 0}(a,e.api,e.browser);t?await e.router.navigate(t.name,t.params,e.transitionOptions):e.allowNotFound?e.router.navigateToNotFound(e.browser.getLocation()):await e.router.navigateToDefault({...e.transitionOptions,reload:!0,replace:!0})}catch(r){r instanceof t.RouterError||function(t){console.error(`[${e.loggerContext}] Critical error in onPopState`,t);try{const t=e.router.getState();if(t){const r=e.buildUrl(t.name,t.params);e.browser.replaceState(t,r)}}catch(t){console.error(`[${e.loggerContext}] Failed to recover from critical error`,t)}}(r)}finally{r=!1,function(){if(n){const t=n;n=null,console.warn(`[${e.loggerContext}] Processing deferred popstate event`),o(t)}}()}}return t=>{o(t)}}function m(t,e,r,n){return(o,a={})=>{const i=t.buildState(o,a);if(!i)throw new Error(`[real-router] Cannot replace state: route "${o}" is not found`);d(t.makeState(i.name,i.params,e.buildPath(i.name,i.params),{params:i.meta},1),n(o,a),!0,r)}}var g={forceDeactivate:!0,base:""},v="browser-plugin";function y(t,e){if(e&&t.startsWith(e)){const r=t.slice(e.length);return r.startsWith("/")?r:`/${r}`}return t}var w,S,P=class{#t;#e;#r;#n;#o;constructor(t,e,r,n,o,a){var i;this.#t=t,this.#e=n,this.#r=(i=n,e.addInterceptor("start",(t,e)=>t(e??i.getLocation())));const s=(e,n)=>{return o=t.buildPath(e,n),r.base+o;var o};this.#n=e.extendRouter({buildUrl:s,matchUrl:t=>{const n=function(t,e){const r=function(t,e){try{const r=new URL(t,globalThis.location.origin);return["http:","https:"].includes(r.protocol)?r:(console.warn(`[${e}] Invalid URL protocol in ${t}`),null)}catch(r){return console.warn(`[${e}] Could not parse url ${t}`,r),null}}(t,v);return r?y(r.pathname,e)+r.search:null}(t,r.base);return n?e.matchPath(n):void 0},replaceHistoryState:m(e,t,n,s)});const c=b({router:t,api:e,browser:n,allowNotFound:e.getOptions().allowNotFound,transitionOptions:o,loggerContext:"browser-plugin",buildUrl:(e,r)=>t.buildUrl(e,r)});this.#o=function(t){return{onStart:()=>{t.shared.removePopStateListener&&t.shared.removePopStateListener(),t.shared.removePopStateListener=t.browser.addPopstateListener(t.handler)},onStop:()=>{t.shared.removePopStateListener&&(t.shared.removePopStateListener(),t.shared.removePopStateListener=void 0)},teardown:()=>{t.shared.removePopStateListener&&(t.shared.removePopStateListener(),t.shared.removePopStateListener=void 0),t.cleanup()}}}({browser:n,shared:a,handler:c,cleanup:()=>{this.#r(),this.#n()}})}getPlugin(){return{...this.#o,onTransitionSuccess:(t,e,r)=>{const n=(a=e,i=this.#t,((o=r).replace??!a)||!!o.reload&&i.areStatesEqual(t,a,!1));var o,a,i;const s=this.#t.buildUrl(t.name,t.params);d(t,e&&e.path!==t.path?s:s+this.#e.getHash(),n,this.#e)}}}},L=(w=g,S=v,t=>{if(t)for(const e of Object.keys(t))if(e in w){const r=t[e],n=typeof w[e],o=typeof r;if(void 0!==r&&o!==n)throw new Error(`[${S}] Invalid type for '${e}': expected ${n}, got ${o}`)}});exports.browserPluginFactory=function(e,r){L(e);const n={...g,...e};n.base=function(t){if(!t)return t;let e=t;return e.startsWith("/")||(e=`/${e}`),e.endsWith("/")&&(e=e.slice(0,-1)),e}(n.base);const o=r??function(t,e){if(void 0!==globalThis.window&&globalThis.history)return{pushState:s,replaceState:c,addPopstateListener:u,getLocation:t,getHash:l};const r=f(e);return{...h(e),getLocation:()=>(r("getLocation"),"")}}(()=>(t=>{try{return encodeURI(decodeURI(t))}catch(e){return console.warn(`[browser-env] Could not encode path "${t}"`,e),t}})(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(e){return new P(e,t.getPluginApi(e),n,o,a,i).getPlugin()}},exports.isState=i;//# sourceMappingURL=index.js.map
1
+ var e=require("@real-router/core/api"),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 o(e){if(null==e)return!0;const t=typeof e;return"string"===t||"boolean"===t||"number"===t&&Number.isFinite(e)}function a(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(!o(n)){const e=typeof n;if("function"===e||"symbol"===e)return!1;r=!0;break}}return!r||n(e)}function i(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("@@")||r.test(e)))}(e.name)&&"string"==typeof e.path&&a(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)&&!i(e[t]))return!1;return!0}(t.params)||"id"in t&&"number"!=typeof t.id)}(t.meta))}var c=(e,t)=>{globalThis.history.pushState(e,"",t)},u=(e,t)=>{globalThis.history.replaceState(e,"",t)},l=e=>(globalThis.addEventListener("popstate",e),()=>{globalThis.removeEventListener("popstate",e)}),p=()=>globalThis.location.hash,f=()=>{},h=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)}},d=e=>{const t=h(e);return{pushState:()=>{t("pushState")},replaceState:()=>{t("replaceState")},addPopstateListener:()=>(t("addPopstateListener"),f),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 m(e){let r=!1,n=null;async function o(a){if(r)return console.warn(`[${e.loggerContext}] Transition in progress, deferring popstate event`),void(n=a);r=!0;try{const t=function(e,t,r){if(s(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,e.api,e.browser);t?await e.router.navigate(t.name,t.params,e.transitionOptions):e.allowNotFound?e.router.navigateToNotFound(e.browser.getLocation()):await e.router.navigateToDefault({...e.transitionOptions,reload:!0,replace:!0})}catch(r){r instanceof t.RouterError||function(t){console.error(`[${e.loggerContext}] Critical error in onPopState`,t);try{const t=e.router.getState();if(t){const r=e.buildUrl(t.name,t.params);e.browser.replaceState(t,r)}}catch(t){console.error(`[${e.loggerContext}] Failed to recover from critical error`,t)}}(r)}finally{r=!1,function(){if(n){const t=n;n=null,console.warn(`[${e.loggerContext}] Processing deferred popstate event`),o(t)}}()}}return e=>{o(e)}}function g(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 v={forceDeactivate:!0,base:""},y="browser-plugin";function w(e,t){if(t&&e.startsWith(t)){const r=e.slice(t.length);return r.startsWith("/")?r:`/${r}`}return e}var S,P,L=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,y);return r?w(r.pathname,t)+r.search:null}(e,r.base);return n?t.matchPath(n):void 0},replaceHistoryState:g(t,e,n,s)});const c=m({router:e,api:t,browser:n,allowNotFound:t.getOptions().allowNotFound,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)}}}},$=(S=v,P=y,e=>{if(e)for(const t of Object.keys(e))if(t in S){const r=e[t],n=typeof S[t],o=typeof r;if(void 0!==r&&o!==n)throw new Error(`[${P}] Invalid type for '${t}': expected ${n}, got ${o}`)}});exports.browserPluginFactory=function(t,r){$(t);const n={...v,...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:c,replaceState:u,addPopstateListener:l,getLocation:e,getHash:p};const r=h(t);return{...d(t),getLocation:()=>(r("getLocation"),"")}}(()=>(e=>{try{return encodeURI(decodeURI(e))}catch(t){return console.warn(`[browser-env] Could not encode path "${e}"`,t),e}})(w(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 L(t,e.getPluginApi(t),n,o,a,i).getPlugin()}},exports.isState=s;//# sourceMappingURL=index.js.map
@@ -1 +1 @@
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 allowNotFound: api.getOptions().allowNotFound,\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
+ {"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 Router,\n State,\n Plugin,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\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 allowNotFound: api.getOptions().allowNotFound,\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/api\";\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"},"../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":4153,"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":2901,"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":8872},"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":4761},"src/constants.ts":{"bytesInOutput":126},"src/url-utils.ts":{"bytesInOutput":442},"src/plugin.ts":{"bytesInOutput":1790},"src/validation.ts":{"bytesInOutput":63},"src/index.ts":{"bytesInOutput":0}},"bytes":10592}}}
1
+ {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.2/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.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"../browser-env/dist/esm/index.mjs":{"bytes":4153,"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.8_typescript@5.9.3_yaml@2.8.2/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.8_typescript@5.9.3_yaml@2.8.2/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.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/plugin.ts":{"bytes":2944,"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.8_typescript@5.9.3_yaml@2.8.2/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.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"},"src/factory.ts":{"bytes":1517,"imports":[{"path":"@real-router/core/api","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.8_typescript@5.9.3_yaml@2.8.2/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.8_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":8921},"dist/cjs/index.js":{"imports":[{"path":"@real-router/core/api","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":831},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2337},"../browser-env/dist/esm/index.mjs":{"bytesInOutput":4761},"src/constants.ts":{"bytesInOutput":126},"src/url-utils.ts":{"bytesInOutput":442},"src/plugin.ts":{"bytesInOutput":1790},"src/validation.ts":{"bytesInOutput":63},"src/index.ts":{"bytesInOutput":0}},"bytes":10596}}}
@@ -33,11 +33,11 @@ declare function browserPluginFactory(opts?: Partial<BrowserPluginOptions>, brow
33
33
  type TransitionPhase = "deactivating" | "activating";
34
34
  type TransitionReason = "success" | "blocked" | "cancelled" | "error";
35
35
  interface TransitionMeta {
36
- readonly reload?: boolean;
37
- readonly redirected?: boolean;
38
36
  phase: TransitionPhase;
39
- from?: string;
40
37
  reason: TransitionReason;
38
+ reload?: boolean;
39
+ redirected?: boolean;
40
+ from?: string;
41
41
  blocker?: string;
42
42
  segments: {
43
43
  deactivated: string[];
@@ -1 +1 @@
1
- import{getPluginApi as t,RouterError 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 o(t){if(null==t)return!0;const e=typeof t;return"string"===e||"boolean"===e||"number"===e&&Number.isFinite(t)}function a(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||n(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 s(t){if("object"!=typeof t||null===t)return!1;const e=t;return!!function(t){return function(t){return"string"==typeof t&&(""===t||!(t.length>1e4)&&(!!t.startsWith("@@")||r.test(t)))}(t.name)&&"string"==typeof t.path&&a(t.params)}(e)&&(void 0===e.meta||function(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)||"id"in e&&"number"!=typeof e.id)}(e.meta))}var c=(t,e)=>{globalThis.history.pushState(t,"",e)},u=(t,e)=>{globalThis.history.replaceState(t,"",e)},l=t=>(globalThis.addEventListener("popstate",t),()=>{globalThis.removeEventListener("popstate",t)}),p=()=>globalThis.location.hash,f=()=>{},h=t=>{let e=!1;return r=>{e||(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: "${t}"). Method "${r}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),e=!0)}},d=t=>{const e=h(t);return{pushState:()=>{e("pushState")},replaceState:()=>{e("replaceState")},addPopstateListener:()=>(e("addPopstateListener"),f),getHash:()=>(e("getHash"),"")}};function b(t,e,r,n){const o={meta:t.meta,name:t.name,params:t.params,path:t.path};r?n.replaceState(o,e):n.pushState(o,e)}function m(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(t,e,r){if(s(t.state))return{name:t.state.name,params:t.state.params};const n=e.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):t.allowNotFound?t.router.navigateToNotFound(t.browser.getLocation()):await t.router.navigateToDefault({...t.transitionOptions,reload:!0,replace:!0})}catch(r){r instanceof e||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 t=>{o(t)}}function g(t,e,r,n){return(o,a={})=>{const i=t.buildState(o,a);if(!i)throw new Error(`[real-router] Cannot replace state: route "${o}" is not found`);b(t.makeState(i.name,i.params,e.buildPath(i.name,i.params),{params:i.meta},1),n(o,a),!0,r)}}var v={forceDeactivate:!0,base:""},y="browser-plugin";function w(t,e){if(e&&t.startsWith(e)){const r=t.slice(e.length);return r.startsWith("/")?r:`/${r}`}return t}var S,P,L=class{#t;#e;#r;#n;#o;constructor(t,e,r,n,o,a){var i;this.#t=t,this.#e=n,this.#r=(i=n,e.addInterceptor("start",(t,e)=>t(e??i.getLocation())));const s=(e,n)=>{return o=t.buildPath(e,n),r.base+o;var o};this.#n=e.extendRouter({buildUrl:s,matchUrl:t=>{const n=function(t,e){const r=function(t,e){try{const r=new URL(t,globalThis.location.origin);return["http:","https:"].includes(r.protocol)?r:(console.warn(`[${e}] Invalid URL protocol in ${t}`),null)}catch(r){return console.warn(`[${e}] Could not parse url ${t}`,r),null}}(t,y);return r?w(r.pathname,e)+r.search:null}(t,r.base);return n?e.matchPath(n):void 0},replaceHistoryState:g(e,t,n,s)});const c=m({router:t,api:e,browser:n,allowNotFound:e.getOptions().allowNotFound,transitionOptions:o,loggerContext:"browser-plugin",buildUrl:(e,r)=>t.buildUrl(e,r)});this.#o=function(t){return{onStart:()=>{t.shared.removePopStateListener&&t.shared.removePopStateListener(),t.shared.removePopStateListener=t.browser.addPopstateListener(t.handler)},onStop:()=>{t.shared.removePopStateListener&&(t.shared.removePopStateListener(),t.shared.removePopStateListener=void 0)},teardown:()=>{t.shared.removePopStateListener&&(t.shared.removePopStateListener(),t.shared.removePopStateListener=void 0),t.cleanup()}}}({browser:n,shared:a,handler:c,cleanup:()=>{this.#r(),this.#n()}})}getPlugin(){return{...this.#o,onTransitionSuccess:(t,e,r)=>{const n=(a=e,i=this.#t,((o=r).replace??!a)||!!o.reload&&i.areStatesEqual(t,a,!1));var o,a,i;const s=this.#t.buildUrl(t.name,t.params);b(t,e&&e.path!==t.path?s:s+this.#e.getHash(),n,this.#e)}}}},$=(S=v,P=y,t=>{if(t)for(const e of Object.keys(t))if(e in S){const r=t[e],n=typeof S[e],o=typeof r;if(void 0!==r&&o!==n)throw new Error(`[${P}] Invalid type for '${e}': expected ${n}, got ${o}`)}});function O(e,r){$(e);const n={...v,...e};n.base=function(t){if(!t)return t;let e=t;return e.startsWith("/")||(e=`/${e}`),e.endsWith("/")&&(e=e.slice(0,-1)),e}(n.base);const o=r??function(t,e){if(void 0!==globalThis.window&&globalThis.history)return{pushState:c,replaceState:u,addPopstateListener:l,getLocation:t,getHash:p};const r=h(e);return{...d(e),getLocation:()=>(r("getLocation"),"")}}(()=>(t=>{try{return encodeURI(decodeURI(t))}catch(e){return console.warn(`[browser-env] Could not encode path "${t}"`,e),t}})(w(globalThis.location.pathname,n.base))+globalThis.location.search,"browser-plugin"),a={forceDeactivate:n.forceDeactivate,source:"popstate",replace:!0},i={removePopStateListener:void 0};return function(e){return new L(e,t(e),n,o,a,i).getPlugin()}}export{O as browserPluginFactory,s as isState};//# sourceMappingURL=index.mjs.map
1
+ import{getPluginApi as t}from"@real-router/core/api";import{RouterError as e}from"@real-router/core";var r=/^[A-Z_a-z][\w-]*(?:\.[A-Z_a-z][\w-]*)*$/;function o(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=>o(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=>o(t,e))}return!1}function n(t){if(null==t)return!0;const e=typeof t;return"string"===e||"boolean"===e||"number"===e&&Number.isFinite(t)}function a(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 o=t[e];if(!n(o)){const t=typeof o;if("function"===t||"symbol"===t)return!1;r=!0;break}}return!r||o(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 s(t){if("object"!=typeof t||null===t)return!1;const e=t;return!!function(t){return function(t){return"string"==typeof t&&(""===t||!(t.length>1e4)&&(!!t.startsWith("@@")||r.test(t)))}(t.name)&&"string"==typeof t.path&&a(t.params)}(e)&&(void 0===e.meta||function(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)||"id"in e&&"number"!=typeof e.id)}(e.meta))}var c=(t,e)=>{globalThis.history.pushState(t,"",e)},u=(t,e)=>{globalThis.history.replaceState(t,"",e)},l=t=>(globalThis.addEventListener("popstate",t),()=>{globalThis.removeEventListener("popstate",t)}),p=()=>globalThis.location.hash,f=()=>{},h=t=>{let e=!1;return r=>{e||(console.warn(`[browser-env] Browser API is running in a non-browser environment (context: "${t}"). Method "${r}" is a no-op. This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`),e=!0)}},d=t=>{const e=h(t);return{pushState:()=>{e("pushState")},replaceState:()=>{e("replaceState")},addPopstateListener:()=>(e("addPopstateListener"),f),getHash:()=>(e("getHash"),"")}};function m(t,e,r,o){const n={meta:t.meta,name:t.name,params:t.params,path:t.path};r?o.replaceState(n,e):o.pushState(n,e)}function b(t){let r=!1,o=null;async function n(a){if(r)return console.warn(`[${t.loggerContext}] Transition in progress, deferring popstate event`),void(o=a);r=!0;try{const e=function(t,e,r){if(s(t.state))return{name:t.state.name,params:t.state.params};const o=e.matchPath(r.getLocation());return o?{name:o.name,params:o.params}:void 0}(a,t.api,t.browser);e?await t.router.navigate(e.name,e.params,t.transitionOptions):t.allowNotFound?t.router.navigateToNotFound(t.browser.getLocation()):await t.router.navigateToDefault({...t.transitionOptions,reload:!0,replace:!0})}catch(r){r instanceof e||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(o){const e=o;o=null,console.warn(`[${t.loggerContext}] Processing deferred popstate event`),n(e)}}()}}return t=>{n(t)}}function g(t,e,r,o){return(n,a={})=>{const i=t.buildState(n,a);if(!i)throw new Error(`[real-router] Cannot replace state: route "${n}" is not found`);m(t.makeState(i.name,i.params,e.buildPath(i.name,i.params),{params:i.meta},1),o(n,a),!0,r)}}var v={forceDeactivate:!0,base:""},y="browser-plugin";function w(t,e){if(e&&t.startsWith(e)){const r=t.slice(e.length);return r.startsWith("/")?r:`/${r}`}return t}var S,P,L=class{#t;#e;#r;#o;#n;constructor(t,e,r,o,n,a){var i;this.#t=t,this.#e=o,this.#r=(i=o,e.addInterceptor("start",(t,e)=>t(e??i.getLocation())));const s=(e,o)=>{return n=t.buildPath(e,o),r.base+n;var n};this.#o=e.extendRouter({buildUrl:s,matchUrl:t=>{const o=function(t,e){const r=function(t,e){try{const r=new URL(t,globalThis.location.origin);return["http:","https:"].includes(r.protocol)?r:(console.warn(`[${e}] Invalid URL protocol in ${t}`),null)}catch(r){return console.warn(`[${e}] Could not parse url ${t}`,r),null}}(t,y);return r?w(r.pathname,e)+r.search:null}(t,r.base);return o?e.matchPath(o):void 0},replaceHistoryState:g(e,t,o,s)});const c=b({router:t,api:e,browser:o,allowNotFound:e.getOptions().allowNotFound,transitionOptions:n,loggerContext:"browser-plugin",buildUrl:(e,r)=>t.buildUrl(e,r)});this.#n=function(t){return{onStart:()=>{t.shared.removePopStateListener&&t.shared.removePopStateListener(),t.shared.removePopStateListener=t.browser.addPopstateListener(t.handler)},onStop:()=>{t.shared.removePopStateListener&&(t.shared.removePopStateListener(),t.shared.removePopStateListener=void 0)},teardown:()=>{t.shared.removePopStateListener&&(t.shared.removePopStateListener(),t.shared.removePopStateListener=void 0),t.cleanup()}}}({browser:o,shared:a,handler:c,cleanup:()=>{this.#r(),this.#o()}})}getPlugin(){return{...this.#n,onTransitionSuccess:(t,e,r)=>{const o=(a=e,i=this.#t,((n=r).replace??!a)||!!n.reload&&i.areStatesEqual(t,a,!1));var n,a,i;const s=this.#t.buildUrl(t.name,t.params);m(t,e&&e.path!==t.path?s:s+this.#e.getHash(),o,this.#e)}}}},$=(S=v,P=y,t=>{if(t)for(const e of Object.keys(t))if(e in S){const r=t[e],o=typeof S[e],n=typeof r;if(void 0!==r&&n!==o)throw new Error(`[${P}] Invalid type for '${e}': expected ${o}, got ${n}`)}});function O(e,r){$(e);const o={...v,...e};o.base=function(t){if(!t)return t;let e=t;return e.startsWith("/")||(e=`/${e}`),e.endsWith("/")&&(e=e.slice(0,-1)),e}(o.base);const n=r??function(t,e){if(void 0!==globalThis.window&&globalThis.history)return{pushState:c,replaceState:u,addPopstateListener:l,getLocation:t,getHash:p};const r=h(e);return{...d(e),getLocation:()=>(r("getLocation"),"")}}(()=>(t=>{try{return encodeURI(decodeURI(t))}catch(e){return console.warn(`[browser-env] Could not encode path "${t}"`,e),t}})(w(globalThis.location.pathname,o.base))+globalThis.location.search,"browser-plugin"),a={forceDeactivate:o.forceDeactivate,source:"popstate",replace:!0},i={removePopStateListener:void 0};return function(e){return new L(e,t(e),o,n,a,i).getPlugin()}}export{O as browserPluginFactory,s as isState};//# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["i","f","c"],"mappings":"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","file":"index.mjs","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 allowNotFound: api.getOptions().allowNotFound,\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
+ {"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["i","f","c"],"mappings":"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","file":"index.mjs","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 Router,\n State,\n Plugin,\n} from \"@real-router/core\";\nimport type { PluginApi } from \"@real-router/core/api\";\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 allowNotFound: api.getOptions().allowNotFound,\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/api\";\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":{"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[],"format":"esm"},"../browser-env/dist/esm/index.mjs":{"bytes":4153,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"@real-router/core","kind":"import-statement","external":true}],"format":"esm"},"src/constants.ts":{"bytes":493,"imports":[],"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"}],"format":"esm"},"src/plugin.ts":{"bytes":2901,"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"}],"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"}],"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"}],"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"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":8872},"dist/esm/index.mjs":{"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":4761},"src/constants.ts":{"bytesInOutput":126},"src/url-utils.ts":{"bytesInOutput":442},"src/plugin.ts":{"bytesInOutput":1790},"src/validation.ts":{"bytesInOutput":63},"src/index.ts":{"bytesInOutput":0}},"bytes":10592}}}
1
+ {"inputs":{"../type-guards/dist/esm/index.mjs":{"bytes":3451,"imports":[],"format":"esm"},"../browser-env/dist/esm/index.mjs":{"bytes":4153,"imports":[{"path":"../type-guards/dist/esm/index.mjs","kind":"import-statement","original":"type-guards"},{"path":"@real-router/core","kind":"import-statement","external":true}],"format":"esm"},"src/constants.ts":{"bytes":493,"imports":[],"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"}],"format":"esm"},"src/plugin.ts":{"bytes":2944,"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"}],"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"}],"format":"esm"},"src/factory.ts":{"bytes":1517,"imports":[{"path":"@real-router/core/api","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"}],"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"}],"format":"esm"}},"outputs":{"dist/esm/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":8921},"dist/esm/index.mjs":{"imports":[{"path":"@real-router/core/api","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":831},"../type-guards/dist/esm/index.mjs":{"bytesInOutput":2337},"../browser-env/dist/esm/index.mjs":{"bytesInOutput":4761},"src/constants.ts":{"bytesInOutput":126},"src/url-utils.ts":{"bytesInOutput":442},"src/plugin.ts":{"bytesInOutput":1790},"src/validation.ts":{"bytesInOutput":63},"src/index.ts":{"bytesInOutput":0}},"bytes":10596}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/browser-plugin",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
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,16 +45,18 @@
45
45
  },
46
46
  "sideEffects": false,
47
47
  "dependencies": {
48
- "@real-router/core": "^0.35.0"
48
+ "@real-router/core": "^0.37.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@testing-library/jest-dom": "6.9.1",
52
- "jsdom": "27.4.0",
53
- "browser-env": "^0.1.1",
54
- "type-guards": "^0.3.5"
52
+ "jsdom": "28.1.0",
53
+ "browser-env": "^0.1.4",
54
+ "type-guards": "^0.3.7"
55
55
  },
56
56
  "scripts": {
57
57
  "test": "vitest",
58
+ "test:stress": "vitest --config vitest.config.stress.mts --run",
59
+ "test:properties": "vitest --config vitest.config.properties.mts --run",
58
60
  "build": "tsup",
59
61
  "type-check": "tsc --noEmit",
60
62
  "lint": "eslint --cache --ext .ts src/ tests/ --fix --max-warnings 0",
package/src/factory.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { getPluginApi } from "@real-router/core";
1
+ import { getPluginApi } from "@real-router/core/api";
2
2
  import {
3
3
  createSafeBrowser,
4
4
  normalizeBase,
package/src/plugin.ts CHANGED
@@ -13,11 +13,11 @@ import type { BrowserPluginOptions } from "./types";
13
13
  import type {
14
14
  NavigationOptions,
15
15
  Params,
16
- PluginApi,
17
16
  Router,
18
17
  State,
19
18
  Plugin,
20
19
  } from "@real-router/core";
20
+ import type { PluginApi } from "@real-router/core/api";
21
21
  import type { Browser, SharedFactoryState } from "browser-env";
22
22
 
23
23
  export class BrowserPlugin {