@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.
Files changed (89) hide show
  1. package/README.md +27 -5
  2. package/dist/cjs/Router-B-Pev7K2.d.ts +46 -0
  3. package/dist/cjs/RouterValidator-mx2Zooya.d.ts +136 -0
  4. package/dist/cjs/api.d.ts +2 -1
  5. package/dist/cjs/api.js +1 -1
  6. package/dist/cjs/api.js.map +1 -1
  7. package/dist/cjs/index.d-y2b-8_3Y.d.ts +236 -0
  8. package/dist/cjs/index.d.ts +7 -24
  9. package/dist/cjs/index.js +1 -1
  10. package/dist/cjs/index.js.map +1 -1
  11. package/dist/cjs/metafile-cjs.json +1 -1
  12. package/dist/cjs/utils.d.ts +6 -1
  13. package/dist/cjs/utils.js +1 -1
  14. package/dist/cjs/utils.js.map +1 -1
  15. package/dist/cjs/validation.d.ts +184 -0
  16. package/dist/cjs/validation.js +1 -0
  17. package/dist/cjs/validation.js.map +1 -0
  18. package/dist/esm/Router-B-Pev7K2.d.mts +46 -0
  19. package/dist/esm/RouterValidator-mx2Zooya.d.mts +136 -0
  20. package/dist/esm/api.d.mts +2 -1
  21. package/dist/esm/api.mjs +1 -1
  22. package/dist/esm/api.mjs.map +1 -1
  23. package/dist/esm/chunk-5QXFUUDL.mjs +1 -0
  24. package/dist/esm/chunk-5QXFUUDL.mjs.map +1 -0
  25. package/dist/esm/chunk-HHIXK5UM.mjs +1 -0
  26. package/dist/esm/chunk-HHIXK5UM.mjs.map +1 -0
  27. package/dist/esm/chunk-QUUNDESP.mjs +1 -0
  28. package/dist/esm/chunk-QUUNDESP.mjs.map +1 -0
  29. package/dist/esm/chunk-RA5VYM7M.mjs +1 -0
  30. package/dist/esm/chunk-RA5VYM7M.mjs.map +1 -0
  31. package/dist/esm/index.d-y2b-8_3Y.d.mts +236 -0
  32. package/dist/esm/index.d.mts +7 -24
  33. package/dist/esm/index.mjs +1 -1
  34. package/dist/esm/metafile-esm.json +1 -1
  35. package/dist/esm/utils.d.mts +6 -1
  36. package/dist/esm/utils.mjs +1 -1
  37. package/dist/esm/utils.mjs.map +1 -1
  38. package/dist/esm/validation.d.mts +184 -0
  39. package/dist/esm/validation.mjs +1 -0
  40. package/dist/esm/validation.mjs.map +1 -0
  41. package/package.json +18 -5
  42. package/src/Router.ts +73 -99
  43. package/src/api/cloneRouter.ts +1 -30
  44. package/src/api/getDependenciesApi.ts +45 -86
  45. package/src/api/getLifecycleApi.ts +24 -19
  46. package/src/api/getPluginApi.ts +20 -28
  47. package/src/api/getRoutesApi.ts +49 -106
  48. package/src/constants.ts +0 -30
  49. package/src/guards.ts +46 -0
  50. package/src/helpers.ts +0 -17
  51. package/src/index.ts +4 -0
  52. package/src/internals.ts +6 -5
  53. package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +2 -2
  54. package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +0 -25
  55. package/src/namespaces/OptionsNamespace/OptionsNamespace.ts +4 -26
  56. package/src/namespaces/OptionsNamespace/constants.ts +0 -20
  57. package/src/namespaces/OptionsNamespace/index.ts +1 -5
  58. package/src/namespaces/OptionsNamespace/validators.ts +6 -245
  59. package/src/namespaces/PluginsNamespace/PluginsNamespace.ts +18 -59
  60. package/src/namespaces/PluginsNamespace/constants.ts +3 -6
  61. package/src/namespaces/PluginsNamespace/validators.ts +2 -57
  62. package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +27 -84
  63. package/src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts +0 -16
  64. package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +3 -12
  65. package/src/namespaces/RoutesNamespace/constants.ts +0 -8
  66. package/src/namespaces/RoutesNamespace/forwardChain.ts +34 -0
  67. package/src/namespaces/RoutesNamespace/index.ts +1 -1
  68. package/src/namespaces/RoutesNamespace/routeGuards.ts +62 -0
  69. package/src/namespaces/RoutesNamespace/routesStore.ts +7 -51
  70. package/src/namespaces/StateNamespace/StateNamespace.ts +0 -33
  71. package/src/namespaces/StateNamespace/helpers.ts +1 -1
  72. package/src/namespaces/index.ts +0 -3
  73. package/src/typeGuards.ts +1 -15
  74. package/src/types/RouterValidator.ts +155 -0
  75. package/src/utils/getStaticPaths.ts +50 -0
  76. package/src/utils/index.ts +4 -0
  77. package/src/validation.ts +12 -0
  78. package/src/wiring/RouterWiringBuilder.ts +32 -9
  79. package/dist/cjs/index.d-DDimDpYc.d.ts +0 -165
  80. package/dist/esm/chunk-CG7TKDP3.mjs +0 -1
  81. package/dist/esm/chunk-CG7TKDP3.mjs.map +0 -1
  82. package/dist/esm/index.d-DDimDpYc.d.mts +0 -165
  83. package/src/namespaces/DependenciesNamespace/validators.ts +0 -103
  84. package/src/namespaces/EventBusNamespace/validators.ts +0 -36
  85. package/src/namespaces/NavigationNamespace/validators.ts +0 -47
  86. package/src/namespaces/RouteLifecycleNamespace/validators.ts +0 -65
  87. package/src/namespaces/RoutesNamespace/forwardToValidation.ts +0 -408
  88. package/src/namespaces/RoutesNamespace/validators.ts +0 -566
  89. package/src/namespaces/StateNamespace/validators.ts +0 -46
