@real-router/core 0.38.0 → 0.40.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 +27 -5
- package/dist/cjs/Router-B-Pev7K2.d.ts +46 -0
- package/dist/cjs/RouterValidator-mx2Zooya.d.ts +136 -0
- package/dist/cjs/api.d.ts +2 -1
- package/dist/cjs/api.js +1 -1
- package/dist/cjs/api.js.map +1 -1
- package/dist/cjs/index.d-y2b-8_3Y.d.ts +236 -0
- package/dist/cjs/index.d.ts +7 -24
- 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/cjs/utils.d.ts +6 -1
- package/dist/cjs/utils.js +1 -1
- package/dist/cjs/utils.js.map +1 -1
- package/dist/cjs/validation.d.ts +184 -0
- package/dist/cjs/validation.js +1 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/Router-B-Pev7K2.d.mts +46 -0
- package/dist/esm/RouterValidator-mx2Zooya.d.mts +136 -0
- package/dist/esm/api.d.mts +2 -1
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/api.mjs.map +1 -1
- package/dist/esm/chunk-5QXFUUDL.mjs +1 -0
- package/dist/esm/chunk-5QXFUUDL.mjs.map +1 -0
- package/dist/esm/chunk-HHIXK5UM.mjs +1 -0
- package/dist/esm/chunk-HHIXK5UM.mjs.map +1 -0
- package/dist/esm/chunk-QUUNDESP.mjs +1 -0
- package/dist/esm/chunk-QUUNDESP.mjs.map +1 -0
- package/dist/esm/chunk-RA5VYM7M.mjs +1 -0
- package/dist/esm/chunk-RA5VYM7M.mjs.map +1 -0
- package/dist/esm/index.d-y2b-8_3Y.d.mts +236 -0
- package/dist/esm/index.d.mts +7 -24
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/metafile-esm.json +1 -1
- package/dist/esm/utils.d.mts +6 -1
- package/dist/esm/utils.mjs +1 -1
- package/dist/esm/utils.mjs.map +1 -1
- package/dist/esm/validation.d.mts +184 -0
- package/dist/esm/validation.mjs +1 -0
- package/dist/esm/validation.mjs.map +1 -0
- package/package.json +18 -5
- package/src/Router.ts +73 -99
- package/src/api/cloneRouter.ts +1 -30
- package/src/api/getDependenciesApi.ts +45 -86
- package/src/api/getLifecycleApi.ts +24 -19
- package/src/api/getPluginApi.ts +20 -28
- package/src/api/getRoutesApi.ts +49 -106
- package/src/constants.ts +0 -30
- package/src/guards.ts +46 -0
- package/src/helpers.ts +0 -17
- package/src/index.ts +4 -0
- package/src/internals.ts +6 -5
- package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +2 -2
- package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +0 -25
- package/src/namespaces/OptionsNamespace/OptionsNamespace.ts +4 -26
- package/src/namespaces/OptionsNamespace/constants.ts +0 -20
- package/src/namespaces/OptionsNamespace/index.ts +1 -5
- package/src/namespaces/OptionsNamespace/validators.ts +6 -245
- package/src/namespaces/PluginsNamespace/PluginsNamespace.ts +18 -59
- package/src/namespaces/PluginsNamespace/constants.ts +3 -6
- package/src/namespaces/PluginsNamespace/validators.ts +2 -57
- package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +27 -84
- package/src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts +0 -16
- package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +3 -12
- package/src/namespaces/RoutesNamespace/constants.ts +0 -8
- package/src/namespaces/RoutesNamespace/forwardChain.ts +34 -0
- package/src/namespaces/RoutesNamespace/index.ts +1 -1
- package/src/namespaces/RoutesNamespace/routeGuards.ts +62 -0
- package/src/namespaces/RoutesNamespace/routesStore.ts +7 -51
- package/src/namespaces/StateNamespace/StateNamespace.ts +0 -33
- package/src/namespaces/StateNamespace/helpers.ts +1 -1
- package/src/namespaces/index.ts +0 -3
- package/src/typeGuards.ts +1 -15
- package/src/types/RouterValidator.ts +155 -0
- package/src/utils/getStaticPaths.ts +50 -0
- package/src/utils/index.ts +4 -0
- package/src/validation.ts +12 -0
- package/src/wiring/RouterWiringBuilder.ts +32 -9
- package/dist/cjs/index.d-DDimDpYc.d.ts +0 -165
- package/dist/esm/chunk-CG7TKDP3.mjs +0 -1
- package/dist/esm/chunk-CG7TKDP3.mjs.map +0 -1
- package/dist/esm/index.d-DDimDpYc.d.mts +0 -165
- package/src/namespaces/DependenciesNamespace/validators.ts +0 -103
- package/src/namespaces/EventBusNamespace/validators.ts +0 -36
- package/src/namespaces/NavigationNamespace/validators.ts +0 -47
- package/src/namespaces/RouteLifecycleNamespace/validators.ts +0 -65
- package/src/namespaces/RoutesNamespace/forwardToValidation.ts +0 -408
- package/src/namespaces/RoutesNamespace/validators.ts +0 -566
- package/src/namespaces/StateNamespace/validators.ts +0 -46
package/src/api/getRoutesApi.ts
CHANGED
|
@@ -1,30 +1,22 @@
|
|
|
1
1
|
import { logger } from "@real-router/logger";
|
|
2
|
-
import { validateRouteName } from "type-guards";
|
|
3
2
|
|
|
4
3
|
import { throwIfDisposed } from "./helpers";
|
|
4
|
+
import { guardRouteStructure } from "../guards";
|
|
5
5
|
import { getInternals } from "../internals";
|
|
6
6
|
import {
|
|
7
7
|
clearConfigEntries,
|
|
8
8
|
removeFromDefinitions,
|
|
9
9
|
sanitizeRoute,
|
|
10
10
|
} from "../namespaces/RoutesNamespace/helpers";
|
|
11
|
+
import {
|
|
12
|
+
validateClearRoutes,
|
|
13
|
+
validateRemoveRoute,
|
|
14
|
+
} from "../namespaces/RoutesNamespace/routeGuards";
|
|
11
15
|
import {
|
|
12
16
|
clearRouteData,
|
|
13
17
|
refreshForwardMap,
|
|
14
18
|
registerAllRouteHandlers,
|
|
15
19
|
} from "../namespaces/RoutesNamespace/routesStore";
|
|
16
|
-
import {
|
|
17
|
-
throwIfInternalRoute,
|
|
18
|
-
throwIfInternalRouteInArray,
|
|
19
|
-
validateAddRouteArgs,
|
|
20
|
-
validateClearRoutes,
|
|
21
|
-
validateParentOption,
|
|
22
|
-
validateRemoveRoute,
|
|
23
|
-
validateRemoveRouteArgs,
|
|
24
|
-
validateUpdateRoute,
|
|
25
|
-
validateUpdateRouteBasicArgs,
|
|
26
|
-
validateUpdateRoutePropertyTypes,
|
|
27
|
-
} from "../namespaces/RoutesNamespace/validators";
|
|
28
20
|
|
|
29
21
|
import type { RoutesApi } from "./types";
|
|
30
22
|
import type { RouterInternals } from "../internals";
|
|
@@ -81,8 +73,8 @@ function clearRouteConfigurations<
|
|
|
81
73
|
routeCustomFields: Record<string, Record<string, unknown>>,
|
|
82
74
|
lifecycleNamespace: RouteLifecycleNamespace<Dependencies>,
|
|
83
75
|
): void {
|
|
84
|
-
const shouldClear = (
|
|
85
|
-
|
|
76
|
+
const shouldClear = (name: string): boolean =>
|
|
77
|
+
name === routeName || name.startsWith(`${routeName}.`);
|
|
86
78
|
|
|
87
79
|
clearConfigEntries(config.decoders, shouldClear);
|
|
88
80
|
clearConfigEntries(config.encoders, shouldClear);
|
|
@@ -100,15 +92,15 @@ function clearRouteConfigurations<
|
|
|
100
92
|
const [canDeactivateFactories, canActivateFactories] =
|
|
101
93
|
lifecycleNamespace.getFactories();
|
|
102
94
|
|
|
103
|
-
for (const
|
|
104
|
-
if (shouldClear(
|
|
105
|
-
lifecycleNamespace.clearCanActivate(
|
|
95
|
+
for (const name of Object.keys(canActivateFactories)) {
|
|
96
|
+
if (shouldClear(name)) {
|
|
97
|
+
lifecycleNamespace.clearCanActivate(name);
|
|
106
98
|
}
|
|
107
99
|
}
|
|
108
100
|
|
|
109
|
-
for (const
|
|
110
|
-
if (shouldClear(
|
|
111
|
-
lifecycleNamespace.clearCanDeactivate(
|
|
101
|
+
for (const name of Object.keys(canDeactivateFactories)) {
|
|
102
|
+
if (shouldClear(name)) {
|
|
103
|
+
lifecycleNamespace.clearCanDeactivate(name);
|
|
112
104
|
}
|
|
113
105
|
}
|
|
114
106
|
}
|
|
@@ -123,11 +115,7 @@ function updateForwardTo<
|
|
|
123
115
|
name: string,
|
|
124
116
|
forwardTo: string | ForwardToCallback<Dependencies> | null,
|
|
125
117
|
config: RouteConfig,
|
|
126
|
-
|
|
127
|
-
refreshForwardMapFn: (
|
|
128
|
-
config: RouteConfig,
|
|
129
|
-
noValidate: boolean,
|
|
130
|
-
) => Record<string, string>,
|
|
118
|
+
refreshForwardMapFn: (config: RouteConfig) => Record<string, string>,
|
|
131
119
|
): Record<string, string> {
|
|
132
120
|
if (forwardTo === null) {
|
|
133
121
|
delete config.forwardMap[name];
|
|
@@ -140,7 +128,7 @@ function updateForwardTo<
|
|
|
140
128
|
config.forwardFnMap[name] = forwardTo;
|
|
141
129
|
}
|
|
142
130
|
|
|
143
|
-
return refreshForwardMapFn(config
|
|
131
|
+
return refreshForwardMapFn(config);
|
|
144
132
|
}
|
|
145
133
|
|
|
146
134
|
/**
|
|
@@ -220,7 +208,6 @@ function addRoutes<
|
|
|
220
208
|
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
221
209
|
>(
|
|
222
210
|
store: RoutesStore<Dependencies>,
|
|
223
|
-
noValidate: boolean,
|
|
224
211
|
routes: Route<Dependencies>[],
|
|
225
212
|
parentName?: string,
|
|
226
213
|
): void {
|
|
@@ -249,7 +236,7 @@ function addRoutes<
|
|
|
249
236
|
parentName ?? "",
|
|
250
237
|
);
|
|
251
238
|
|
|
252
|
-
store.treeOperations.commitTreeChanges(store
|
|
239
|
+
store.treeOperations.commitTreeChanges(store);
|
|
253
240
|
}
|
|
254
241
|
|
|
255
242
|
/**
|
|
@@ -260,7 +247,6 @@ function replaceRoutes<
|
|
|
260
247
|
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
261
248
|
>(
|
|
262
249
|
store: RoutesStore<Dependencies>,
|
|
263
|
-
noValidate: boolean,
|
|
264
250
|
routes: Route<Dependencies>[],
|
|
265
251
|
ctx: RouterInternals<Dependencies>,
|
|
266
252
|
currentPath: string | undefined,
|
|
@@ -288,7 +274,7 @@ function replaceRoutes<
|
|
|
288
274
|
);
|
|
289
275
|
|
|
290
276
|
// Step 5: One tree rebuild
|
|
291
|
-
store.treeOperations.commitTreeChanges(store
|
|
277
|
+
store.treeOperations.commitTreeChanges(store);
|
|
292
278
|
|
|
293
279
|
// Step 6: Revalidate state
|
|
294
280
|
if (currentPath !== undefined) {
|
|
@@ -309,11 +295,7 @@ function replaceRoutes<
|
|
|
309
295
|
*/
|
|
310
296
|
function removeRoute<
|
|
311
297
|
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
312
|
-
>(
|
|
313
|
-
store: RoutesStore<Dependencies>,
|
|
314
|
-
noValidate: boolean,
|
|
315
|
-
name: string,
|
|
316
|
-
): boolean {
|
|
298
|
+
>(store: RoutesStore<Dependencies>, name: string): boolean {
|
|
317
299
|
const wasRemoved = removeFromDefinitions(store.definitions, name);
|
|
318
300
|
|
|
319
301
|
if (!wasRemoved) {
|
|
@@ -328,7 +310,7 @@ function removeRoute<
|
|
|
328
310
|
store.lifecycleNamespace!,
|
|
329
311
|
);
|
|
330
312
|
|
|
331
|
-
store.treeOperations.commitTreeChanges(store
|
|
313
|
+
store.treeOperations.commitTreeChanges(store);
|
|
332
314
|
|
|
333
315
|
return true;
|
|
334
316
|
}
|
|
@@ -340,7 +322,6 @@ function updateRouteConfig<
|
|
|
340
322
|
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
341
323
|
>(
|
|
342
324
|
store: RoutesStore<Dependencies>,
|
|
343
|
-
noValidate: boolean,
|
|
344
325
|
name: string,
|
|
345
326
|
updates: {
|
|
346
327
|
forwardTo?: string | ForwardToCallback<Dependencies> | null | undefined;
|
|
@@ -354,8 +335,7 @@ function updateRouteConfig<
|
|
|
354
335
|
name,
|
|
355
336
|
updates.forwardTo,
|
|
356
337
|
store.config,
|
|
357
|
-
|
|
358
|
-
refreshForwardMap,
|
|
338
|
+
(config) => refreshForwardMap(config),
|
|
359
339
|
);
|
|
360
340
|
}
|
|
361
341
|
|
|
@@ -424,7 +404,6 @@ export function getRoutesApi<
|
|
|
424
404
|
const ctx = getInternals(router);
|
|
425
405
|
|
|
426
406
|
const store = ctx.routeGetStore();
|
|
427
|
-
const noValidate = ctx.noValidate;
|
|
428
407
|
|
|
429
408
|
return {
|
|
430
409
|
add: (routes, options) => {
|
|
@@ -433,32 +412,24 @@ export function getRoutesApi<
|
|
|
433
412
|
const routeArray = Array.isArray(routes) ? routes : [routes];
|
|
434
413
|
const parentName = options?.parent;
|
|
435
414
|
|
|
436
|
-
|
|
437
|
-
if (parentName !== undefined) {
|
|
438
|
-
validateParentOption(parentName);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
throwIfInternalRouteInArray(routeArray, "addRoute");
|
|
442
|
-
validateAddRouteArgs(routeArray);
|
|
415
|
+
guardRouteStructure(routeArray, ctx.validator);
|
|
443
416
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
store.tree,
|
|
447
|
-
store.config.forwardMap,
|
|
448
|
-
parentName,
|
|
449
|
-
);
|
|
417
|
+
if (parentName !== undefined) {
|
|
418
|
+
ctx.validator?.routes.validateParentOption(parentName, store.tree);
|
|
450
419
|
}
|
|
451
420
|
|
|
452
|
-
|
|
421
|
+
ctx.validator?.routes.throwIfInternalRouteInArray(routeArray, "addRoute");
|
|
422
|
+
ctx.validator?.routes.validateAddRouteArgs(routeArray);
|
|
423
|
+
ctx.validator?.routes.validateRoutes(routeArray, store);
|
|
424
|
+
|
|
425
|
+
addRoutes(store, routeArray, parentName);
|
|
453
426
|
},
|
|
454
427
|
|
|
455
428
|
remove: (name) => {
|
|
456
429
|
throwIfDisposed(ctx.isDisposed);
|
|
457
430
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
throwIfInternalRoute(name, "removeRoute");
|
|
461
|
-
}
|
|
431
|
+
ctx.validator?.routes.validateRemoveRouteArgs(name);
|
|
432
|
+
ctx.validator?.routes.throwIfInternalRoute(name, "removeRoute");
|
|
462
433
|
|
|
463
434
|
const canRemove = validateRemoveRoute(
|
|
464
435
|
name,
|
|
@@ -470,7 +441,7 @@ export function getRoutesApi<
|
|
|
470
441
|
return;
|
|
471
442
|
}
|
|
472
443
|
|
|
473
|
-
const wasRemoved = removeRoute(store,
|
|
444
|
+
const wasRemoved = removeRoute(store, name);
|
|
474
445
|
|
|
475
446
|
if (!wasRemoved) {
|
|
476
447
|
logger.warn(
|
|
@@ -483,10 +454,8 @@ export function getRoutesApi<
|
|
|
483
454
|
update: (name, updates) => {
|
|
484
455
|
throwIfDisposed(ctx.isDisposed);
|
|
485
456
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
throwIfInternalRoute(name, "updateRoute");
|
|
489
|
-
}
|
|
457
|
+
ctx.validator?.routes.validateUpdateRouteBasicArgs(name, updates);
|
|
458
|
+
ctx.validator?.routes.throwIfInternalRoute(name, "updateRoute");
|
|
490
459
|
|
|
491
460
|
const {
|
|
492
461
|
forwardTo,
|
|
@@ -497,14 +466,7 @@ export function getRoutesApi<
|
|
|
497
466
|
canDeactivate,
|
|
498
467
|
} = updates;
|
|
499
468
|
|
|
500
|
-
|
|
501
|
-
validateUpdateRoutePropertyTypes(
|
|
502
|
-
forwardTo,
|
|
503
|
-
defaultParams,
|
|
504
|
-
decodeParams,
|
|
505
|
-
encodeParams,
|
|
506
|
-
);
|
|
507
|
-
}
|
|
469
|
+
ctx.validator?.routes.validateUpdateRoutePropertyTypes(name, updates);
|
|
508
470
|
|
|
509
471
|
/* v8 ignore next 6 -- @preserve: race condition guard, mirrors Router.updateRoute() same-path guard tested via Router.ts unit tests */
|
|
510
472
|
if (ctx.isTransitioning()) {
|
|
@@ -514,17 +476,9 @@ export function getRoutesApi<
|
|
|
514
476
|
);
|
|
515
477
|
}
|
|
516
478
|
|
|
517
|
-
|
|
518
|
-
validateUpdateRoute(
|
|
519
|
-
name,
|
|
520
|
-
forwardTo,
|
|
521
|
-
(n) => store.matcher.hasRoute(n),
|
|
522
|
-
store.matcher,
|
|
523
|
-
store.config,
|
|
524
|
-
);
|
|
525
|
-
}
|
|
479
|
+
ctx.validator?.routes.validateUpdateRoute(name, updates, store);
|
|
526
480
|
|
|
527
|
-
updateRouteConfig(store,
|
|
481
|
+
updateRouteConfig(store, name, {
|
|
528
482
|
forwardTo,
|
|
529
483
|
defaultParams,
|
|
530
484
|
decodeParams,
|
|
@@ -537,12 +491,7 @@ export function getRoutesApi<
|
|
|
537
491
|
store.lifecycleNamespace!.clearCanActivate(name);
|
|
538
492
|
} else {
|
|
539
493
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring
|
|
540
|
-
store.lifecycleNamespace!.addCanActivate(
|
|
541
|
-
name,
|
|
542
|
-
canActivate,
|
|
543
|
-
noValidate,
|
|
544
|
-
true,
|
|
545
|
-
);
|
|
494
|
+
store.lifecycleNamespace!.addCanActivate(name, canActivate, true);
|
|
546
495
|
}
|
|
547
496
|
}
|
|
548
497
|
|
|
@@ -552,12 +501,7 @@ export function getRoutesApi<
|
|
|
552
501
|
store.lifecycleNamespace!.clearCanDeactivate(name);
|
|
553
502
|
} else {
|
|
554
503
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring
|
|
555
|
-
store.lifecycleNamespace!.addCanDeactivate(
|
|
556
|
-
name,
|
|
557
|
-
canDeactivate,
|
|
558
|
-
noValidate,
|
|
559
|
-
true,
|
|
560
|
-
);
|
|
504
|
+
store.lifecycleNamespace!.addCanDeactivate(name, canDeactivate, true);
|
|
561
505
|
}
|
|
562
506
|
}
|
|
563
507
|
},
|
|
@@ -579,17 +523,13 @@ export function getRoutesApi<
|
|
|
579
523
|
},
|
|
580
524
|
|
|
581
525
|
has: (name) => {
|
|
582
|
-
|
|
583
|
-
validateRouteName(name, "hasRoute");
|
|
584
|
-
}
|
|
526
|
+
ctx.validator?.routes.validateRouteName(name, "hasRoute");
|
|
585
527
|
|
|
586
528
|
return store.matcher.hasRoute(name);
|
|
587
529
|
},
|
|
588
530
|
|
|
589
531
|
get: (name) => {
|
|
590
|
-
|
|
591
|
-
validateRouteName(name, "getRoute");
|
|
592
|
-
}
|
|
532
|
+
ctx.validator?.routes.validateRouteName(name, "getRoute");
|
|
593
533
|
|
|
594
534
|
return getRoute(store, name);
|
|
595
535
|
},
|
|
@@ -605,15 +545,18 @@ export function getRoutesApi<
|
|
|
605
545
|
return;
|
|
606
546
|
}
|
|
607
547
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
548
|
+
guardRouteStructure(routeArray, ctx.validator);
|
|
549
|
+
|
|
550
|
+
ctx.validator?.routes.throwIfInternalRouteInArray(
|
|
551
|
+
routeArray,
|
|
552
|
+
"replaceRoutes",
|
|
553
|
+
);
|
|
554
|
+
ctx.validator?.routes.validateAddRouteArgs(routeArray);
|
|
555
|
+
ctx.validator?.routes.validateRoutes(routeArray, store);
|
|
613
556
|
|
|
614
557
|
const currentPath = router.getState()?.path;
|
|
615
558
|
|
|
616
|
-
replaceRoutes(store,
|
|
559
|
+
replaceRoutes(store, routeArray, ctx, currentPath);
|
|
617
560
|
},
|
|
618
561
|
};
|
|
619
562
|
}
|
package/src/constants.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// packages/core/src/constants.ts
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
|
-
EventName,
|
|
5
4
|
EventToNameMap,
|
|
6
5
|
EventToPluginMap,
|
|
7
6
|
ErrorCodeToValueMap,
|
|
@@ -74,22 +73,6 @@ export const events: EventToNameMap = {
|
|
|
74
73
|
TRANSITION_ERROR: "$$error", // Emitted when navigation fails
|
|
75
74
|
};
|
|
76
75
|
|
|
77
|
-
/**
|
|
78
|
-
* Valid event names for validation.
|
|
79
|
-
*/
|
|
80
|
-
export const validEventNames = new Set<EventName>([
|
|
81
|
-
events.ROUTER_START,
|
|
82
|
-
events.TRANSITION_START,
|
|
83
|
-
events.TRANSITION_SUCCESS,
|
|
84
|
-
events.TRANSITION_ERROR,
|
|
85
|
-
events.TRANSITION_CANCEL,
|
|
86
|
-
events.ROUTER_STOP,
|
|
87
|
-
]);
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Default limits configuration for the router.
|
|
91
|
-
* These values match the hardcoded constants from the current codebase.
|
|
92
|
-
*/
|
|
93
76
|
export const DEFAULT_LIMITS = {
|
|
94
77
|
maxDependencies: 100,
|
|
95
78
|
maxPlugins: 50,
|
|
@@ -100,16 +83,3 @@ export const DEFAULT_LIMITS = {
|
|
|
100
83
|
} as const;
|
|
101
84
|
|
|
102
85
|
export const EMPTY_PARAMS: Readonly<Record<string, never>> = Object.freeze({});
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Bounds for each limit - defines min and max allowed values.
|
|
106
|
-
* Used for runtime validation in setLimit/withLimits.
|
|
107
|
-
*/
|
|
108
|
-
export const LIMIT_BOUNDS = {
|
|
109
|
-
maxDependencies: { min: 0, max: 10_000 },
|
|
110
|
-
maxPlugins: { min: 0, max: 1000 },
|
|
111
|
-
maxListeners: { min: 0, max: 100_000 },
|
|
112
|
-
warnListeners: { min: 0, max: 100_000 },
|
|
113
|
-
maxEventDepth: { min: 0, max: 100 },
|
|
114
|
-
maxLifecycleHandlers: { min: 0, max: 10_000 },
|
|
115
|
-
} as const;
|
package/src/guards.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// packages/core/src/guards.ts
|
|
2
|
+
|
|
3
|
+
import type { Route } from "./types";
|
|
4
|
+
import type { RouterValidator } from "./types/RouterValidator";
|
|
5
|
+
|
|
6
|
+
export function guardDependencies(deps: unknown): void {
|
|
7
|
+
if (
|
|
8
|
+
!deps ||
|
|
9
|
+
typeof deps !== "object" ||
|
|
10
|
+
(deps as { constructor: unknown }).constructor !== Object
|
|
11
|
+
) {
|
|
12
|
+
throw new TypeError("dependencies must be a plain object");
|
|
13
|
+
}
|
|
14
|
+
for (const key in deps as Record<string, unknown>) {
|
|
15
|
+
if (Object.getOwnPropertyDescriptor(deps, key)?.get) {
|
|
16
|
+
throw new TypeError(`dependencies cannot contain getters: "${key}"`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* eslint-disable @typescript-eslint/no-explicit-any -- accepts any Route type */
|
|
22
|
+
export function guardRouteStructure(
|
|
23
|
+
routes: Route<any>[],
|
|
24
|
+
validator?: RouterValidator | null,
|
|
25
|
+
): void {
|
|
26
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
27
|
+
for (const route of routes) {
|
|
28
|
+
const routeValue: unknown = route;
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
routeValue === null ||
|
|
32
|
+
typeof routeValue !== "object" ||
|
|
33
|
+
Array.isArray(routeValue)
|
|
34
|
+
) {
|
|
35
|
+
throw new TypeError("route must be a non-array object");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
validator?.routes.guardRouteCallbacks(route as Route);
|
|
39
|
+
validator?.routes.guardNoAsyncCallbacks(route as Route);
|
|
40
|
+
const children = (route as Route).children;
|
|
41
|
+
|
|
42
|
+
if (children) {
|
|
43
|
+
guardRouteStructure(children, validator);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/helpers.ts
CHANGED
|
@@ -5,8 +5,6 @@ import { DEFAULT_LIMITS } from "./constants";
|
|
|
5
5
|
import type { Limits } from "./types";
|
|
6
6
|
import type { State, LimitsConfig } from "@real-router/types";
|
|
7
7
|
|
|
8
|
-
export { getTypeDescription } from "type-guards";
|
|
9
|
-
|
|
10
8
|
// =============================================================================
|
|
11
9
|
// State Helpers
|
|
12
10
|
// =============================================================================
|
|
@@ -170,21 +168,6 @@ export function freezeStateInPlace<T extends State>(state: T): T {
|
|
|
170
168
|
return state;
|
|
171
169
|
}
|
|
172
170
|
|
|
173
|
-
/**
|
|
174
|
-
* Computes warning and error thresholds for a given limit.
|
|
175
|
-
* WARN threshold: 20% of limit
|
|
176
|
-
* ERROR threshold: 50% of limit
|
|
177
|
-
*/
|
|
178
|
-
export function computeThresholds(limit: number): {
|
|
179
|
-
warn: number;
|
|
180
|
-
error: number;
|
|
181
|
-
} {
|
|
182
|
-
return {
|
|
183
|
-
warn: Math.floor(limit * 0.2),
|
|
184
|
-
error: Math.floor(limit * 0.5),
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
171
|
/**
|
|
189
172
|
* Merges user limits with defaults.
|
|
190
173
|
* Returns frozen object for immutability.
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,8 @@ export type {
|
|
|
10
10
|
RouteConfigUpdate,
|
|
11
11
|
} from "./types";
|
|
12
12
|
|
|
13
|
+
export type { RouterValidator } from "./types/RouterValidator";
|
|
14
|
+
|
|
13
15
|
// Router class (replaces Router interface from core-types)
|
|
14
16
|
export { Router } from "./Router";
|
|
15
17
|
|
|
@@ -44,4 +46,6 @@ export { createRouter } from "./createRouter";
|
|
|
44
46
|
|
|
45
47
|
export { getNavigator } from "./getNavigator";
|
|
46
48
|
|
|
49
|
+
export { resolveForwardChain } from "./namespaces/RoutesNamespace/forwardChain";
|
|
50
|
+
|
|
47
51
|
export type { RouteTree } from "route-tree";
|
package/src/internals.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { DependenciesStore } from "./namespaces/DependenciesNamespace";
|
|
|
2
2
|
import type { RoutesStore } from "./namespaces/RoutesNamespace";
|
|
3
3
|
import type { Router as RouterClass } from "./Router";
|
|
4
4
|
import type { EventMethodMap, GuardFnFactory, PluginFactory } from "./types";
|
|
5
|
+
import type { RouterValidator } from "./types/RouterValidator";
|
|
5
6
|
import type {
|
|
6
7
|
DefaultDependencies,
|
|
7
8
|
EventName,
|
|
@@ -67,7 +68,7 @@ export interface RouterInternals<
|
|
|
67
68
|
|
|
68
69
|
readonly isDisposed: () => boolean;
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
validator: RouterValidator | null;
|
|
71
72
|
|
|
72
73
|
// Dependencies (issue #172)
|
|
73
74
|
readonly dependenciesGetStore: () => DependenciesStore<D>;
|
|
@@ -127,7 +128,7 @@ function executeInterceptorChain<T>(
|
|
|
127
128
|
for (const interceptor of interceptors) {
|
|
128
129
|
const prev = chain;
|
|
129
130
|
|
|
130
|
-
chain = (...
|
|
131
|
+
chain = (...chainArgs: any[]) => interceptor(prev, ...chainArgs);
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
return chain(...args) as T;
|
|
@@ -160,14 +161,14 @@ export function createInterceptable2<A, B, R>(
|
|
|
160
161
|
((next: (...args: any[]) => any, ...args: any[]) => any)[]
|
|
161
162
|
>,
|
|
162
163
|
): (a: A, b: B) => R {
|
|
163
|
-
return (
|
|
164
|
+
return (arg1: A, arg2: B) => {
|
|
164
165
|
const chain = interceptors.get(name);
|
|
165
166
|
|
|
166
167
|
if (!chain || chain.length === 0) {
|
|
167
|
-
return original(
|
|
168
|
+
return original(arg1, arg2);
|
|
168
169
|
}
|
|
169
170
|
|
|
170
|
-
return executeInterceptorChain(chain, original, [
|
|
171
|
+
return executeInterceptorChain(chain, original, [arg1, arg2]);
|
|
171
172
|
};
|
|
172
173
|
}
|
|
173
174
|
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
|
|
@@ -158,9 +158,9 @@ export class EventBusNamespace {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
isActive(): boolean {
|
|
161
|
-
const
|
|
161
|
+
const fsmState = this.#fsm.getState();
|
|
162
162
|
|
|
163
|
-
return
|
|
163
|
+
return fsmState !== routerStates.IDLE && fsmState !== routerStates.DISPOSED;
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
isDisposed(): boolean {
|
|
@@ -10,11 +10,6 @@ import {
|
|
|
10
10
|
import { completeTransition } from "./transition/completeTransition";
|
|
11
11
|
import { routeTransitionError } from "./transition/errorHandling";
|
|
12
12
|
import { executeGuardPipeline } from "./transition/guardPhase";
|
|
13
|
-
import {
|
|
14
|
-
validateNavigateArgs,
|
|
15
|
-
validateNavigateToDefaultArgs,
|
|
16
|
-
validateNavigationOptions,
|
|
17
|
-
} from "./validators";
|
|
18
13
|
import { errorCodes, constants } from "../../constants";
|
|
19
14
|
import { RouterError } from "../../RouterError";
|
|
20
15
|
import { getTransitionPath, nameToIDs } from "../../transitionPath";
|
|
@@ -73,26 +68,6 @@ export class NavigationNamespace {
|
|
|
73
68
|
#currentController: AbortController | null = null;
|
|
74
69
|
#navigationId = 0;
|
|
75
70
|
|
|
76
|
-
// =========================================================================
|
|
77
|
-
// Static validation methods (called by facade before instance methods)
|
|
78
|
-
// Proxy to functions in validators.ts for separation of concerns
|
|
79
|
-
// =========================================================================
|
|
80
|
-
|
|
81
|
-
static validateNavigateArgs(name: unknown): asserts name is string {
|
|
82
|
-
validateNavigateArgs(name);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
static validateNavigateToDefaultArgs(opts: unknown): void {
|
|
86
|
-
validateNavigateToDefaultArgs(opts);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
static validateNavigationOptions(
|
|
90
|
-
opts: unknown,
|
|
91
|
-
methodName: string,
|
|
92
|
-
): asserts opts is NavigationOptions {
|
|
93
|
-
validateNavigationOptions(opts, methodName);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
71
|
// =========================================================================
|
|
97
72
|
// Dependency injection
|
|
98
73
|
// =========================================================================
|
|
@@ -2,48 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
import { defaultOptions } from "./constants";
|
|
4
4
|
import { deepFreeze } from "./helpers";
|
|
5
|
-
import {
|
|
5
|
+
import { validateOptionsIsObject } from "./validators";
|
|
6
6
|
|
|
7
7
|
import type { Options } from "@real-router/types";
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* Independent namespace for managing router options.
|
|
11
|
-
*
|
|
12
|
-
* Options are immutable after construction.
|
|
13
|
-
* Static methods handle validation (called by facade).
|
|
14
|
-
* Instance methods provide read-only access.
|
|
15
|
-
*/
|
|
16
9
|
export class OptionsNamespace {
|
|
17
10
|
readonly #options: Readonly<Options>;
|
|
18
11
|
|
|
19
12
|
constructor(initialOptions: Partial<Options> = {}) {
|
|
20
|
-
// Note: validation should be done by facade before calling constructor
|
|
21
13
|
this.#options = deepFreeze({
|
|
22
14
|
...defaultOptions,
|
|
23
15
|
...initialOptions,
|
|
24
16
|
});
|
|
25
17
|
}
|
|
26
18
|
|
|
27
|
-
|
|
28
|
-
// Static validation methods (called by facade before instance methods)
|
|
29
|
-
// Proxy to functions in validators.ts for separation of concerns
|
|
30
|
-
// =========================================================================
|
|
31
|
-
|
|
32
|
-
static validateOptions(
|
|
19
|
+
static validateOptionsIsObject(
|
|
33
20
|
options: unknown,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
validateOptions(options, methodName);
|
|
21
|
+
): asserts options is Record<string, unknown> {
|
|
22
|
+
validateOptionsIsObject(options);
|
|
37
23
|
}
|
|
38
24
|
|
|
39
|
-
// =========================================================================
|
|
40
|
-
// Instance methods (read-only access)
|
|
41
|
-
// =========================================================================
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Returns the frozen options object.
|
|
45
|
-
* Safe to return directly - mutations will throw in strict mode.
|
|
46
|
-
*/
|
|
47
25
|
get(): Readonly<Options> {
|
|
48
26
|
return this.#options;
|
|
49
27
|
}
|
|
@@ -18,24 +18,4 @@ export const defaultOptions: Options = {
|
|
|
18
18
|
urlParamsEncoding: "default",
|
|
19
19
|
allowNotFound: true,
|
|
20
20
|
rewritePathOnMatch: true,
|
|
21
|
-
noValidate: false,
|
|
22
21
|
} satisfies Options;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Valid values for string enum options.
|
|
26
|
-
* Used for runtime validation in constructor options.
|
|
27
|
-
*/
|
|
28
|
-
export const VALID_OPTION_VALUES = {
|
|
29
|
-
trailingSlash: ["strict", "never", "always", "preserve"] as const,
|
|
30
|
-
queryParamsMode: ["default", "strict", "loose"] as const,
|
|
31
|
-
urlParamsEncoding: ["default", "uri", "uriComponent", "none"] as const,
|
|
32
|
-
} as const;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Valid keys and values for queryParams option.
|
|
36
|
-
*/
|
|
37
|
-
export const VALID_QUERY_PARAMS = {
|
|
38
|
-
arrayFormat: ["none", "brackets", "index", "comma"] as const,
|
|
39
|
-
booleanFormat: ["none", "string", "empty-true"] as const,
|
|
40
|
-
nullFormat: ["default", "hidden"] as const,
|
|
41
|
-
} as const;
|
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
export { OptionsNamespace } from "./OptionsNamespace";
|
|
4
4
|
|
|
5
|
-
export {
|
|
6
|
-
defaultOptions,
|
|
7
|
-
VALID_OPTION_VALUES,
|
|
8
|
-
VALID_QUERY_PARAMS,
|
|
9
|
-
} from "./constants";
|
|
5
|
+
export { defaultOptions } from "./constants";
|
|
10
6
|
|
|
11
7
|
export { deepFreeze, resolveOption } from "./helpers";
|