@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,566 +0,0 @@
1
- // packages/core/src/namespaces/RoutesNamespace/validators.ts
2
-
3
- /**
4
- * Static validation functions for RoutesNamespace.
5
- * Called by Router facade before instance methods.
6
- *
7
- * Extracted from RoutesNamespace class for better separation of concerns.
8
- */
9
-
10
- import { logger } from "@real-router/logger";
11
- import { validateRoute } from "route-tree";
12
- import {
13
- isString,
14
- validateRouteName,
15
- isParams,
16
- getTypeDescription,
17
- } from "type-guards";
18
-
19
- import {
20
- resolveForwardChain,
21
- validateForwardToTargets,
22
- validateRouteProperties,
23
- } from "./forwardToValidation";
24
-
25
- import type { RouteConfig } from "./types";
26
- import type { Route, RouteConfigUpdate } from "../../types";
27
- import type {
28
- DefaultDependencies,
29
- ForwardToCallback,
30
- } from "@real-router/types";
31
- import type { Matcher, RouteTree } from "route-tree";
32
-
33
- // SECURITY: Reserved prefix for system routes (e.g., @@router/UNKNOWN_ROUTE).
34
- // Internal code (RouterWiringBuilder, routesStore) bypasses this check.
35
- const INTERNAL_ROUTE_PREFIX = "@@";
36
-
37
- export function throwIfInternalRoute(name: string, methodName: string): void {
38
- if (name.startsWith(INTERNAL_ROUTE_PREFIX)) {
39
- throw new Error(
40
- `[router.${methodName}] Route name "${name}" uses the reserved "${INTERNAL_ROUTE_PREFIX}" prefix. Routes with this prefix are internal and cannot be modified through the public API.`,
41
- );
42
- }
43
- }
44
-
45
- export function throwIfInternalRouteInArray(
46
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Route type
47
- routes: readonly Route<any>[],
48
- methodName: string,
49
- ): void {
50
- for (const route of routes) {
51
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime safety
52
- if (route && typeof route === "object" && typeof route.name === "string") {
53
- throwIfInternalRoute(route.name, methodName);
54
-
55
- if (route.children) {
56
- throwIfInternalRouteInArray(route.children, methodName);
57
- }
58
- }
59
- }
60
- }
61
-
62
- /**
63
- * Validates removeRoute arguments.
64
- */
65
- export function validateRemoveRouteArgs(name: unknown): asserts name is string {
66
- validateRouteName(name, "removeRoute");
67
- }
68
-
69
- /**
70
- * Validates setRootPath arguments.
71
- */
72
- export function validateSetRootPathArgs(
73
- rootPath: unknown,
74
- ): asserts rootPath is string {
75
- if (typeof rootPath !== "string") {
76
- throw new TypeError(
77
- `[router.setRootPath] rootPath must be a string, got ${getTypeDescription(rootPath)}`,
78
- );
79
- }
80
- }
81
-
82
- /**
83
- * Validates addRoute arguments (route structure and properties).
84
- * State-dependent validation (duplicates, tree) happens in instance method.
85
- */
86
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- accepts any Route type
87
- export function validateAddRouteArgs(routes: readonly Route<any>[]): void {
88
- for (const route of routes) {
89
- // First check if route is an object (before accessing route.name)
90
- // Runtime check for invalid types passed via `as any`
91
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime check
92
- if (route === null || typeof route !== "object" || Array.isArray(route)) {
93
- throw new TypeError(
94
- `[router.addRoute] Route must be an object, got ${getTypeDescription(route)}`,
95
- );
96
- }
97
-
98
- // Validate route properties (canActivate, canDeactivate, defaultParams, async checks)
99
- // Note: validateRouteProperties handles children recursively
100
- validateRouteProperties(route, route.name);
101
- }
102
- }
103
-
104
- /**
105
- * Validates parent option for addRoute.
106
- */
107
- export function validateParentOption(
108
- parent: unknown,
109
- ): asserts parent is string {
110
- if (typeof parent !== "string" || parent === "") {
111
- throw new TypeError(
112
- `[router.addRoute] parent option must be a non-empty string, got ${getTypeDescription(parent)}`,
113
- );
114
- }
115
-
116
- // Validate parent is a valid route name format (can contain dots — it's a fullName reference)
117
- validateRouteName(parent, "addRoute");
118
- }
119
-
120
- /**
121
- * Validates isActiveRoute arguments.
122
- */
123
- export function validateIsActiveRouteArgs(
124
- name: unknown,
125
- params: unknown,
126
- strictEquality: unknown,
127
- ignoreQueryParams: unknown,
128
- ): void {
129
- // Validate name - non-string throws
130
- if (!isString(name)) {
131
- throw new TypeError(`Route name must be a string`);
132
- }
133
-
134
- // Validate params if provided
135
- if (params !== undefined && !isParams(params)) {
136
- throw new TypeError(`[router.isActiveRoute] Invalid params structure`);
137
- }
138
-
139
- // Validate strictEquality if provided
140
- if (strictEquality !== undefined && typeof strictEquality !== "boolean") {
141
- throw new TypeError(
142
- `[router.isActiveRoute] strictEquality must be a boolean, got ${typeof strictEquality}`,
143
- );
144
- }
145
-
146
- // Validate ignoreQueryParams if provided
147
- if (
148
- ignoreQueryParams !== undefined &&
149
- typeof ignoreQueryParams !== "boolean"
150
- ) {
151
- throw new TypeError(
152
- `[router.isActiveRoute] ignoreQueryParams must be a boolean, got ${typeof ignoreQueryParams}`,
153
- );
154
- }
155
- }
156
-
157
- /**
158
- * Validates forwardState/buildState arguments.
159
- */
160
- export function validateStateBuilderArgs(
161
- routeName: unknown,
162
- routeParams: unknown,
163
- methodName: string,
164
- ): void {
165
- if (!isString(routeName)) {
166
- throw new TypeError(
167
- `[router.${methodName}] Invalid routeName: ${getTypeDescription(routeName)}. Expected string.`,
168
- );
169
- }
170
-
171
- if (!isParams(routeParams)) {
172
- throw new TypeError(
173
- `[router.${methodName}] Invalid routeParams: ${getTypeDescription(routeParams)}. Expected plain object.`,
174
- );
175
- }
176
- }
177
-
178
- /**
179
- * Validates updateRoute basic arguments (name and updates object structure).
180
- * Does NOT read property values to allow caller to cache them first.
181
- */
182
- export function validateUpdateRouteBasicArgs<
183
- Dependencies extends DefaultDependencies,
184
- >(
185
- name: unknown,
186
- updates: unknown,
187
- ): asserts updates is RouteConfigUpdate<Dependencies> {
188
- // Validate name
189
- validateRouteName(name, "updateRoute");
190
-
191
- if (name === "") {
192
- throw new ReferenceError(
193
- `[router.updateRoute] Invalid name: empty string. Cannot update root node.`,
194
- );
195
- }
196
-
197
- // Validate updates is not null
198
-
199
- if (updates === null) {
200
- throw new TypeError(
201
- `[real-router] updateRoute: updates must be an object, got null`,
202
- );
203
- }
204
-
205
- // Validate updates is an object (not array)
206
- if (typeof updates !== "object" || Array.isArray(updates)) {
207
- throw new TypeError(
208
- `[real-router] updateRoute: updates must be an object, got ${getTypeDescription(updates)}`,
209
- );
210
- }
211
- }
212
-
213
- /**
214
- * Asserts that a function is not async (native or transpiled).
215
- * Checks both constructor name and toString() for __awaiter pattern.
216
- */
217
- /* v8 ignore next 12 -- @preserve: transpiled async (__awaiter) branch tested in addRoute */
218
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -- needs constructor.name access
219
- function assertNotAsync(value: Function, paramName: string): void {
220
- if (
221
- (value as { constructor: { name: string } }).constructor.name ===
222
- "AsyncFunction" ||
223
- (value as { toString: () => string }).toString().includes("__awaiter")
224
- ) {
225
- throw new TypeError(
226
- `[real-router] updateRoute: ${paramName} cannot be an async function`,
227
- );
228
- }
229
- }
230
-
231
- /**
232
- * Validates that a value is a non-async function, if provided.
233
- */
234
- function validateFunctionParam(value: unknown, paramName: string): void {
235
- if (value === undefined || value === null) {
236
- return;
237
- }
238
-
239
- if (typeof value !== "function") {
240
- throw new TypeError(
241
- `[real-router] updateRoute: ${paramName} must be a function or null, got ${typeof value}`,
242
- );
243
- }
244
-
245
- assertNotAsync(value, paramName);
246
- }
247
-
248
- /**
249
- * Validates updateRoute property types using pre-cached values.
250
- * Called AFTER properties are cached to ensure getters are called only once.
251
- */
252
- export function validateUpdateRoutePropertyTypes(
253
- forwardTo: unknown,
254
- defaultParams: unknown,
255
- decodeParams: unknown,
256
- encodeParams: unknown,
257
- ): void {
258
- // Validate forwardTo type (existence check is done by instance method)
259
- if (forwardTo !== undefined && forwardTo !== null) {
260
- if (typeof forwardTo !== "string" && typeof forwardTo !== "function") {
261
- throw new TypeError(
262
- `[real-router] updateRoute: forwardTo must be a string, function, or null, got ${getTypeDescription(forwardTo)}`,
263
- );
264
- }
265
-
266
- if (typeof forwardTo === "function") {
267
- assertNotAsync(forwardTo, "forwardTo callback");
268
- }
269
- }
270
-
271
- // Validate defaultParams
272
- if (
273
- defaultParams !== undefined &&
274
- defaultParams !== null &&
275
- (typeof defaultParams !== "object" || Array.isArray(defaultParams))
276
- ) {
277
- throw new TypeError(
278
- `[real-router] updateRoute: defaultParams must be an object or null, got ${getTypeDescription(defaultParams)}`,
279
- );
280
- }
281
-
282
- validateFunctionParam(decodeParams, "decodeParams");
283
- validateFunctionParam(encodeParams, "encodeParams");
284
- }
285
-
286
- /**
287
- * Validates buildPath arguments.
288
- */
289
- export function validateBuildPathArgs(route: unknown): asserts route is string {
290
- if (!isString(route) || route === "") {
291
- throw new TypeError(
292
- `[real-router] buildPath: route must be a non-empty string, got ${typeof route === "string" ? '""' : typeof route}`,
293
- );
294
- }
295
- }
296
-
297
- /**
298
- * Validates matchPath arguments.
299
- */
300
- export function validateMatchPathArgs(path: unknown): asserts path is string {
301
- if (!isString(path)) {
302
- throw new TypeError(
303
- `[real-router] matchPath: path must be a string, got ${typeof path}`,
304
- );
305
- }
306
- }
307
-
308
- /**
309
- * Validates shouldUpdateNode arguments.
310
- */
311
- export function validateShouldUpdateNodeArgs(
312
- nodeName: unknown,
313
- ): asserts nodeName is string {
314
- if (!isString(nodeName)) {
315
- throw new TypeError(
316
- `[router.shouldUpdateNode] nodeName must be a string, got ${typeof nodeName}`,
317
- );
318
- }
319
- }
320
-
321
- /**
322
- * Validates routes for addition to the router.
323
- * Checks parent existence, duplicates, and forwardTo targets/cycles.
324
- *
325
- * @param routes - Routes to validate
326
- * @param tree - Current route tree (optional for initial validation)
327
- * @param forwardMap - Current forward map for cycle detection
328
- * @param parentName - Optional parent route fullName for nesting via addRoute({ parent })
329
- */
330
- export function validateRoutes<Dependencies extends DefaultDependencies>(
331
- routes: Route<Dependencies>[],
332
- tree?: RouteTree,
333
- forwardMap?: Record<string, string>,
334
- parentName?: string,
335
- ): void {
336
- // Validate parent route exists in tree
337
- if (parentName && tree) {
338
- let node: RouteTree | undefined = tree;
339
-
340
- for (const segment of parentName.split(".")) {
341
- node = node.children.get(segment);
342
-
343
- if (!node) {
344
- throw new Error(
345
- `[router.addRoute] Parent route "${parentName}" does not exist`,
346
- );
347
- }
348
- }
349
- }
350
-
351
- // Tracking sets for duplicate detection
352
- const seenNames = new Set<string>();
353
- const seenPathsByParent = new Map<string, Set<string>>();
354
-
355
- for (const route of routes) {
356
- validateRoute(
357
- route,
358
- "addRoute",
359
- tree,
360
- parentName ?? "",
361
- seenNames,
362
- seenPathsByParent,
363
- );
364
- }
365
-
366
- if (tree && forwardMap) {
367
- validateForwardToTargets(routes, forwardMap, tree);
368
- }
369
- }
370
-
371
- // ============================================================================
372
- // Instance-level validators (moved from routesCrud.ts)
373
- // ============================================================================
374
-
375
- /**
376
- * Collects URL params from segments into a Set.
377
- */
378
- function collectUrlParams(segments: readonly RouteTree[]): Set<string> {
379
- const params = new Set<string>();
380
-
381
- for (const segment of segments) {
382
- for (const param of segment.paramMeta.urlParams) {
383
- params.add(param);
384
- }
385
- }
386
-
387
- return params;
388
- }
389
-
390
- /**
391
- * Validates removeRoute constraints.
392
- * Returns false if removal should be blocked (route is active).
393
- * Logs warnings for edge cases.
394
- *
395
- * @param name - Route name to remove
396
- * @param currentStateName - Current active route name (or undefined)
397
- * @param isNavigating - Whether navigation is in progress
398
- * @returns true if removal can proceed, false if blocked
399
- */
400
- export function validateRemoveRoute(
401
- name: string,
402
- currentStateName: string | undefined,
403
- isNavigating: boolean,
404
- ): boolean {
405
- // Check if trying to remove currently active route (or its parent)
406
- if (currentStateName) {
407
- const isExactMatch = currentStateName === name;
408
- const isParentOfCurrent = currentStateName.startsWith(`${name}.`);
409
-
410
- if (isExactMatch || isParentOfCurrent) {
411
- const suffix = isExactMatch ? "" : ` (current: "${currentStateName}")`;
412
-
413
- logger.warn(
414
- "router.removeRoute",
415
- `Cannot remove route "${name}" — it is currently active${suffix}. Navigate away first.`,
416
- );
417
-
418
- return false;
419
- }
420
- }
421
-
422
- // Warn if navigation is in progress (but allow removal)
423
- if (isNavigating) {
424
- logger.warn(
425
- "router.removeRoute",
426
- `Route "${name}" removed while navigation is in progress. This may cause unexpected behavior.`,
427
- );
428
- }
429
-
430
- return true;
431
- }
432
-
433
- /**
434
- * Validates clearRoutes operation.
435
- * Returns false if operation should be blocked (navigation in progress).
436
- *
437
- * @param isNavigating - Whether navigation is in progress
438
- * @returns true if clearRoutes can proceed, false if blocked
439
- */
440
- export function validateClearRoutes(isNavigating: boolean): boolean {
441
- if (isNavigating) {
442
- logger.error(
443
- "router.clearRoutes",
444
- "Cannot clear routes while navigation is in progress. Wait for navigation to complete.",
445
- );
446
-
447
- return false;
448
- }
449
-
450
- return true;
451
- }
452
-
453
- /**
454
- * Validates that forwardTo target doesn't require params that source doesn't have.
455
- *
456
- * @param sourceName - Source route name
457
- * @param targetName - Target route name
458
- * @param matcher - Current route matcher
459
- */
460
- export function validateForwardToParamCompatibility(
461
- sourceName: string,
462
- targetName: string,
463
- matcher: Matcher,
464
- ): void {
465
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
466
- const sourceSegments = matcher.getSegmentsByName(
467
- sourceName,
468
- )! as readonly RouteTree[];
469
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
470
- const targetSegments = matcher.getSegmentsByName(
471
- targetName,
472
- )! as readonly RouteTree[];
473
-
474
- // Get source URL params as a Set for O(1) lookup
475
- const sourceParams = collectUrlParams(sourceSegments);
476
-
477
- // Build target URL params array (inline — no separate helper needed)
478
- const targetParams: string[] = [];
479
-
480
- for (const segment of targetSegments) {
481
- for (const param of segment.paramMeta.urlParams) {
482
- targetParams.push(param);
483
- }
484
- }
485
-
486
- // Check if target requires params that source doesn't have
487
- const missingParams = targetParams.filter(
488
- (param) => !sourceParams.has(param),
489
- );
490
-
491
- if (missingParams.length > 0) {
492
- throw new Error(
493
- `[real-router] forwardTo target "${targetName}" requires params ` +
494
- `[${missingParams.join(", ")}] that are not available in source route "${sourceName}"`,
495
- );
496
- }
497
- }
498
-
499
- /**
500
- * Validates that adding forwardTo doesn't create a cycle.
501
- * Creates a test map with the new entry and uses resolveForwardChain
502
- * to detect cycles before any mutation happens.
503
- *
504
- * @param sourceName - Source route name
505
- * @param targetName - Target route name
506
- * @param config - Current route config (forwardMap read-only in this call)
507
- */
508
- export function validateForwardToCycle(
509
- sourceName: string,
510
- targetName: string,
511
- config: RouteConfig,
512
- ): void {
513
- // Create a test map with the new entry to validate BEFORE mutation
514
- const testMap = {
515
- ...config.forwardMap,
516
- [sourceName]: targetName,
517
- };
518
-
519
- // resolveForwardChain will throw if cycle is detected or max depth exceeded
520
- resolveForwardChain(sourceName, testMap);
521
- }
522
-
523
- /**
524
- * Validates updateRoute instance-level constraints (route existence, forwardTo).
525
- *
526
- * @param name - Route name (already validated by static method)
527
- * @param forwardTo - Cached forwardTo value
528
- * @param hasRoute - Function to check route existence
529
- * @param matcher - Current route matcher
530
- * @param config - Current route config
531
- */
532
- export function validateUpdateRoute<
533
- Dependencies extends DefaultDependencies = DefaultDependencies,
534
- >(
535
- name: string,
536
- forwardTo: string | ForwardToCallback<Dependencies> | null | undefined,
537
- hasRoute: (n: string) => boolean,
538
- matcher: Matcher,
539
- config: RouteConfig,
540
- ): void {
541
- // Validate route exists
542
- if (!hasRoute(name)) {
543
- throw new ReferenceError(
544
- `[real-router] updateRoute: route "${name}" does not exist`,
545
- );
546
- }
547
-
548
- // Validate forwardTo target exists and is valid (only for string forwardTo)
549
- if (
550
- forwardTo !== undefined &&
551
- forwardTo !== null &&
552
- typeof forwardTo === "string"
553
- ) {
554
- if (!hasRoute(forwardTo)) {
555
- throw new Error(
556
- `[real-router] updateRoute: forwardTo target "${forwardTo}" does not exist`,
557
- );
558
- }
559
-
560
- // Check forwardTo param compatibility
561
- validateForwardToParamCompatibility(name, forwardTo, matcher);
562
-
563
- // Check for cycle detection
564
- validateForwardToCycle(name, forwardTo, config);
565
- }
566
- }
@@ -1,46 +0,0 @@
1
- // packages/core/src/namespaces/StateNamespace/validators.ts
2
-
3
- /**
4
- * Static validation functions for StateNamespace.
5
- * Called by Router facade before instance methods.
6
- */
7
-
8
- import { isString, isParams, getTypeDescription } from "type-guards";
9
-
10
- /**
11
- * Validates makeState arguments.
12
- */
13
- export function validateMakeStateArgs(
14
- name: unknown,
15
- params: unknown,
16
- path: unknown,
17
- forceId: unknown,
18
- ): void {
19
- // Validate name is a string
20
- if (!isString(name)) {
21
- throw new TypeError(
22
- `[router.makeState] Invalid name: ${getTypeDescription(name)}. Expected string.`,
23
- );
24
- }
25
-
26
- // Validate params if provided
27
- if (params !== undefined && !isParams(params)) {
28
- throw new TypeError(
29
- `[router.makeState] Invalid params: ${getTypeDescription(params)}. Expected plain object.`,
30
- );
31
- }
32
-
33
- // Validate path if provided
34
- if (path !== undefined && !isString(path)) {
35
- throw new TypeError(
36
- `[router.makeState] Invalid path: ${getTypeDescription(path)}. Expected string.`,
37
- );
38
- }
39
-
40
- // Validate forceId if provided
41
- if (forceId !== undefined && typeof forceId !== "number") {
42
- throw new TypeError(
43
- `[router.makeState] Invalid forceId: ${getTypeDescription(forceId)}. Expected number.`,
44
- );
45
- }
46
- }