@ktjs/router 0.14.7 → 0.14.9
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 +61 -23
- package/dist/index.d.ts +5 -8
- package/dist/index.iife.js +18 -134
- package/dist/index.legacy.js +32 -151
- package/dist/index.mjs +18 -134
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,28 +8,25 @@
|
|
|
8
8
|
|
|
9
9
|
Client-side router with navigation guards for KT.js.
|
|
10
10
|
|
|
11
|
-
**Current Version:** 0.
|
|
11
|
+
**Current Version:** 0.14.9
|
|
12
12
|
|
|
13
13
|
## Overview
|
|
14
14
|
|
|
15
|
-
`@ktjs/router` is a lightweight, hash-based client-side router with powerful navigation guards
|
|
15
|
+
`@ktjs/router` is a lightweight, hash-based client-side router with powerful async navigation guards. It provides all the essential routing features you need without the bloat. Simplified in v0.14.7+ to focus exclusively on hash-based routing for better performance and maintainability.
|
|
16
16
|
|
|
17
17
|
## Features
|
|
18
18
|
|
|
19
|
-
- **Hash-Based Routing**: Uses URL hash for client-side navigation (`#/path`)
|
|
19
|
+
- **Hash-Based Routing**: Uses URL hash for client-side navigation (`#/path`) - **hash-mode only** since v0.14.7
|
|
20
20
|
- **Path Matching**: Static and dynamic route matching with parameter extraction
|
|
21
21
|
- Dynamic segments: `/user/:id`
|
|
22
22
|
- Wildcard matching support
|
|
23
23
|
- Optimized matching algorithm with pre-flattened routes
|
|
24
|
-
- **Navigation Guards**: Control navigation flow with
|
|
24
|
+
- **Async Navigation Guards**: Control navigation flow with Promise-based guard system
|
|
25
25
|
- `beforeEach`: Global guard before every navigation
|
|
26
26
|
- `beforeEnter`: Per-route guard for specific routes
|
|
27
27
|
- `afterEach`: Global hook after successful navigation
|
|
28
28
|
- Guard-level control with bitwise operations for fine-grained execution
|
|
29
|
-
-
|
|
30
|
-
- Uses async guards when `Promise` is available
|
|
31
|
-
- Falls back to synchronous mode in older browsers
|
|
32
|
-
- No configuration needed - it just works
|
|
29
|
+
- All guards are async - return `Promise` or immediate values
|
|
33
30
|
- **Named Routes**: Navigate using route names instead of paths
|
|
34
31
|
- **Query Parameters**: Built-in query string parsing and handling
|
|
35
32
|
- **Route Context**: Access route information in handlers
|
|
@@ -87,7 +84,7 @@ const router = createRouter({
|
|
|
87
84
|
h1({}, `User Profile`),
|
|
88
85
|
div({}, `User ID: ${to.params.id}`),
|
|
89
86
|
div({}, `Query: ${JSON.stringify(to.query)}`),
|
|
90
|
-
])
|
|
87
|
+
]),
|
|
91
88
|
);
|
|
92
89
|
},
|
|
93
90
|
},
|
|
@@ -196,27 +193,61 @@ Creates and returns a router instance.
|
|
|
196
193
|
- `path` (string): Route path with optional dynamic segments (`:param`)
|
|
197
194
|
- `name` (string, optional): Route name for named navigation
|
|
198
195
|
- `meta` (object, optional): Metadata attached to the route
|
|
199
|
-
- `
|
|
196
|
+
- `component` (function): Function that returns HTMLElement or Promise<HTMLElement>
|
|
197
|
+
- `beforeEnter` (function, optional): Route-specific guard, receives `(to: RouteContext) => boolean | string | NavOptions | void | Promise<...>`
|
|
200
198
|
- `after` (function, optional): Route-specific hook after navigation
|
|
201
199
|
- `children` (array, optional): Nested child routes
|
|
202
|
-
- `beforeEach` (function, optional): Global guard before every navigation, receives `(to: RouteContext, from: RouteContext | null)
|
|
203
|
-
- `afterEach` (function, optional): Global hook after successful navigation, receives `(to: RouteContext, from: RouteContext | null)
|
|
204
|
-
- `onNotFound` (function, optional): Handler for 404 errors, receives `(path: string)`
|
|
205
|
-
- `onError` (function, optional): Error handler for navigation failures, receives `(error: Error
|
|
206
|
-
- `
|
|
200
|
+
- `beforeEach` (function, optional): Global guard before every navigation, receives `(to: RouteContext, from: RouteContext | null) => boolean | string | NavOptions | void | Promise<...>`
|
|
201
|
+
- `afterEach` (function, optional): Global hook after successful navigation, receives `(to: RouteContext, from: RouteContext | null) => void | Promise<void>`
|
|
202
|
+
- `onNotFound` (function, optional): Handler for 404 errors, receives `(path: string) => void`
|
|
203
|
+
- `onError` (function, optional): Error handler for navigation failures, receives `(error: Error) => void`
|
|
204
|
+
- `prefix` (string, optional): URL prefix for all routes (default: `''`)
|
|
207
205
|
|
|
208
206
|
**Router Instance Properties:**
|
|
209
207
|
|
|
210
208
|
- `current` (property): Current active route context (or `null`)
|
|
211
209
|
- `history` (property): Array of navigation history
|
|
210
|
+
- `routes` (property): Normalized route configurations
|
|
212
211
|
|
|
213
212
|
**Router Instance Methods:**
|
|
214
213
|
|
|
215
|
-
- `push(location)`: Navigate to a new location (string path or route object)
|
|
216
|
-
- `silentPush(location)`: Navigate without global guards (`beforeEach` guards)
|
|
214
|
+
- `push(location)`: Navigate to a new location (string path or route object with `name`/`params`)
|
|
217
215
|
- `replace(location)`: Replace current history entry
|
|
218
216
|
- `back()`: Navigate back in history
|
|
219
|
-
- `
|
|
217
|
+
- `listen()`: Start listening to hash changes (auto-called on router creation since v0.14.7)
|
|
218
|
+
- `init(routes)`: Initialize router with route configurations
|
|
219
|
+
|
|
220
|
+
### Navigation Guards (v0.14.7+)
|
|
221
|
+
|
|
222
|
+
All guards are **async** and can return:
|
|
223
|
+
|
|
224
|
+
- `false`: Cancel navigation
|
|
225
|
+
- `string`: Redirect to the given path
|
|
226
|
+
- `NavOptions` object: Redirect with options `{ name, params, query }`
|
|
227
|
+
- `void` or `undefined`: Continue navigation
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// Global guard
|
|
231
|
+
beforeEach: (async (to, from) => {
|
|
232
|
+
// Check authentication
|
|
233
|
+
const isAuthenticated = await checkAuth();
|
|
234
|
+
if (!isAuthenticated && to.path !== '/login') {
|
|
235
|
+
return '/login'; // Redirect to login
|
|
236
|
+
}
|
|
237
|
+
// Return nothing to continue
|
|
238
|
+
},
|
|
239
|
+
// Route guard
|
|
240
|
+
{
|
|
241
|
+
path: '/admin',
|
|
242
|
+
component: () => AdminPage(),
|
|
243
|
+
beforeEnter: async (to) => {
|
|
244
|
+
const hasPermission = await checkAdminPermission();
|
|
245
|
+
if (!hasPermission) {
|
|
246
|
+
return false; // Cancel navigation
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
```
|
|
220
251
|
|
|
221
252
|
### Route Context
|
|
222
253
|
|
|
@@ -231,15 +262,22 @@ Guards and hooks receive a `RouteContext` object with:
|
|
|
231
262
|
|
|
232
263
|
The router includes several performance optimizations:
|
|
233
264
|
|
|
234
|
-
- **
|
|
235
|
-
- **
|
|
236
|
-
- **
|
|
237
|
-
- **
|
|
265
|
+
- **Hash-Mode Only**: Simplified implementation focusing on hash-based routing (v0.14.7+)
|
|
266
|
+
- **Pre-flattened Routes**: Nested routes are flattened during initialization for faster lookups
|
|
267
|
+
- **Efficient Matching**: Optimized regex-based path matching with caching
|
|
268
|
+
- **Async Guards**: All guards use Promise-based architecture for consistent async handling
|
|
238
269
|
- **Guard Level Control**: Fine-grained control over guard execution using bitwise operations
|
|
270
|
+
- **Automatic Initialization**: Router auto-initializes on creation, no manual setup needed
|
|
239
271
|
|
|
240
272
|
## Browser Compatibility
|
|
241
273
|
|
|
242
|
-
Works in all modern browsers
|
|
274
|
+
Works in all modern browsers that support:
|
|
275
|
+
|
|
276
|
+
- Hash-based navigation
|
|
277
|
+
- ES5 (with transpilation)
|
|
278
|
+
- Promise API (required for async guards)
|
|
279
|
+
|
|
280
|
+
For older browsers without Promise support, include a Promise polyfill.
|
|
243
281
|
|
|
244
282
|
## License
|
|
245
283
|
|
package/dist/index.d.ts
CHANGED
|
@@ -99,7 +99,10 @@ interface NavOptions extends NavBaseOptions {
|
|
|
99
99
|
* Router configuration
|
|
100
100
|
*/
|
|
101
101
|
interface RouterConfig {
|
|
102
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Might not be needed while using hash routing
|
|
104
|
+
*/
|
|
105
|
+
prefix?: string;
|
|
103
106
|
|
|
104
107
|
/** Array of route definitions */
|
|
105
108
|
routes: RawRouteConfig[];
|
|
@@ -118,12 +121,6 @@ interface RouterConfig {
|
|
|
118
121
|
|
|
119
122
|
/** Handler for routing errors */
|
|
120
123
|
onError?: (error: Error, route?: RouteConfig) => void;
|
|
121
|
-
|
|
122
|
-
// # options
|
|
123
|
-
/**
|
|
124
|
-
* Default is `true`
|
|
125
|
-
*/
|
|
126
|
-
asyncGuards?: boolean;
|
|
127
124
|
}
|
|
128
125
|
|
|
129
126
|
/**
|
|
@@ -171,7 +168,7 @@ declare function KTRouter({ router }: {
|
|
|
171
168
|
/**
|
|
172
169
|
* Create a new router instance
|
|
173
170
|
*/
|
|
174
|
-
declare const createRouter: (
|
|
171
|
+
declare const createRouter: ({ beforeEach, afterEach, onNotFound, onError, prefix, }: RouterConfig) => Router;
|
|
175
172
|
|
|
176
173
|
export { GuardLevel, KTRouter, createRouter };
|
|
177
174
|
export type { NavOptions, RawRouteConfig, RouteConfig, RouteContext, RouteMatch, Router, RouterConfig };
|
package/dist/index.iife.js
CHANGED
|
@@ -4,7 +4,7 @@ var __ktjs_router__ = (function (exports) {
|
|
|
4
4
|
/**
|
|
5
5
|
* Default guard that always returns true
|
|
6
6
|
*/
|
|
7
|
-
const
|
|
7
|
+
const fn = (() => true);
|
|
8
8
|
const throws = (m) => {
|
|
9
9
|
throw new Error(`@ktjs/router: ${m}`);
|
|
10
10
|
};
|
|
@@ -162,14 +162,7 @@ var __ktjs_router__ = (function (exports) {
|
|
|
162
162
|
/**
|
|
163
163
|
* Create a new router instance
|
|
164
164
|
*/
|
|
165
|
-
const createRouter = (
|
|
166
|
-
// # default configs
|
|
167
|
-
const beforeEach = config.beforeEach ?? defaultHook;
|
|
168
|
-
const afterEach = config.afterEach ?? defaultHook;
|
|
169
|
-
const onNotFound = config.onNotFound ?? defaultHook;
|
|
170
|
-
const onError = config.onError ?? defaultHook;
|
|
171
|
-
const asyncGuards = config.asyncGuards ?? true;
|
|
172
|
-
const baseUrl = config.baseUrl ?? '';
|
|
165
|
+
const createRouter = ({ beforeEach = fn, afterEach = fn, onNotFound = fn, onError = fn, prefix = '', }) => {
|
|
173
166
|
// # private values
|
|
174
167
|
const routes = [];
|
|
175
168
|
const history = [];
|
|
@@ -179,11 +172,11 @@ var __ktjs_router__ = (function (exports) {
|
|
|
179
172
|
const normalize = (rawRoutes, parentPath) => rawRoutes.map((route) => {
|
|
180
173
|
const path = normalizePath(parentPath, route.path);
|
|
181
174
|
const normalized = {
|
|
182
|
-
path:
|
|
175
|
+
path: prefix + path,
|
|
183
176
|
name: route.name ?? '',
|
|
184
177
|
meta: route.meta ?? {},
|
|
185
|
-
beforeEnter: route.beforeEnter ??
|
|
186
|
-
after: route.after ??
|
|
178
|
+
beforeEnter: route.beforeEnter ?? fn,
|
|
179
|
+
after: route.after ?? fn,
|
|
187
180
|
children: route.children ? normalize(route.children, path) : [],
|
|
188
181
|
component: route.component,
|
|
189
182
|
};
|
|
@@ -192,44 +185,7 @@ var __ktjs_router__ = (function (exports) {
|
|
|
192
185
|
routes.push(normalized);
|
|
193
186
|
return normalized;
|
|
194
187
|
});
|
|
195
|
-
const
|
|
196
|
-
try {
|
|
197
|
-
if (guardLevel === 0 /* GuardLevel.None */) {
|
|
198
|
-
return { continue: true };
|
|
199
|
-
}
|
|
200
|
-
if (guardLevel & 1 /* GuardLevel.Global */) {
|
|
201
|
-
const result = beforeEach(to, from);
|
|
202
|
-
if (result === false) {
|
|
203
|
-
return { continue: false };
|
|
204
|
-
}
|
|
205
|
-
if (typeof result === 'string') {
|
|
206
|
-
return { continue: false, redirectTo: { path: result } };
|
|
207
|
-
}
|
|
208
|
-
if (result && typeof result === 'object' && !('then' in result)) {
|
|
209
|
-
return { continue: false, redirectTo: result };
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (guardLevel & 2 /* GuardLevel.Route */) {
|
|
213
|
-
const targetRoute = to.matched[to.matched.length - 1];
|
|
214
|
-
const result = targetRoute.beforeEnter(to);
|
|
215
|
-
if (result === false) {
|
|
216
|
-
return { continue: false };
|
|
217
|
-
}
|
|
218
|
-
if (typeof result === 'string') {
|
|
219
|
-
return { continue: false, redirectTo: { path: result } };
|
|
220
|
-
}
|
|
221
|
-
if (result && typeof result === 'object' && !('then' in result)) {
|
|
222
|
-
return { continue: false, redirectTo: result };
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return { continue: true };
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
onError(error);
|
|
229
|
-
return { continue: false };
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
const executeGuards = async (to, from, guardLevel) => {
|
|
188
|
+
const guard = async (to, from, guardLevel) => {
|
|
233
189
|
try {
|
|
234
190
|
if (guardLevel === 0 /* GuardLevel.None */) {
|
|
235
191
|
return { continue: true };
|
|
@@ -312,76 +268,7 @@ var __ktjs_router__ = (function (exports) {
|
|
|
312
268
|
fullPath,
|
|
313
269
|
};
|
|
314
270
|
};
|
|
315
|
-
const
|
|
316
|
-
try {
|
|
317
|
-
// Prevent infinite redirect loop
|
|
318
|
-
if (redirectCount > 10) {
|
|
319
|
-
onError(new Error('Maximum redirect count exceeded'));
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
|
-
const prep = navigatePrepare(options);
|
|
323
|
-
if (!prep) {
|
|
324
|
-
return false;
|
|
325
|
-
}
|
|
326
|
-
const { guardLevel, replace, to, fullPath } = prep;
|
|
327
|
-
// Execute guards
|
|
328
|
-
const guardResult = executeGuardsSync(to, current, guardLevel);
|
|
329
|
-
if (!guardResult.continue) {
|
|
330
|
-
// Check if there's a redirect
|
|
331
|
-
if (guardResult.redirectTo) {
|
|
332
|
-
return navigateSync(guardResult.redirectTo, redirectCount + 1);
|
|
333
|
-
}
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
// Update browser history depending on mode
|
|
337
|
-
const url = fullPath;
|
|
338
|
-
const hashUrl = '#' + fullPath;
|
|
339
|
-
if (replace) {
|
|
340
|
-
window.location.replace(hashUrl);
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
window.location.hash = fullPath;
|
|
344
|
-
}
|
|
345
|
-
// Update current route in memory
|
|
346
|
-
current = to;
|
|
347
|
-
if (replace) {
|
|
348
|
-
if (history.length > 0) {
|
|
349
|
-
history[history.length - 1] = to;
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
history.push(to);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
history.push(to);
|
|
357
|
-
}
|
|
358
|
-
// Render component if routerView exists
|
|
359
|
-
if (routerView && to.matched.length > 0) {
|
|
360
|
-
const route = to.matched[to.matched.length - 1];
|
|
361
|
-
if (route.component) {
|
|
362
|
-
const element = route.component();
|
|
363
|
-
if (element instanceof Promise) {
|
|
364
|
-
element.then((el) => {
|
|
365
|
-
routerView.innerHTML = '';
|
|
366
|
-
routerView.appendChild(el);
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
routerView.innerHTML = '';
|
|
371
|
-
routerView.appendChild(element);
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
// Execute after hooks
|
|
376
|
-
executeAfterHooksSync(to, history[history.length - 2] ?? null);
|
|
377
|
-
return true;
|
|
378
|
-
}
|
|
379
|
-
catch (error) {
|
|
380
|
-
onError(error);
|
|
381
|
-
return false;
|
|
382
|
-
}
|
|
383
|
-
};
|
|
384
|
-
const navigateAsync = async (options, redirectCount = 0) => {
|
|
271
|
+
const navigate = async (options, redirectCount = 0) => {
|
|
385
272
|
try {
|
|
386
273
|
// Prevent infinite redirect loop
|
|
387
274
|
if (redirectCount > 10) {
|
|
@@ -393,11 +280,11 @@ var __ktjs_router__ = (function (exports) {
|
|
|
393
280
|
return false;
|
|
394
281
|
}
|
|
395
282
|
const { guardLevel, replace, to, fullPath } = prep;
|
|
396
|
-
const guardResult = await
|
|
283
|
+
const guardResult = await guard(to, current, guardLevel);
|
|
397
284
|
if (!guardResult.continue) {
|
|
398
285
|
// Check if there's a redirect
|
|
399
286
|
if (guardResult.redirectTo) {
|
|
400
|
-
return
|
|
287
|
+
return await navigate(guardResult.redirectTo, redirectCount + 1);
|
|
401
288
|
}
|
|
402
289
|
return false;
|
|
403
290
|
}
|
|
@@ -438,12 +325,6 @@ var __ktjs_router__ = (function (exports) {
|
|
|
438
325
|
return false;
|
|
439
326
|
}
|
|
440
327
|
};
|
|
441
|
-
const navigate = asyncGuards ? navigateSync : navigateAsync;
|
|
442
|
-
const executeAfterHooksSync = (to, from) => {
|
|
443
|
-
const targetRoute = to.matched[to.matched.length - 1];
|
|
444
|
-
targetRoute.after(to);
|
|
445
|
-
afterEach(to, from);
|
|
446
|
-
};
|
|
447
328
|
const executeAfterHooks = async (to, from) => {
|
|
448
329
|
const targetRoute = to.matched[to.matched.length - 1];
|
|
449
330
|
await targetRoute.after(to);
|
|
@@ -463,7 +344,6 @@ var __ktjs_router__ = (function (exports) {
|
|
|
463
344
|
};
|
|
464
345
|
};
|
|
465
346
|
// # register events
|
|
466
|
-
// hash mode: listen to hashchange
|
|
467
347
|
window.addEventListener('hashchange', () => {
|
|
468
348
|
const hash = window.location.hash.slice(1);
|
|
469
349
|
const [path] = hash.split('?');
|
|
@@ -505,13 +385,10 @@ var __ktjs_router__ = (function (exports) {
|
|
|
505
385
|
}
|
|
506
386
|
}
|
|
507
387
|
}
|
|
508
|
-
|
|
388
|
+
executeAfterHooks(to, history[history.length - 2] ?? null);
|
|
509
389
|
});
|
|
510
390
|
// # initialize
|
|
511
|
-
|
|
512
|
-
const { findByName, match } = createMatcher(routes);
|
|
513
|
-
// Router instance
|
|
514
|
-
return {
|
|
391
|
+
const instance = {
|
|
515
392
|
get current() {
|
|
516
393
|
return current;
|
|
517
394
|
},
|
|
@@ -540,6 +417,13 @@ var __ktjs_router__ = (function (exports) {
|
|
|
540
417
|
window.history.forward();
|
|
541
418
|
},
|
|
542
419
|
};
|
|
420
|
+
normalize(routes, '/');
|
|
421
|
+
const { findByName, match } = createMatcher(routes);
|
|
422
|
+
const currentHash = window.location.hash.slice(1);
|
|
423
|
+
if (currentHash) {
|
|
424
|
+
instance.push(currentHash);
|
|
425
|
+
}
|
|
426
|
+
return instance;
|
|
543
427
|
};
|
|
544
428
|
|
|
545
429
|
exports.KTRouter = KTRouter;
|
package/dist/index.legacy.js
CHANGED
|
@@ -85,7 +85,7 @@ var __ktjs_router__ = (function (exports) {
|
|
|
85
85
|
/**
|
|
86
86
|
* Default guard that always returns true
|
|
87
87
|
*/
|
|
88
|
-
var
|
|
88
|
+
var fn = (function () { return true; });
|
|
89
89
|
var throws = function (m) {
|
|
90
90
|
throw new Error("@ktjs/router: ".concat(m));
|
|
91
91
|
};
|
|
@@ -252,15 +252,8 @@ var __ktjs_router__ = (function (exports) {
|
|
|
252
252
|
/**
|
|
253
253
|
* Create a new router instance
|
|
254
254
|
*/
|
|
255
|
-
var createRouter = function (
|
|
256
|
-
var _a, _b, _c, _d, _e, _f;
|
|
257
|
-
// # default configs
|
|
258
|
-
var beforeEach = (_a = config.beforeEach) !== null && _a !== void 0 ? _a : defaultHook;
|
|
259
|
-
var afterEach = (_b = config.afterEach) !== null && _b !== void 0 ? _b : defaultHook;
|
|
260
|
-
var onNotFound = (_c = config.onNotFound) !== null && _c !== void 0 ? _c : defaultHook;
|
|
261
|
-
var onError = (_d = config.onError) !== null && _d !== void 0 ? _d : defaultHook;
|
|
262
|
-
var asyncGuards = (_e = config.asyncGuards) !== null && _e !== void 0 ? _e : true;
|
|
263
|
-
var baseUrl = (_f = config.baseUrl) !== null && _f !== void 0 ? _f : '';
|
|
255
|
+
var createRouter = function (_a) {
|
|
256
|
+
var _b = _a.beforeEach, beforeEach = _b === void 0 ? fn : _b, _c = _a.afterEach, afterEach = _c === void 0 ? fn : _c, _d = _a.onNotFound, onNotFound = _d === void 0 ? fn : _d, _e = _a.onError, onError = _e === void 0 ? fn : _e, _f = _a.prefix, prefix = _f === void 0 ? '' : _f;
|
|
264
257
|
// # private values
|
|
265
258
|
var routes = [];
|
|
266
259
|
var history = [];
|
|
@@ -272,11 +265,11 @@ var __ktjs_router__ = (function (exports) {
|
|
|
272
265
|
var _a, _b, _c, _d;
|
|
273
266
|
var path = normalizePath(parentPath, route.path);
|
|
274
267
|
var normalized = {
|
|
275
|
-
path:
|
|
268
|
+
path: prefix + path,
|
|
276
269
|
name: (_a = route.name) !== null && _a !== void 0 ? _a : '',
|
|
277
270
|
meta: (_b = route.meta) !== null && _b !== void 0 ? _b : {},
|
|
278
|
-
beforeEnter: (_c = route.beforeEnter) !== null && _c !== void 0 ? _c :
|
|
279
|
-
after: (_d = route.after) !== null && _d !== void 0 ? _d :
|
|
271
|
+
beforeEnter: (_c = route.beforeEnter) !== null && _c !== void 0 ? _c : fn,
|
|
272
|
+
after: (_d = route.after) !== null && _d !== void 0 ? _d : fn,
|
|
280
273
|
children: route.children ? normalize(route.children, path) : [],
|
|
281
274
|
component: route.component,
|
|
282
275
|
};
|
|
@@ -286,44 +279,7 @@ var __ktjs_router__ = (function (exports) {
|
|
|
286
279
|
return normalized;
|
|
287
280
|
});
|
|
288
281
|
};
|
|
289
|
-
var
|
|
290
|
-
try {
|
|
291
|
-
if (guardLevel === 0 /* GuardLevel.None */) {
|
|
292
|
-
return { continue: true };
|
|
293
|
-
}
|
|
294
|
-
if (guardLevel & 1 /* GuardLevel.Global */) {
|
|
295
|
-
var result = beforeEach(to, from);
|
|
296
|
-
if (result === false) {
|
|
297
|
-
return { continue: false };
|
|
298
|
-
}
|
|
299
|
-
if (typeof result === 'string') {
|
|
300
|
-
return { continue: false, redirectTo: { path: result } };
|
|
301
|
-
}
|
|
302
|
-
if (result && typeof result === 'object' && !('then' in result)) {
|
|
303
|
-
return { continue: false, redirectTo: result };
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (guardLevel & 2 /* GuardLevel.Route */) {
|
|
307
|
-
var targetRoute = to.matched[to.matched.length - 1];
|
|
308
|
-
var result = targetRoute.beforeEnter(to);
|
|
309
|
-
if (result === false) {
|
|
310
|
-
return { continue: false };
|
|
311
|
-
}
|
|
312
|
-
if (typeof result === 'string') {
|
|
313
|
-
return { continue: false, redirectTo: { path: result } };
|
|
314
|
-
}
|
|
315
|
-
if (result && typeof result === 'object' && !('then' in result)) {
|
|
316
|
-
return { continue: false, redirectTo: result };
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return { continue: true };
|
|
320
|
-
}
|
|
321
|
-
catch (error) {
|
|
322
|
-
onError(error);
|
|
323
|
-
return { continue: false };
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
var executeGuards = function (to, from, guardLevel) { return __awaiter(void 0, void 0, void 0, function () {
|
|
282
|
+
var guard = function (to, from, guardLevel) { return __awaiter(void 0, void 0, void 0, function () {
|
|
327
283
|
var result, targetRoute, result, error_1;
|
|
328
284
|
return __generator(this, function (_a) {
|
|
329
285
|
switch (_a.label) {
|
|
@@ -418,78 +374,7 @@ var __ktjs_router__ = (function (exports) {
|
|
|
418
374
|
fullPath: fullPath,
|
|
419
375
|
};
|
|
420
376
|
};
|
|
421
|
-
var
|
|
422
|
-
var _a;
|
|
423
|
-
if (redirectCount === void 0) { redirectCount = 0; }
|
|
424
|
-
try {
|
|
425
|
-
// Prevent infinite redirect loop
|
|
426
|
-
if (redirectCount > 10) {
|
|
427
|
-
onError(new Error('Maximum redirect count exceeded'));
|
|
428
|
-
return false;
|
|
429
|
-
}
|
|
430
|
-
var prep = navigatePrepare(options);
|
|
431
|
-
if (!prep) {
|
|
432
|
-
return false;
|
|
433
|
-
}
|
|
434
|
-
var guardLevel = prep.guardLevel, replace = prep.replace, to = prep.to, fullPath = prep.fullPath;
|
|
435
|
-
// Execute guards
|
|
436
|
-
var guardResult = executeGuardsSync(to, current, guardLevel);
|
|
437
|
-
if (!guardResult.continue) {
|
|
438
|
-
// Check if there's a redirect
|
|
439
|
-
if (guardResult.redirectTo) {
|
|
440
|
-
return navigateSync(guardResult.redirectTo, redirectCount + 1);
|
|
441
|
-
}
|
|
442
|
-
return false;
|
|
443
|
-
}
|
|
444
|
-
// Update browser history depending on mode
|
|
445
|
-
var url = fullPath;
|
|
446
|
-
var hashUrl = '#' + fullPath;
|
|
447
|
-
if (replace) {
|
|
448
|
-
window.location.replace(hashUrl);
|
|
449
|
-
}
|
|
450
|
-
else {
|
|
451
|
-
window.location.hash = fullPath;
|
|
452
|
-
}
|
|
453
|
-
// Update current route in memory
|
|
454
|
-
current = to;
|
|
455
|
-
if (replace) {
|
|
456
|
-
if (history.length > 0) {
|
|
457
|
-
history[history.length - 1] = to;
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
history.push(to);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
else {
|
|
464
|
-
history.push(to);
|
|
465
|
-
}
|
|
466
|
-
// Render component if routerView exists
|
|
467
|
-
if (routerView && to.matched.length > 0) {
|
|
468
|
-
var route = to.matched[to.matched.length - 1];
|
|
469
|
-
if (route.component) {
|
|
470
|
-
var element = route.component();
|
|
471
|
-
if (element instanceof Promise) {
|
|
472
|
-
element.then(function (el) {
|
|
473
|
-
routerView.innerHTML = '';
|
|
474
|
-
routerView.appendChild(el);
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
routerView.innerHTML = '';
|
|
479
|
-
routerView.appendChild(element);
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
// Execute after hooks
|
|
484
|
-
executeAfterHooksSync(to, (_a = history[history.length - 2]) !== null && _a !== void 0 ? _a : null);
|
|
485
|
-
return true;
|
|
486
|
-
}
|
|
487
|
-
catch (error) {
|
|
488
|
-
onError(error);
|
|
489
|
-
return false;
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
var navigateAsync = function (options_1) {
|
|
377
|
+
var navigate = function (options_1) {
|
|
493
378
|
var args_1 = [];
|
|
494
379
|
for (var _i = 1; _i < arguments.length; _i++) {
|
|
495
380
|
args_1[_i - 1] = arguments[_i];
|
|
@@ -501,7 +386,7 @@ var __ktjs_router__ = (function (exports) {
|
|
|
501
386
|
return __generator(this, function (_b) {
|
|
502
387
|
switch (_b.label) {
|
|
503
388
|
case 0:
|
|
504
|
-
_b.trys.push([0,
|
|
389
|
+
_b.trys.push([0, 7, , 8]);
|
|
505
390
|
// Prevent infinite redirect loop
|
|
506
391
|
if (redirectCount > 10) {
|
|
507
392
|
onError(new Error('Maximum redirect count exceeded'));
|
|
@@ -512,16 +397,15 @@ var __ktjs_router__ = (function (exports) {
|
|
|
512
397
|
return [2 /*return*/, false];
|
|
513
398
|
}
|
|
514
399
|
guardLevel = prep.guardLevel, replace = prep.replace, to = prep.to, fullPath = prep.fullPath;
|
|
515
|
-
return [4 /*yield*/,
|
|
400
|
+
return [4 /*yield*/, guard(to, current, guardLevel)];
|
|
516
401
|
case 1:
|
|
517
402
|
guardResult = _b.sent();
|
|
518
|
-
if (
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
403
|
+
if (!!guardResult.continue) return [3 /*break*/, 4];
|
|
404
|
+
if (!guardResult.redirectTo) return [3 /*break*/, 3];
|
|
405
|
+
return [4 /*yield*/, navigate(guardResult.redirectTo, redirectCount + 1)];
|
|
406
|
+
case 2: return [2 /*return*/, _b.sent()];
|
|
407
|
+
case 3: return [2 /*return*/, false];
|
|
408
|
+
case 4:
|
|
525
409
|
hashUrl = '#' + fullPath;
|
|
526
410
|
if (replace) {
|
|
527
411
|
window.location.replace(hashUrl);
|
|
@@ -541,33 +425,27 @@ var __ktjs_router__ = (function (exports) {
|
|
|
541
425
|
else {
|
|
542
426
|
history.push(to);
|
|
543
427
|
}
|
|
544
|
-
if (!(routerView && to.matched.length > 0)) return [3 /*break*/,
|
|
428
|
+
if (!(routerView && to.matched.length > 0)) return [3 /*break*/, 6];
|
|
545
429
|
route = to.matched[to.matched.length - 1];
|
|
546
|
-
if (!route.component) return [3 /*break*/,
|
|
430
|
+
if (!route.component) return [3 /*break*/, 6];
|
|
547
431
|
return [4 /*yield*/, route.component()];
|
|
548
|
-
case
|
|
432
|
+
case 5:
|
|
549
433
|
element = _b.sent();
|
|
550
434
|
routerView.innerHTML = '';
|
|
551
435
|
routerView.appendChild(element);
|
|
552
|
-
_b.label =
|
|
553
|
-
case
|
|
436
|
+
_b.label = 6;
|
|
437
|
+
case 6:
|
|
554
438
|
executeAfterHooks(to, (_a = history[history.length - 2]) !== null && _a !== void 0 ? _a : null);
|
|
555
439
|
return [2 /*return*/, true];
|
|
556
|
-
case
|
|
440
|
+
case 7:
|
|
557
441
|
error_2 = _b.sent();
|
|
558
442
|
onError(error_2);
|
|
559
443
|
return [2 /*return*/, false];
|
|
560
|
-
case
|
|
444
|
+
case 8: return [2 /*return*/];
|
|
561
445
|
}
|
|
562
446
|
});
|
|
563
447
|
});
|
|
564
448
|
};
|
|
565
|
-
var navigate = asyncGuards ? navigateSync : navigateAsync;
|
|
566
|
-
var executeAfterHooksSync = function (to, from) {
|
|
567
|
-
var targetRoute = to.matched[to.matched.length - 1];
|
|
568
|
-
targetRoute.after(to);
|
|
569
|
-
afterEach(to, from);
|
|
570
|
-
};
|
|
571
449
|
var executeAfterHooks = function (to, from) { return __awaiter(void 0, void 0, void 0, function () {
|
|
572
450
|
var targetRoute;
|
|
573
451
|
return __generator(this, function (_a) {
|
|
@@ -598,7 +476,6 @@ var __ktjs_router__ = (function (exports) {
|
|
|
598
476
|
};
|
|
599
477
|
};
|
|
600
478
|
// # register events
|
|
601
|
-
// hash mode: listen to hashchange
|
|
602
479
|
window.addEventListener('hashchange', function () {
|
|
603
480
|
var _a, _b;
|
|
604
481
|
var hash = window.location.hash.slice(1);
|
|
@@ -641,13 +518,10 @@ var __ktjs_router__ = (function (exports) {
|
|
|
641
518
|
}
|
|
642
519
|
}
|
|
643
520
|
}
|
|
644
|
-
|
|
521
|
+
executeAfterHooks(to, (_b = history[history.length - 2]) !== null && _b !== void 0 ? _b : null);
|
|
645
522
|
});
|
|
646
523
|
// # initialize
|
|
647
|
-
|
|
648
|
-
var _g = createMatcher(routes), findByName = _g.findByName, match = _g.match;
|
|
649
|
-
// Router instance
|
|
650
|
-
return {
|
|
524
|
+
var instance = {
|
|
651
525
|
get current() {
|
|
652
526
|
return current;
|
|
653
527
|
},
|
|
@@ -676,6 +550,13 @@ var __ktjs_router__ = (function (exports) {
|
|
|
676
550
|
window.history.forward();
|
|
677
551
|
},
|
|
678
552
|
};
|
|
553
|
+
normalize(routes, '/');
|
|
554
|
+
var _g = createMatcher(routes), findByName = _g.findByName, match = _g.match;
|
|
555
|
+
var currentHash = window.location.hash.slice(1);
|
|
556
|
+
if (currentHash) {
|
|
557
|
+
instance.push(currentHash);
|
|
558
|
+
}
|
|
559
|
+
return instance;
|
|
679
560
|
};
|
|
680
561
|
|
|
681
562
|
exports.KTRouter = KTRouter;
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Default guard that always returns true
|
|
3
3
|
*/
|
|
4
|
-
const
|
|
4
|
+
const fn = (() => true);
|
|
5
5
|
const throws = (m) => {
|
|
6
6
|
throw new Error(`@ktjs/router: ${m}`);
|
|
7
7
|
};
|
|
@@ -159,14 +159,7 @@ function KTRouter({ router }) {
|
|
|
159
159
|
/**
|
|
160
160
|
* Create a new router instance
|
|
161
161
|
*/
|
|
162
|
-
const createRouter = (
|
|
163
|
-
// # default configs
|
|
164
|
-
const beforeEach = config.beforeEach ?? defaultHook;
|
|
165
|
-
const afterEach = config.afterEach ?? defaultHook;
|
|
166
|
-
const onNotFound = config.onNotFound ?? defaultHook;
|
|
167
|
-
const onError = config.onError ?? defaultHook;
|
|
168
|
-
const asyncGuards = config.asyncGuards ?? true;
|
|
169
|
-
const baseUrl = config.baseUrl ?? '';
|
|
162
|
+
const createRouter = ({ beforeEach = fn, afterEach = fn, onNotFound = fn, onError = fn, prefix = '', }) => {
|
|
170
163
|
// # private values
|
|
171
164
|
const routes = [];
|
|
172
165
|
const history = [];
|
|
@@ -176,11 +169,11 @@ const createRouter = (config) => {
|
|
|
176
169
|
const normalize = (rawRoutes, parentPath) => rawRoutes.map((route) => {
|
|
177
170
|
const path = normalizePath(parentPath, route.path);
|
|
178
171
|
const normalized = {
|
|
179
|
-
path:
|
|
172
|
+
path: prefix + path,
|
|
180
173
|
name: route.name ?? '',
|
|
181
174
|
meta: route.meta ?? {},
|
|
182
|
-
beforeEnter: route.beforeEnter ??
|
|
183
|
-
after: route.after ??
|
|
175
|
+
beforeEnter: route.beforeEnter ?? fn,
|
|
176
|
+
after: route.after ?? fn,
|
|
184
177
|
children: route.children ? normalize(route.children, path) : [],
|
|
185
178
|
component: route.component,
|
|
186
179
|
};
|
|
@@ -189,44 +182,7 @@ const createRouter = (config) => {
|
|
|
189
182
|
routes.push(normalized);
|
|
190
183
|
return normalized;
|
|
191
184
|
});
|
|
192
|
-
const
|
|
193
|
-
try {
|
|
194
|
-
if (guardLevel === 0 /* GuardLevel.None */) {
|
|
195
|
-
return { continue: true };
|
|
196
|
-
}
|
|
197
|
-
if (guardLevel & 1 /* GuardLevel.Global */) {
|
|
198
|
-
const result = beforeEach(to, from);
|
|
199
|
-
if (result === false) {
|
|
200
|
-
return { continue: false };
|
|
201
|
-
}
|
|
202
|
-
if (typeof result === 'string') {
|
|
203
|
-
return { continue: false, redirectTo: { path: result } };
|
|
204
|
-
}
|
|
205
|
-
if (result && typeof result === 'object' && !('then' in result)) {
|
|
206
|
-
return { continue: false, redirectTo: result };
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
if (guardLevel & 2 /* GuardLevel.Route */) {
|
|
210
|
-
const targetRoute = to.matched[to.matched.length - 1];
|
|
211
|
-
const result = targetRoute.beforeEnter(to);
|
|
212
|
-
if (result === false) {
|
|
213
|
-
return { continue: false };
|
|
214
|
-
}
|
|
215
|
-
if (typeof result === 'string') {
|
|
216
|
-
return { continue: false, redirectTo: { path: result } };
|
|
217
|
-
}
|
|
218
|
-
if (result && typeof result === 'object' && !('then' in result)) {
|
|
219
|
-
return { continue: false, redirectTo: result };
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return { continue: true };
|
|
223
|
-
}
|
|
224
|
-
catch (error) {
|
|
225
|
-
onError(error);
|
|
226
|
-
return { continue: false };
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
const executeGuards = async (to, from, guardLevel) => {
|
|
185
|
+
const guard = async (to, from, guardLevel) => {
|
|
230
186
|
try {
|
|
231
187
|
if (guardLevel === 0 /* GuardLevel.None */) {
|
|
232
188
|
return { continue: true };
|
|
@@ -309,76 +265,7 @@ const createRouter = (config) => {
|
|
|
309
265
|
fullPath,
|
|
310
266
|
};
|
|
311
267
|
};
|
|
312
|
-
const
|
|
313
|
-
try {
|
|
314
|
-
// Prevent infinite redirect loop
|
|
315
|
-
if (redirectCount > 10) {
|
|
316
|
-
onError(new Error('Maximum redirect count exceeded'));
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
const prep = navigatePrepare(options);
|
|
320
|
-
if (!prep) {
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
const { guardLevel, replace, to, fullPath } = prep;
|
|
324
|
-
// Execute guards
|
|
325
|
-
const guardResult = executeGuardsSync(to, current, guardLevel);
|
|
326
|
-
if (!guardResult.continue) {
|
|
327
|
-
// Check if there's a redirect
|
|
328
|
-
if (guardResult.redirectTo) {
|
|
329
|
-
return navigateSync(guardResult.redirectTo, redirectCount + 1);
|
|
330
|
-
}
|
|
331
|
-
return false;
|
|
332
|
-
}
|
|
333
|
-
// Update browser history depending on mode
|
|
334
|
-
const url = fullPath;
|
|
335
|
-
const hashUrl = '#' + fullPath;
|
|
336
|
-
if (replace) {
|
|
337
|
-
window.location.replace(hashUrl);
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
window.location.hash = fullPath;
|
|
341
|
-
}
|
|
342
|
-
// Update current route in memory
|
|
343
|
-
current = to;
|
|
344
|
-
if (replace) {
|
|
345
|
-
if (history.length > 0) {
|
|
346
|
-
history[history.length - 1] = to;
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
history.push(to);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
history.push(to);
|
|
354
|
-
}
|
|
355
|
-
// Render component if routerView exists
|
|
356
|
-
if (routerView && to.matched.length > 0) {
|
|
357
|
-
const route = to.matched[to.matched.length - 1];
|
|
358
|
-
if (route.component) {
|
|
359
|
-
const element = route.component();
|
|
360
|
-
if (element instanceof Promise) {
|
|
361
|
-
element.then((el) => {
|
|
362
|
-
routerView.innerHTML = '';
|
|
363
|
-
routerView.appendChild(el);
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
routerView.innerHTML = '';
|
|
368
|
-
routerView.appendChild(element);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
// Execute after hooks
|
|
373
|
-
executeAfterHooksSync(to, history[history.length - 2] ?? null);
|
|
374
|
-
return true;
|
|
375
|
-
}
|
|
376
|
-
catch (error) {
|
|
377
|
-
onError(error);
|
|
378
|
-
return false;
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
const navigateAsync = async (options, redirectCount = 0) => {
|
|
268
|
+
const navigate = async (options, redirectCount = 0) => {
|
|
382
269
|
try {
|
|
383
270
|
// Prevent infinite redirect loop
|
|
384
271
|
if (redirectCount > 10) {
|
|
@@ -390,11 +277,11 @@ const createRouter = (config) => {
|
|
|
390
277
|
return false;
|
|
391
278
|
}
|
|
392
279
|
const { guardLevel, replace, to, fullPath } = prep;
|
|
393
|
-
const guardResult = await
|
|
280
|
+
const guardResult = await guard(to, current, guardLevel);
|
|
394
281
|
if (!guardResult.continue) {
|
|
395
282
|
// Check if there's a redirect
|
|
396
283
|
if (guardResult.redirectTo) {
|
|
397
|
-
return
|
|
284
|
+
return await navigate(guardResult.redirectTo, redirectCount + 1);
|
|
398
285
|
}
|
|
399
286
|
return false;
|
|
400
287
|
}
|
|
@@ -435,12 +322,6 @@ const createRouter = (config) => {
|
|
|
435
322
|
return false;
|
|
436
323
|
}
|
|
437
324
|
};
|
|
438
|
-
const navigate = asyncGuards ? navigateSync : navigateAsync;
|
|
439
|
-
const executeAfterHooksSync = (to, from) => {
|
|
440
|
-
const targetRoute = to.matched[to.matched.length - 1];
|
|
441
|
-
targetRoute.after(to);
|
|
442
|
-
afterEach(to, from);
|
|
443
|
-
};
|
|
444
325
|
const executeAfterHooks = async (to, from) => {
|
|
445
326
|
const targetRoute = to.matched[to.matched.length - 1];
|
|
446
327
|
await targetRoute.after(to);
|
|
@@ -460,7 +341,6 @@ const createRouter = (config) => {
|
|
|
460
341
|
};
|
|
461
342
|
};
|
|
462
343
|
// # register events
|
|
463
|
-
// hash mode: listen to hashchange
|
|
464
344
|
window.addEventListener('hashchange', () => {
|
|
465
345
|
const hash = window.location.hash.slice(1);
|
|
466
346
|
const [path] = hash.split('?');
|
|
@@ -502,13 +382,10 @@ const createRouter = (config) => {
|
|
|
502
382
|
}
|
|
503
383
|
}
|
|
504
384
|
}
|
|
505
|
-
|
|
385
|
+
executeAfterHooks(to, history[history.length - 2] ?? null);
|
|
506
386
|
});
|
|
507
387
|
// # initialize
|
|
508
|
-
|
|
509
|
-
const { findByName, match } = createMatcher(routes);
|
|
510
|
-
// Router instance
|
|
511
|
-
return {
|
|
388
|
+
const instance = {
|
|
512
389
|
get current() {
|
|
513
390
|
return current;
|
|
514
391
|
},
|
|
@@ -537,6 +414,13 @@ const createRouter = (config) => {
|
|
|
537
414
|
window.history.forward();
|
|
538
415
|
},
|
|
539
416
|
};
|
|
417
|
+
normalize(routes, '/');
|
|
418
|
+
const { findByName, match } = createMatcher(routes);
|
|
419
|
+
const currentHash = window.location.hash.slice(1);
|
|
420
|
+
if (currentHash) {
|
|
421
|
+
instance.push(currentHash);
|
|
422
|
+
}
|
|
423
|
+
return instance;
|
|
540
424
|
};
|
|
541
425
|
|
|
542
426
|
export { KTRouter, createRouter };
|