@real-router/browser-plugin 0.8.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 +11 -29
- package/dist/cjs/index.d.ts +7 -153
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/metafile-cjs.json +1 -1
- package/dist/esm/index.d.mts +7 -153
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/metafile-esm.json +1 -1
- package/package.json +4 -3
- package/src/constants.ts +2 -36
- package/src/factory.ts +27 -58
- package/src/index.ts +3 -1
- package/src/plugin.ts +54 -187
- package/src/types.ts +2 -179
- package/src/url-utils.ts +12 -80
- package/src/validation.ts +7 -63
- package/src/browser.ts +0 -128
- package/src/popstate-utils.ts +0 -60
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
|
-
|
|
54
|
-
|
|
52
|
+
base: "/app",
|
|
53
|
+
forceDeactivate: true,
|
|
55
54
|
}),
|
|
56
55
|
);
|
|
57
56
|
|
|
58
57
|
router.navigate("products", { id: "123" });
|
|
59
|
-
// URL: http://example.com
|
|
58
|
+
// URL: http://example.com/app/products/123
|
|
60
59
|
```
|
|
61
60
|
|
|
62
|
-
| Option | Type | Default | Description
|
|
63
|
-
| ----------------- | --------- | ------- |
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
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
|
-
**
|
|
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
|
|
|
@@ -79,7 +75,7 @@ The plugin extends the router instance with browser-specific methods (via [`exte
|
|
|
79
75
|
|
|
80
76
|
#### `router.buildUrl(name: string, params?: Params): string`
|
|
81
77
|
|
|
82
|
-
Build full URL with base path
|
|
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
|
-
###
|
|
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
|
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
4
|
+
* Browser plugin configuration.
|
|
5
5
|
*/
|
|
6
|
-
interface
|
|
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
|
-
|
|
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
|
|
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
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/constants.ts","../../src/url-utils.ts","../../src/browser.ts","../../src/popstate-utils.ts","../../src/plugin.ts","../../src/validation.ts","../../src/factory.ts"],"names":["regExpCache","logger","RouterError","defaultOptions","getPluginApi"],"mappings":"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","file":"index.js","sourcesContent":["// packages/browser-plugin/modules/constants.ts\n\n/**\n * Internal type for default options.\n *\n * Why separate type instead of BrowserPluginOptions?\n *\n * BrowserPluginOptions is a discriminated union:\n * - HashModeOptions: allows hashPrefix, forbids preserveHash (never)\n * - HistoryModeOptions: allows preserveHash, forbids hashPrefix (never)\n *\n * We cannot create a single object of type BrowserPluginOptions that contains\n * BOTH hashPrefix and preserveHash - one will always be 'never' depending on useHash.\n *\n * Example - this would fail TypeScript:\n * const defaults: BrowserPluginOptions = {\n * useHash: false, // → HistoryModeOptions branch\n * preserveHash: true, // ✅ OK\n * hashPrefix: \"\" // ❌ Error: Type 'string' is not assignable to type 'never'\n * };\n *\n * DefaultBrowserPluginOptions solves this by containing ALL options,\n * enabling:\n * - Default values for every option\n * - Type validation via typeof defaultOptions\n * - Runtime validation of user-provided option types\n */\nexport interface DefaultBrowserPluginOptions {\n forceDeactivate: boolean;\n useHash: boolean;\n base: string;\n preserveHash: boolean;\n hashPrefix: string;\n}\n\nexport const defaultOptions: DefaultBrowserPluginOptions = {\n forceDeactivate: true,\n useHash: false,\n hashPrefix: \"\",\n base: \"\",\n preserveHash: true,\n};\n\n/**\n * Source identifier for transitions triggered by browser events.\n * Used to distinguish browser-initiated navigation (back/forward buttons)\n * from programmatic navigation (router.navigate()).\n */\nexport const source = \"popstate\";\n\nexport const LOGGER_CONTEXT = \"browser-plugin\";\n","// packages/browser-plugin/src/url-utils.ts\n\nimport { LOGGER_CONTEXT } from \"./constants\";\n\nimport type { URLParseOptions, RegExpCache } from \"./types\";\n\nconst escapeRegExpCache = new Map<string, string>();\n\nexport const escapeRegExp = (str: string): string => {\n const cached = escapeRegExpCache.get(str);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const escaped = str.replaceAll(/[$()*+.?[\\\\\\]^{|}-]/g, String.raw`\\$&`);\n\n escapeRegExpCache.set(str, escaped);\n\n return escaped;\n};\n\nexport function extractPath(\n pathname: string,\n hash: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string {\n if (options.useHash) {\n const escapedHashPrefix = escapeRegExp(options.hashPrefix);\n const path = escapedHashPrefix\n ? hash.replace(regExpCache.get(`^#${escapedHashPrefix}`), \"\")\n : hash.slice(1);\n\n return path || \"/\";\n }\n\n if (options.base) {\n const escapedBase = escapeRegExp(options.base);\n const stripped = pathname.replace(regExpCache.get(`^${escapedBase}`), \"\");\n\n return stripped.startsWith(\"/\") ? stripped : `/${stripped}`;\n }\n\n return pathname;\n}\n\nexport function urlToPath(\n url: string,\n options: URLParseOptions,\n regExpCache: RegExpCache,\n): string | null {\n try {\n const parsedUrl = new URL(url, globalThis.location.origin);\n\n if (![\"http:\", \"https:\"].includes(parsedUrl.protocol)) {\n console.warn(`[${LOGGER_CONTEXT}] Invalid URL protocol in ${url}`);\n\n return null;\n }\n\n return (\n extractPath(parsedUrl.pathname, parsedUrl.hash, options, regExpCache) +\n parsedUrl.search\n );\n } catch (error) {\n console.warn(`[${LOGGER_CONTEXT}] Could not parse url ${url}`, error);\n\n return null;\n }\n}\n\nexport function buildUrl(path: string, base: string, prefix: string): string {\n return base + prefix + path;\n}\n\nexport function createRegExpCache(): RegExpCache {\n const cache = new Map<string, RegExp>();\n\n return {\n get(pattern: string): RegExp {\n const cached = cache.get(pattern);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const newRegExp = new RegExp(pattern);\n\n cache.set(pattern, newRegExp);\n\n return newRegExp;\n },\n };\n}\n","// packages/browser-plugin/modules/browser.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { createRegExpCache, extractPath } from \"./url-utils\";\n\nimport type { Browser, BrowserPluginOptions, URLParseOptions } from \"./types\";\nimport type { State } from \"@real-router/core\";\n\n/** No-operation cleanup function for fallback browser */\nconst NOOP = (): void => {};\n\nconst pushState = (state: State, path: string) => {\n globalThis.history.pushState(state, \"\", path);\n};\n\nconst replaceState = (state: State, path: string) => {\n globalThis.history.replaceState(state, \"\", path);\n};\n\nconst addPopstateListener: Browser[\"addPopstateListener\"] = (fn) => {\n globalThis.addEventListener(\"popstate\", fn);\n\n return () => {\n globalThis.removeEventListener(\"popstate\", fn);\n };\n};\n\nconst regExpCache = createRegExpCache();\n\n/**\n * Safely encodes/decodes path to normalize URL encoding\n *\n * @param path - Path to normalize\n * @returns Normalized path or original on error\n */\nconst safelyEncodePath = (path: string): string => {\n try {\n return encodeURI(decodeURI(path));\n } catch (error) {\n logger.warn(LOGGER_CONTEXT, `Could not encode path \"${path}\"`, error);\n\n return path;\n }\n};\n\nconst getLocation = (opts: BrowserPluginOptions) => {\n const rawPath = extractPath(\n globalThis.location.pathname,\n globalThis.location.hash,\n opts as URLParseOptions,\n regExpCache,\n );\n\n return safelyEncodePath(rawPath) + globalThis.location.search;\n};\n\n/**\n * Gets current URL hash\n */\nconst getHash = () => globalThis.location.hash;\n\n/**\n * Creates a fallback browser for non-browser environments (SSR).\n * Logs warning on first method call to help diagnose misconfiguration.\n *\n * @returns Browser API with no-op implementations\n */\nfunction createFallbackBrowser(): Browser {\n let hasWarned = false;\n\n const warnOnce = (method: string) => {\n if (!hasWarned) {\n logger.warn(\n LOGGER_CONTEXT,\n `Browser plugin is running in a non-browser environment. ` +\n `Method \"${method}\" is a no-op. ` +\n `This is expected for SSR, but may indicate misconfiguration if you expected browser behavior.`,\n );\n hasWarned = true;\n }\n };\n\n return {\n pushState: () => {\n warnOnce(\"pushState\");\n },\n replaceState: () => {\n warnOnce(\"replaceState\");\n },\n addPopstateListener: () => {\n warnOnce(\"addPopstateListener\");\n\n return NOOP;\n },\n getLocation: () => {\n warnOnce(\"getLocation\");\n\n return \"\";\n },\n getHash: () => {\n warnOnce(\"getHash\");\n\n return \"\";\n },\n };\n}\n\n/**\n * Creates browser API abstraction that works in both browser and SSR environments\n *\n * @returns Browser API object\n */\nexport function createSafeBrowser(): Browser {\n const isBrowser =\n typeof globalThis.window !== \"undefined\" && !!globalThis.history;\n\n return isBrowser\n ? {\n pushState,\n replaceState,\n addPopstateListener,\n getLocation,\n getHash,\n }\n : createFallbackBrowser();\n}\n","import { isStateStrict as isState } from \"type-guards\";\n\nimport type { BrowserPluginOptions, Browser } from \"./types\";\nimport type { PluginApi, State, Params } from \"@real-router/core\";\n\n/**\n * Extracts route name and params from a popstate event.\n *\n * - If history.state is a valid router state → returns name/params from it\n * - If not (e.g. manually entered URL) → matches current URL against route tree\n * - Returns undefined if no route matches\n *\n * @param evt - PopStateEvent from browser\n * @param api - PluginApi instance\n * @param browser - Browser API instance\n * @param options - Browser plugin options\n * @returns Route identifier or undefined\n */\nexport function getRouteFromEvent(\n evt: PopStateEvent,\n api: PluginApi,\n browser: Browser,\n options: BrowserPluginOptions,\n): { name: string; params: Params } | undefined {\n if (isState(evt.state)) {\n return { name: evt.state.name, params: evt.state.params };\n }\n\n const state = api.matchPath(browser.getLocation(options));\n\n return state ? { name: state.name, params: state.params } : undefined;\n}\n\n/**\n * Updates browser state (pushState or replaceState)\n *\n * @param state - Router state\n * @param url - URL to set\n * @param replace - Whether to replace instead of push\n * @param browser - Browser API instance\n */\nexport function updateBrowserState(\n state: State,\n url: string,\n replace: boolean,\n browser: Browser,\n): void {\n const historyState = {\n meta: state.meta,\n name: state.name,\n params: state.params,\n path: state.path,\n };\n\n if (replace) {\n browser.replaceState(historyState, url);\n } else {\n browser.pushState(historyState, url);\n }\n}\n","import { RouterError } from \"@real-router/core\";\n\nimport { LOGGER_CONTEXT } from \"./constants\";\nimport { getRouteFromEvent, updateBrowserState } from \"./popstate-utils\";\nimport { buildUrl, urlToPath } from \"./url-utils\";\n\nimport type {\n Browser,\n BrowserPluginOptions,\n RegExpCache,\n SharedFactoryState,\n URLParseOptions,\n} from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n PluginApi,\n Router,\n State,\n Plugin,\n} from \"@real-router/core\";\n\nexport class BrowserPlugin {\n readonly #router: Router;\n readonly #api: PluginApi;\n readonly #options: BrowserPluginOptions;\n readonly #browser: Browser;\n readonly #regExpCache: RegExpCache;\n readonly #prefix: string;\n readonly #transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n };\n readonly #shared: SharedFactoryState;\n\n #isTransitioning = false;\n #deferredPopstateEvent: PopStateEvent | null = null;\n readonly #removeStartInterceptor: () => void;\n readonly #removeExtensions: () => void;\n\n constructor(\n router: Router,\n api: PluginApi,\n options: BrowserPluginOptions,\n browser: Browser,\n regExpCache: RegExpCache,\n transitionOptions: {\n source: string;\n replace: true;\n forceDeactivate?: boolean;\n },\n shared: SharedFactoryState,\n ) {\n this.#router = router;\n this.#api = api;\n this.#options = options;\n this.#browser = browser;\n this.#regExpCache = regExpCache;\n this.#transitionOptions = transitionOptions;\n this.#shared = shared;\n\n const normalizedOptions = options as URLParseOptions;\n\n this.#prefix = options.useHash ? `#${normalizedOptions.hashPrefix}` : \"\";\n\n this.#removeStartInterceptor = this.#api.addInterceptor(\n \"start\",\n (next, path) => next(path ?? this.#browser.getLocation(this.#options)),\n );\n\n this.#removeExtensions = this.#api.extendRouter({\n buildUrl: (route: string, params?: Params) => {\n const path = this.#router.buildPath(route, params);\n\n return buildUrl(\n path,\n (this.#options as URLParseOptions).base,\n this.#prefix,\n );\n },\n matchUrl: (url: string) => {\n const path = urlToPath(\n url,\n this.#options as URLParseOptions,\n this.#regExpCache,\n );\n\n return path ? this.#api.matchPath(path) : undefined;\n },\n replaceHistoryState: (name: string, params: Params = {}) => {\n const state = this.#api.buildState(name, params);\n\n if (!state) {\n throw new Error(\n `[real-router] Cannot replace state: route \"${name}\" is not found`,\n );\n }\n\n const builtState = this.#api.makeState(\n state.name,\n state.params,\n this.#router.buildPath(state.name, state.params),\n {\n params: state.meta,\n },\n 1, // forceId\n );\n const url = this.#router.buildUrl(name, params);\n\n updateBrowserState(builtState, url, true, this.#browser);\n },\n });\n }\n\n getPlugin(): Plugin {\n return {\n onStart: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n }\n\n this.#shared.removePopStateListener = this.#browser.addPopstateListener(\n (evt: PopStateEvent) => void this.#onPopState(evt),\n );\n },\n\n onStop: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n },\n\n onTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n navOptions: NavigationOptions,\n ) => {\n const shouldReplaceHistory =\n (navOptions.replace ?? !fromState) ||\n (!!navOptions.reload &&\n this.#router.areStatesEqual(toState, fromState, false));\n\n const url = this.#router.buildUrl(toState.name, toState.params);\n\n const shouldPreserveHash =\n !!this.#options.preserveHash &&\n (!fromState || fromState.path === toState.path);\n\n const finalUrl = shouldPreserveHash\n ? url + this.#browser.getHash()\n : url;\n\n updateBrowserState(\n toState,\n finalUrl,\n shouldReplaceHistory,\n this.#browser,\n );\n },\n\n teardown: () => {\n if (this.#shared.removePopStateListener) {\n this.#shared.removePopStateListener();\n this.#shared.removePopStateListener = undefined;\n }\n\n this.#removeStartInterceptor();\n this.#removeExtensions();\n },\n };\n }\n\n #processDeferredEvent(): void {\n if (this.#deferredPopstateEvent) {\n const event = this.#deferredPopstateEvent;\n\n this.#deferredPopstateEvent = null;\n console.warn(`[${LOGGER_CONTEXT}] Processing deferred popstate event`);\n void this.#onPopState(event);\n }\n }\n\n async #onPopState(evt: PopStateEvent): Promise<void> {\n if (this.#isTransitioning) {\n console.warn(\n `[${LOGGER_CONTEXT}] Transition in progress, deferring popstate event`,\n );\n this.#deferredPopstateEvent = evt;\n\n return;\n }\n\n this.#isTransitioning = true;\n\n try {\n const route = getRouteFromEvent(\n evt,\n this.#api,\n this.#browser,\n this.#options,\n );\n\n // eslint-disable-next-line unicorn/prefer-ternary\n if (route) {\n await this.#router.navigate(\n route.name,\n route.params,\n this.#transitionOptions,\n );\n } else {\n await this.#router.navigateToDefault({\n ...this.#transitionOptions,\n reload: true,\n replace: true,\n });\n }\n } catch (error) {\n if (!(error instanceof RouterError)) {\n this.#recoverFromCriticalError(error);\n }\n } finally {\n this.#isTransitioning = false;\n this.#processDeferredEvent();\n }\n }\n\n #recoverFromCriticalError(error: unknown): void {\n console.error(`[${LOGGER_CONTEXT}] Critical error in onPopState`, error);\n\n try {\n const currentState = this.#router.getState();\n\n /* v8 ignore next -- @preserve: router always has state after start(); defensive guard for edge cases */\n if (currentState) {\n const url = this.#router.buildUrl(\n currentState.name,\n currentState.params,\n );\n\n this.#browser.replaceState(currentState, url);\n }\n } catch (recoveryError) {\n console.error(\n `[${LOGGER_CONTEXT}] Failed to recover from critical error`,\n recoveryError,\n );\n }\n }\n}\n","import { type DefaultBrowserPluginOptions, LOGGER_CONTEXT } from \"./constants\";\n\nimport type { BrowserPluginOptions } from \"./types\";\n\nfunction isDefaultOptionKey(\n key: string,\n defaults: DefaultBrowserPluginOptions,\n): key is keyof DefaultBrowserPluginOptions {\n return key in defaults;\n}\n\nfunction validateOptionType(\n key: keyof DefaultBrowserPluginOptions,\n value: unknown,\n expectedType: string,\n): boolean {\n const actualType = typeof value;\n\n if (actualType !== expectedType && value !== undefined) {\n console.warn(\n `[${LOGGER_CONTEXT}] Invalid type for '${key}': expected ${expectedType}, got ${actualType}`,\n );\n\n return false;\n }\n\n return true;\n}\n\nexport function validateOptions(\n opts: Partial<BrowserPluginOptions> | undefined,\n defaultOptions: DefaultBrowserPluginOptions,\n): boolean {\n if (!opts) {\n return false;\n }\n\n let hasInvalidTypes = false;\n\n for (const key of Object.keys(opts)) {\n if (isDefaultOptionKey(key, defaultOptions)) {\n const expectedType = typeof defaultOptions[key];\n const value = opts[key];\n const isValid = validateOptionType(key, value, expectedType);\n\n if (!isValid) {\n hasInvalidTypes = true;\n }\n }\n }\n\n if (opts.useHash === true && \"preserveHash\" in opts) {\n console.warn(`[${LOGGER_CONTEXT}] preserveHash ignored in hash mode`);\n }\n\n if (opts.useHash === false && \"hashPrefix\" in opts) {\n const optsRecord = opts as unknown as Record<string, unknown>;\n const hashPrefix = optsRecord.hashPrefix;\n\n if (hashPrefix !== undefined && hashPrefix !== \"\") {\n console.warn(`[${LOGGER_CONTEXT}] hashPrefix ignored in history mode`);\n }\n }\n\n return hasInvalidTypes;\n}\n","import { getPluginApi } from \"@real-router/core\";\n\nimport { createSafeBrowser } from \"./browser\";\nimport { defaultOptions, LOGGER_CONTEXT, source } from \"./constants\";\nimport { BrowserPlugin } from \"./plugin\";\nimport { createRegExpCache } from \"./url-utils\";\nimport { validateOptions } from \"./validation\";\n\nimport type {\n BrowserPluginOptions,\n Browser,\n SharedFactoryState,\n} from \"./types\";\nimport type { PluginFactory, Router } from \"@real-router/core\";\n\n/**\n * Browser plugin factory for real-router.\n * Integrates router with browser history API.\n *\n * @param opts - Plugin configuration options\n * @param browser - Browser API abstraction (for testing/SSR)\n * @returns Plugin factory function\n *\n * @example\n * ```ts\n * // Hash routing\n * router.usePlugin(browserPluginFactory({ useHash: true, hashPrefix: \"!\" }));\n *\n * // History routing with hash preservation\n * router.usePlugin(browserPluginFactory({ useHash: false, preserveHash: true }));\n * ```\n */\nexport function browserPluginFactory(\n opts?: Partial<BrowserPluginOptions>,\n browser: Browser = createSafeBrowser(),\n): PluginFactory {\n const hasInvalidTypes = validateOptions(opts, defaultOptions);\n\n let options = { ...defaultOptions, ...opts } as BrowserPluginOptions;\n\n if (hasInvalidTypes) {\n console.warn(\n `[${LOGGER_CONTEXT}] Using default options due to invalid types`,\n );\n options = { ...defaultOptions } as BrowserPluginOptions;\n }\n\n if (options.useHash === true) {\n delete (options as unknown as Record<string, unknown>).preserveHash;\n } else {\n delete (options as unknown as Record<string, unknown>).hashPrefix;\n }\n\n if (options.base) {\n if (!options.base.startsWith(\"/\")) {\n options.base = `/${options.base}`;\n }\n\n if (options.base.endsWith(\"/\")) {\n options.base = options.base.slice(0, -1);\n }\n }\n\n const regExpCache = createRegExpCache();\n\n const forceDeactivate = options.forceDeactivate;\n /* v8 ignore next 4 -- @preserve both branches tested, coverage tool limitation */\n const transitionOptions =\n forceDeactivate === undefined\n ? { source, replace: true as const }\n : { forceDeactivate, source, replace: true as const };\n\n const shared: SharedFactoryState = { removePopStateListener: undefined };\n\n return function browserPlugin(routerBase) {\n const plugin = new BrowserPlugin(\n routerBase as Router,\n getPluginApi(routerBase),\n options,\n browser,\n regExpCache,\n transitionOptions,\n shared,\n );\n\n return plugin.getPlugin();\n };\n}\n"]}
|
|
1
|
+
{"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"},"
|
|
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}}}
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -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
|
-
*
|
|
4
|
+
* Browser plugin configuration.
|
|
5
5
|
*/
|
|
6
|
-
interface
|
|
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
|
-
|
|
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";
|