white-label-router 1.1.2 → 4.0.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 +152 -267
- package/dist/index.d.ts +111 -0
- package/dist/index.js +259 -343
- package/dist/index.js.map +1 -0
- package/package.json +42 -8
- package/.babelrc +0 -4
- package/.editorconfig +0 -13
- package/.npmignore +0 -2
- package/.travis.yml +0 -5
- package/bin/compile.sh +0 -2
- package/bower.json +0 -26
- package/src/index.js +0 -325
package/README.md
CHANGED
|
@@ -1,361 +1,246 @@
|
|
|
1
1
|
# white-label-router
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`white-label-router` is a small client-side router built on the browser History API. It maps URL prefixes to functions or view lifecycle objects, intercepts links marked with `data-pushstate`, handles browser back/forward navigation, and can receive navigation requests through a mediator.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Requirements
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Node.js `^22.18.0` or `>=24.11.0` for installation and development
|
|
8
|
+
- A browser environment with `window.history`, `window.location`, and standard DOM events at runtime
|
|
9
|
+
- Server fallback configuration that serves the application entry page for client-managed URLs
|
|
8
10
|
|
|
9
|
-
## Install
|
|
11
|
+
## Install and import
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
```
|
|
14
|
-
npm install white-label-router --save
|
|
13
|
+
```sh
|
|
14
|
+
npm install white-label-router
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```
|
|
17
|
+
```js
|
|
20
18
|
import Router from 'white-label-router';
|
|
21
19
|
```
|
|
22
20
|
|
|
21
|
+
## Complete example
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
The most simple way simply executes any function you define:
|
|
23
|
+
```js
|
|
24
|
+
import Router from 'white-label-router';
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
const MyRoute = class extends Router {
|
|
26
|
+
class ApplicationRouter extends Router {
|
|
30
27
|
constructor() {
|
|
31
28
|
super();
|
|
32
|
-
this.routes = {
|
|
33
|
-
defaultRoute: (scope, locationData) => {
|
|
34
|
-
//here you would put any view specific logic for the defaultRoute
|
|
35
|
-
window.console.log('the defaultRoute executed');
|
|
36
|
-
},
|
|
37
|
-
'/page2': (scope, locationData) => {
|
|
38
|
-
//here you would put any view specific logic for the page2 route
|
|
39
|
-
window.console.log('the page2 route executed');
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
```
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
const MyRoute = class extends Router {
|
|
50
|
-
constructor() {
|
|
51
|
-
super();
|
|
30
|
+
this.scope = document.querySelector('main');
|
|
52
31
|
this.routes = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
initialize: (scope, locationData) => {
|
|
56
|
-
window.console.log('the defaultRoute initialized');
|
|
57
|
-
},
|
|
58
|
-
destroy: (scope, locationData) => {
|
|
59
|
-
window.console.log('the defaultRoute has been destroyed');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
32
|
+
'/products': (scope, location) => {
|
|
33
|
+
scope.textContent = `Product: ${location.data.url[0] || 'all'}`;
|
|
62
34
|
},
|
|
63
|
-
'/
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
window.console.log('the page2 initialized');
|
|
67
|
-
},
|
|
68
|
-
destroy: (scope, locationData) => {
|
|
69
|
-
window.console.log('the page2 has been destroyed');
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
You have some additional options when using the view object approach. They include the ability to set the page title for a route and also do security check before a route's view is initialized at the router level:
|
|
79
|
-
|
|
80
|
-
```
|
|
81
|
-
const MyRoute = class extends Router {
|
|
82
|
-
constructor() {
|
|
83
|
-
super();
|
|
84
|
-
this.routes = {
|
|
85
|
-
defaultRoute: {
|
|
86
|
-
title: 'Home',
|
|
35
|
+
'/account': {
|
|
36
|
+
title: 'Your account',
|
|
37
|
+
secure: () => Boolean(window.currentUser),
|
|
87
38
|
view: {
|
|
88
|
-
initialize: (scope,
|
|
89
|
-
|
|
39
|
+
initialize: (scope, location) => {
|
|
40
|
+
scope.textContent = 'Account';
|
|
41
|
+
console.log(location.data.query);
|
|
90
42
|
},
|
|
91
|
-
destroy: (
|
|
92
|
-
|
|
43
|
+
destroy: () => {
|
|
44
|
+
console.log('Leaving account');
|
|
93
45
|
}
|
|
94
46
|
}
|
|
95
47
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
view: {
|
|
99
|
-
initialize: (scope, locationData) => {
|
|
100
|
-
window.console.log('the page2 initialized');
|
|
101
|
-
},
|
|
102
|
-
destroy: (scope, locationData) => {
|
|
103
|
-
window.console.log('the page2 has been destroyed');
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
|
-
secure: this.isSecure
|
|
48
|
+
defaultRoute: () => {
|
|
49
|
+
console.log('No configured route matched.');
|
|
107
50
|
}
|
|
108
51
|
};
|
|
109
52
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const router = new ApplicationRouter();
|
|
56
|
+
router.initialize();
|
|
118
57
|
```
|
|
119
58
|
|
|
120
|
-
|
|
59
|
+
At startup, `initialize()` reads the current path and query string, registers document click and browser `popstate` listeners, and navigates to the matching route.
|
|
121
60
|
|
|
122
|
-
##
|
|
61
|
+
## Define routes
|
|
123
62
|
|
|
124
|
-
|
|
63
|
+
A route may be a function:
|
|
125
64
|
|
|
65
|
+
```js
|
|
66
|
+
this.routes = {
|
|
67
|
+
'/help': (scope, location) => {
|
|
68
|
+
scope.textContent = `Help page: ${location.url}`;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
126
71
|
```
|
|
127
|
-
const MyRoute = class extends Router {
|
|
128
|
-
constructor() {
|
|
129
|
-
super();
|
|
130
72
|
|
|
131
|
-
|
|
132
|
-
this.scope = document.querySelector('body');
|
|
73
|
+
Or it may describe a title, security check, and view lifecycle:
|
|
133
74
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
75
|
+
```js
|
|
76
|
+
this.routes = {
|
|
77
|
+
'/orders': {
|
|
78
|
+
title: 'Orders',
|
|
79
|
+
secure(scope, location) {
|
|
80
|
+
return Boolean(window.currentUser);
|
|
81
|
+
},
|
|
82
|
+
view: {
|
|
83
|
+
initialize(scope, location) {
|
|
84
|
+
scope.textContent = 'Order history';
|
|
138
85
|
},
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
window.console.log('the page2 route executed');
|
|
86
|
+
destroy(scope, nextLocation) {
|
|
87
|
+
scope.replaceChildren();
|
|
142
88
|
}
|
|
143
|
-
}
|
|
144
|
-
|
|
89
|
+
}
|
|
145
90
|
}
|
|
146
91
|
};
|
|
147
92
|
```
|
|
148
93
|
|
|
149
|
-
|
|
94
|
+
The `secure` function must return exactly `true` to allow navigation. When navigation succeeds, the previous route's `destroy()` runs before the next route's `initialize()`.
|
|
150
95
|
|
|
151
|
-
|
|
152
|
-
const myRouter = new MyRouter();
|
|
153
|
-
|
|
154
|
-
myRouter.initialize();
|
|
155
|
-
```
|
|
96
|
+
### Route matching behavior
|
|
156
97
|
|
|
157
|
-
|
|
98
|
+
Routes match complete path boundaries in object insertion order. For example, `/products` matches `/products` and `/products/42`, but it does not match `/products-old`. Put more specific routes before broader ones:
|
|
158
99
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
100
|
+
```js
|
|
101
|
+
this.routes = {
|
|
102
|
+
'/products/sale': saleRoute,
|
|
103
|
+
'/products': productsRoute,
|
|
104
|
+
defaultRoute
|
|
105
|
+
};
|
|
163
106
|
```
|
|
164
107
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
you can also trigger navigation to a route with the navigate method
|
|
108
|
+
If no prefix matches, `defaultRoute` is used when present.
|
|
168
109
|
|
|
169
|
-
|
|
170
|
-
myRouter.navigate('/page2');
|
|
171
|
-
```
|
|
110
|
+
## Navigate from HTML
|
|
172
111
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
Optionally you can use a mediator, such as White Label Mediator, with the router. To do so you would like this:
|
|
112
|
+
The router delegates one document-level click listener. Add `data-pushstate` to an anchor to use client-side navigation:
|
|
176
113
|
|
|
114
|
+
```html
|
|
115
|
+
<a href="/products/42" data-pushstate>
|
|
116
|
+
<span>View product 42</span>
|
|
117
|
+
</a>
|
|
177
118
|
```
|
|
178
|
-
const MyRoute = class extends Router {
|
|
179
|
-
constructor() {
|
|
180
|
-
super();
|
|
181
119
|
|
|
182
|
-
|
|
183
|
-
this.scope = document.querySelector('body');
|
|
120
|
+
Clicks on nested elements such as the `span` are resolved to the enclosing anchor. Links without `data-pushstate` keep their normal browser behavior.
|
|
184
121
|
|
|
185
|
-
|
|
122
|
+
Modified clicks, non-left clicks, downloads, links targeting another browsing context, and cross-origin URLs also keep their normal browser behavior.
|
|
186
123
|
|
|
187
|
-
|
|
188
|
-
defaultRoute: (scope, locationData) => {
|
|
189
|
-
//here you would put any view specific logic for the defaultRoute
|
|
190
|
-
window.console.log('the defaultRoute executed');
|
|
191
|
-
},
|
|
192
|
-
'/page2': (scope, locationData) => {
|
|
193
|
-
//here you would put any view specific logic for the page2 route
|
|
194
|
-
window.console.log('the page2 route executed');
|
|
195
|
-
}
|
|
196
|
-
};
|
|
124
|
+
## Navigate from JavaScript
|
|
197
125
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
Then anywhere else in the application you want to trigger the router to navigate you could do so like this:
|
|
203
|
-
|
|
204
|
-
```
|
|
205
|
-
myMediator.emit('router:navigate', {
|
|
206
|
-
url: '/authentication',
|
|
207
|
-
message: 'Session expired, please login again.'
|
|
126
|
+
```js
|
|
127
|
+
router.navigate('/products/42', {
|
|
128
|
+
source: 'featured-products'
|
|
208
129
|
});
|
|
209
130
|
```
|
|
210
131
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
Optionally you and provide a DOM element to be the scope of the router. That DOM object will then be passed into all your routes as they are executed. To do so you would like this:
|
|
214
|
-
|
|
215
|
-
```
|
|
216
|
-
const MyRoute = class extends Router {
|
|
217
|
-
constructor() {
|
|
218
|
-
super();
|
|
219
|
-
|
|
220
|
-
// the container element where all views will be placed
|
|
221
|
-
this.scope = document.querySelector('body');
|
|
132
|
+
The second argument becomes `location.data.mediator`. Unless the call represents a browser `popstate`, successful navigation adds the URL to browser history.
|
|
222
133
|
|
|
223
|
-
|
|
224
|
-
defaultRoute: (scope, locationData) => {
|
|
225
|
-
//here you would put any view specific logic for the defaultRoute
|
|
226
|
-
window.console.log('the defaultRoute executed');
|
|
227
|
-
},
|
|
228
|
-
'/page2': (scope, locationData) => {
|
|
229
|
-
//here you would put any view specific logic for the page2 route
|
|
230
|
-
window.console.log('the page2 route executed');
|
|
231
|
-
}
|
|
232
|
-
};
|
|
134
|
+
## Location data
|
|
233
135
|
|
|
234
|
-
|
|
235
|
-
};
|
|
236
|
-
```
|
|
136
|
+
Every route receives the configured `scope` and a location object:
|
|
237
137
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
The router passes in a location data object to the route that is being executed. Here is an example object passed:
|
|
241
|
-
|
|
242
|
-
```
|
|
138
|
+
```js
|
|
243
139
|
{
|
|
244
|
-
url:
|
|
140
|
+
url: '/products/42?color=blue',
|
|
245
141
|
data: {
|
|
246
|
-
url: [],
|
|
247
|
-
mediator: {
|
|
248
|
-
|
|
142
|
+
url: ['42'],
|
|
143
|
+
mediator: {
|
|
144
|
+
source: 'featured-products'
|
|
145
|
+
},
|
|
146
|
+
query: {
|
|
147
|
+
color: 'blue'
|
|
148
|
+
}
|
|
249
149
|
}
|
|
250
150
|
}
|
|
251
151
|
```
|
|
252
152
|
|
|
253
|
-
|
|
153
|
+
- `url` is the complete route URL.
|
|
154
|
+
- `data.url` contains path segments after the matched route prefix.
|
|
155
|
+
- `data.mediator` contains the object passed to `navigate()` or received from the mediator.
|
|
156
|
+
- `data.query` contains parsed query values.
|
|
254
157
|
|
|
255
|
-
|
|
256
|
-
{
|
|
257
|
-
url: ['/user/fred'],
|
|
258
|
-
data: {
|
|
259
|
-
url: ['fred'],
|
|
260
|
-
mediator: {},
|
|
261
|
-
query: {}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
```
|
|
158
|
+
Path segments and query values are URI-decoded with browser-native URL APIs. Application code must still validate values before using them.
|
|
265
159
|
|
|
266
|
-
|
|
160
|
+
## Version 3 migration note
|
|
267
161
|
|
|
268
|
-
|
|
269
|
-
{
|
|
270
|
-
url: ['/user?name=fred'],
|
|
271
|
-
data: {
|
|
272
|
-
url: [],
|
|
273
|
-
mediator: {},
|
|
274
|
-
query: {
|
|
275
|
-
name: 'fred'
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
```
|
|
162
|
+
Version 3 changes route matching from unrestricted string prefixes to path boundaries. A route such as `/page2` no longer matches `/page23`. Define `/page23` explicitly if the application previously depended on that behavior.
|
|
280
163
|
|
|
281
|
-
|
|
164
|
+
## Mediator navigation
|
|
282
165
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
166
|
+
Assign an EventEmitter-compatible mediator before initialization:
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
import Mediator from 'white-label-mediator';
|
|
170
|
+
|
|
171
|
+
const mediator = new Mediator();
|
|
172
|
+
const router = new ApplicationRouter();
|
|
173
|
+
router.mediator = mediator;
|
|
174
|
+
router.initialize();
|
|
175
|
+
|
|
176
|
+
mediator.emit('router:navigate', {
|
|
177
|
+
url: '/account',
|
|
178
|
+
reason: 'Session refreshed'
|
|
287
179
|
});
|
|
288
180
|
```
|
|
289
181
|
|
|
290
|
-
|
|
182
|
+
The complete emitted object is passed to the route as `location.data.mediator`. `destroy()` removes the document, window, and mediator listeners. Repeated listener initialization is idempotent and does not add duplicate listeners.
|
|
291
183
|
|
|
292
|
-
|
|
293
|
-
{
|
|
294
|
-
url: ['/authentication'],
|
|
295
|
-
data: {
|
|
296
|
-
url: [],
|
|
297
|
-
mediator: {
|
|
298
|
-
url: '/authentication',
|
|
299
|
-
message: 'Session expired, please login again.'
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
```
|
|
184
|
+
## Lifecycle
|
|
304
185
|
|
|
305
|
-
|
|
186
|
+
| Method | Behavior |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| `initialize()` | Reads the browser URL, adds listeners, and runs the initial route. |
|
|
189
|
+
| `navigate(url, data, isPopState)` | Matches and runs a route, updates history, and returns the router; returns `false` when blocked or invalid. |
|
|
190
|
+
| `addListeners()` | Adds browser and optional mediator listeners once. |
|
|
191
|
+
| `removeListeners()` | Removes listeners previously added by this router. |
|
|
192
|
+
| `destroy()` | Calls `removeListeners()` and returns the router. |
|
|
306
193
|
|
|
307
|
-
|
|
194
|
+
Call `destroy()` when the router is no longer used:
|
|
308
195
|
|
|
309
|
-
```
|
|
310
|
-
|
|
196
|
+
```js
|
|
197
|
+
router.destroy();
|
|
311
198
|
```
|
|
312
199
|
|
|
313
|
-
|
|
200
|
+
## Development
|
|
314
201
|
|
|
202
|
+
```sh
|
|
203
|
+
npm ci
|
|
204
|
+
npm run build
|
|
205
|
+
npm run typecheck
|
|
206
|
+
npm test
|
|
207
|
+
npm run coverage
|
|
208
|
+
npm run audit
|
|
315
209
|
```
|
|
316
|
-
const myRouter = class extends Router {
|
|
317
|
-
// this is the constructor. This executed whenever the view is instantiated.
|
|
318
|
-
constructor() {
|
|
319
210
|
|
|
320
|
-
|
|
211
|
+
The npm package publishes the compiled `dist` file and this README.
|
|
321
212
|
|
|
322
|
-
|
|
323
|
-
this.scope = document.querySelector('body');
|
|
213
|
+
## TypeScript development and version 4.0.0 migration
|
|
324
214
|
|
|
325
|
-
|
|
326
|
-
defaultRoute: (scope, locationData) => {
|
|
327
|
-
//here you would put any view specific logic for the defaultRoute
|
|
328
|
-
window.console.log('the defaultRoute executed');
|
|
329
|
-
},
|
|
330
|
-
'/page2': (scope, locationData) => {
|
|
331
|
-
//here you would put any view specific logic for the page2 route
|
|
332
|
-
window.console.log('the page2 route executed');
|
|
333
|
-
}
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
};
|
|
215
|
+
Implementation code now uses strict TypeScript. Builds emit JavaScript, source maps with embedded source, and `.d.ts` declarations into `dist`. JavaScript callers can still use the package without compiling TypeScript themselves. JSDoc comments describe parameters, return values, lifecycle behavior, and validation at the implementation, and are retained in declarations.
|
|
338
216
|
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
instantiate your router
|
|
217
|
+
```ts
|
|
218
|
+
import Router from 'white-label-router';
|
|
342
219
|
|
|
343
|
-
|
|
344
|
-
const
|
|
220
|
+
const router = new Router();
|
|
221
|
+
const greeting: Router.Route = (scope, location) => {
|
|
222
|
+
console.log(location.data.query.name); // /hello?name=Ada
|
|
223
|
+
};
|
|
224
|
+
router.routes['/hello'] = greeting;
|
|
225
|
+
router.initialize();
|
|
345
226
|
```
|
|
346
227
|
|
|
347
|
-
|
|
228
|
+
`Router.Route` covers function routes and objects with a guard or view lifecycle. `Router.Location` describes decoded URL data, `Router.Navigation` describes mediator payloads, and `Router.Handler` describes a route callback. Browser DOM types are required when compiling a consuming application.
|
|
348
229
|
|
|
349
|
-
|
|
350
|
-
myRouter.initialize();
|
|
351
|
-
```
|
|
230
|
+
This is a major release because the distribution is now CommonJS emitted by TypeScript, replacing the previous UMD wrapper. CommonJS `require` and the documented ESM imports remain supported. Direct AMD loading or browser script tags that depended on UMD globals must migrate to a browser bundler. Edit `src/*.ts`, then run `npm run build`; do not edit generated `dist` files. The obsolete Babel build dependencies have been removed.
|
|
352
231
|
|
|
353
|
-
|
|
232
|
+
### Verification and coverage
|
|
354
233
|
|
|
234
|
+
```sh
|
|
235
|
+
npm ci --ignore-scripts
|
|
236
|
+
npm run typecheck
|
|
237
|
+
npm test
|
|
238
|
+
npm run coverage
|
|
239
|
+
npm pack --dry-run
|
|
355
240
|
```
|
|
356
|
-
myRouter.navigate('/page2');
|
|
357
|
-
```
|
|
358
241
|
|
|
359
|
-
|
|
242
|
+
`npm test` builds the code, checks TypeScript consumer examples against the emitted declarations, and runs the tests. `npm run coverage` additionally enforces **100% statements, branches, functions, and lines for each implementation file**. Unexecuted implementation files count toward the result; declaration-only files contain no executable code and are excluded. Reports are written to `coverage`, including `lcov.info` for coverage viewers. CI runs the same gate and checks committed build output for drift.
|
|
243
|
+
|
|
244
|
+
Tests exercise the compiled JavaScript interface used by downstream callers. Coverage is an execution metric, not proof that all possible inputs or external integrations are correct.
|
|
360
245
|
|
|
361
|
-
|
|
246
|
+
To undo this migration, revert its commit and run `npm ci` from the restored lockfile. No npm release, database migration, or production deployment is performed by these development changes.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/** Navigation payload, including caller-defined metadata. */
|
|
2
|
+
interface NavigationData {
|
|
3
|
+
url?: string;
|
|
4
|
+
[key: string]: unknown;
|
|
5
|
+
}
|
|
6
|
+
/** URL-derived values supplied to route handlers. */
|
|
7
|
+
interface LocationData {
|
|
8
|
+
url: string;
|
|
9
|
+
data: {
|
|
10
|
+
url: string[];
|
|
11
|
+
mediator: NavigationData | undefined;
|
|
12
|
+
query: Record<string, string>;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
type RouteHandler = (scope: Element | null, location: LocationData) => unknown;
|
|
16
|
+
/** An object route can own a function or a view lifecycle. */
|
|
17
|
+
interface RouteObject {
|
|
18
|
+
title?: string;
|
|
19
|
+
secure?: RouteHandler;
|
|
20
|
+
view?: RouteHandler | {
|
|
21
|
+
initialize?: RouteHandler;
|
|
22
|
+
destroy?: RouteHandler;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
type Route = RouteHandler | RouteObject;
|
|
26
|
+
/** Minimal event-bus contract required by the router. */
|
|
27
|
+
interface NavigationMediator {
|
|
28
|
+
on(event: string, callback: (data?: NavigationData) => unknown): unknown;
|
|
29
|
+
removeListener?(event: string, callback: (data?: NavigationData) => unknown): unknown;
|
|
30
|
+
}
|
|
31
|
+
/** History API router with ordered path-boundary matching. */
|
|
32
|
+
declare class Router {
|
|
33
|
+
routes: Record<string, Route>;
|
|
34
|
+
url: string;
|
|
35
|
+
route: string | null;
|
|
36
|
+
scope: Element | null;
|
|
37
|
+
previousRoute: string | null;
|
|
38
|
+
pageTitle: string | null;
|
|
39
|
+
mediator: NavigationMediator | false;
|
|
40
|
+
boundPushStateClick: (event: MouseEvent) => unknown;
|
|
41
|
+
boundPopState: (event: PopStateEvent) => unknown;
|
|
42
|
+
boundMediatorNavigate: (data?: NavigationData) => unknown;
|
|
43
|
+
listenersInitialized: boolean;
|
|
44
|
+
locationData: LocationData;
|
|
45
|
+
/**
|
|
46
|
+
* Create an instance with its own state and listener references.
|
|
47
|
+
*/
|
|
48
|
+
constructor();
|
|
49
|
+
/**
|
|
50
|
+
* Start this instance and return it for lifecycle chaining.
|
|
51
|
+
* @returns This instance for chaining.
|
|
52
|
+
*/
|
|
53
|
+
initialize(): this;
|
|
54
|
+
/**
|
|
55
|
+
* Register stable document, history, and optional mediator handlers once.
|
|
56
|
+
* @returns This instance for chaining.
|
|
57
|
+
*/
|
|
58
|
+
addListeners(): this;
|
|
59
|
+
/**
|
|
60
|
+
* Release owned state and listeners so the instance can leave the application lifecycle.
|
|
61
|
+
* @returns This instance after cleanup.
|
|
62
|
+
*/
|
|
63
|
+
destroy(): this;
|
|
64
|
+
/**
|
|
65
|
+
* Release listeners owned by this instance; subclasses may extend the lifecycle hook.
|
|
66
|
+
* @returns This instance for chaining.
|
|
67
|
+
*/
|
|
68
|
+
removeListeners(): this;
|
|
69
|
+
/**
|
|
70
|
+
* Intercept eligible same-origin push-state links while preserving normal browser actions.
|
|
71
|
+
* @param e - Browser event being handled.
|
|
72
|
+
* @returns True when the browser should handle the event, or this router after interception.
|
|
73
|
+
*/
|
|
74
|
+
eventPushStateClick(e: MouseEvent): true | this;
|
|
75
|
+
/**
|
|
76
|
+
* Restore browser history state without adding a new history entry.
|
|
77
|
+
* @param e - Browser event being handled.
|
|
78
|
+
* @returns This router after restoring the history state.
|
|
79
|
+
*/
|
|
80
|
+
eventPopState(e: PopStateEvent): this;
|
|
81
|
+
/**
|
|
82
|
+
* Decode URL query parameters, keeping the last value for duplicate keys.
|
|
83
|
+
* @param queryString - Encoded URL query portion.
|
|
84
|
+
* @returns A record of decoded query names and their final values.
|
|
85
|
+
*/
|
|
86
|
+
parseQueryString(queryString: string): {
|
|
87
|
+
[k: string]: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Build decoded path, query, and mediator data for the selected route.
|
|
91
|
+
* @param mediatorData - Optional caller metadata passed to the route.
|
|
92
|
+
* @returns No value; locationData is updated in place.
|
|
93
|
+
*/
|
|
94
|
+
setLocationData(mediatorData?: NavigationData): void;
|
|
95
|
+
/**
|
|
96
|
+
* Select a route, enforce its guard, transition view lifecycles, and update history and title.
|
|
97
|
+
* @param url - URL or optional adapter argument.
|
|
98
|
+
* @param mediatorData - Optional caller metadata passed to the route.
|
|
99
|
+
* @param isPopState - Whether navigation came from browser history.
|
|
100
|
+
* @returns False when a guard or route rejects navigation; otherwise this router.
|
|
101
|
+
*/
|
|
102
|
+
navigate(url?: string | false, mediatorData?: NavigationData, isPopState?: boolean): false | this;
|
|
103
|
+
}
|
|
104
|
+
/** Public route configuration and navigation payload types. */
|
|
105
|
+
declare namespace Router {
|
|
106
|
+
type Handler = RouteHandler;
|
|
107
|
+
type Route = RouteHandler | RouteObject;
|
|
108
|
+
type Location = LocationData;
|
|
109
|
+
type Navigation = NavigationData;
|
|
110
|
+
}
|
|
111
|
+
export = Router;
|