@@ -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 = (n: string): boolean =>
85
- n === routeName || n.startsWith(`${routeName}.`);
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 n of Object.keys(canActivateFactories)) {
104
- if (shouldClear(n)) {
105
- lifecycleNamespace.clearCanActivate(n);
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 n of Object.keys(canDeactivateFactories)) {
110
- if (shouldClear(n)) {
111
- lifecycleNamespace.clearCanDeactivate(n);
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
- noValidate: boolean,
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, noValidate);
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, noValidate);
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, noValidate);
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, noValidate);
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
- noValidate,
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
- if (!ctx.noValidate) {
437
- if (parentName !== undefined) {
438
- validateParentOption(parentName);
439
- }
440
-
441
- throwIfInternalRouteInArray(routeArray, "addRoute");
442
- validateAddRouteArgs(routeArray);
415
+ guardRouteStructure(routeArray, ctx.validator);
443
416
 
444
- store.treeOperations.validateRoutes(
445
- routeArray,
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
- addRoutes(store, noValidate, routeArray, parentName);
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
- if (!ctx.noValidate) {
459
- validateRemoveRouteArgs(name);
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, noValidate, name);
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
- if (!ctx.noValidate) {
487
- validateUpdateRouteBasicArgs(name, updates);
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
- if (!ctx.noValidate) {
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
- if (!ctx.noValidate) {
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, noValidate, name, {
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
- if (!ctx.noValidate) {
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
- if (!ctx.noValidate) {
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
- if (!ctx.noValidate) {
609
- throwIfInternalRouteInArray(routeArray, "replaceRoutes");
610
- validateAddRouteArgs(routeArray);
611
- store.treeOperations.validateRoutes(routeArray);
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, noValidate, routeArray, ctx, currentPath);
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
- readonly noValidate: boolean;
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 = (...a: any[]) => interceptor(prev, ...a);
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 (a: A, b: B) => {
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(a, b);
168
+ return original(arg1, arg2);
168
169
  }
169
170
 
170
- return executeInterceptorChain(chain, original, [a, b]);
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 s = this.#fsm.getState();
161
+ const fsmState = this.#fsm.getState();
162
162
 
163
- return s !== routerStates.IDLE && s !== routerStates.DISPOSED;
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 { validateOptions } from "./validators";
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
- methodName: string,
35
- ): asserts options is Partial<Options> {
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";