@real-router/core 0.56.0 → 0.57.1

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 (112) hide show
  1. package/dist/cjs/Router-Brp6_4FE.js +6 -0
  2. package/dist/cjs/Router-Brp6_4FE.js.map +1 -0
  3. package/dist/cjs/api.d.ts +1 -1
  4. package/dist/cjs/api.js +1 -1
  5. package/dist/cjs/{cloneRouter-DRieJvam.js → cloneRouter-CZx0T0RQ.js} +2 -2
  6. package/dist/cjs/{cloneRouter-DRieJvam.js.map → cloneRouter-CZx0T0RQ.js.map} +1 -1
  7. package/dist/cjs/{index-C-i6vx5Y.d.ts → index-BWUmnecT.d.ts} +1 -2
  8. package/dist/cjs/index-BWUmnecT.d.ts.map +1 -0
  9. package/dist/cjs/index-CYpAZCoc.d.ts.map +1 -1
  10. package/dist/cjs/index.d.ts +1 -1
  11. package/dist/cjs/index.js +1 -1
  12. package/dist/cjs/utils.js +1 -1
  13. package/dist/cjs/utils.js.map +1 -1
  14. package/dist/cjs/validation.d.ts +1 -1
  15. package/dist/esm/Router-LT61erYH.mjs +6 -0
  16. package/dist/esm/Router-LT61erYH.mjs.map +1 -0
  17. package/dist/esm/api.d.mts +1 -1
  18. package/dist/esm/api.mjs +1 -1
  19. package/dist/esm/{cloneRouter-DHrH6D_z.mjs → cloneRouter-DAscsmmF.mjs} +2 -2
  20. package/dist/esm/{cloneRouter-DHrH6D_z.mjs.map → cloneRouter-DAscsmmF.mjs.map} +1 -1
  21. package/dist/esm/{index-C-i6vx5Y.d.mts → index-BWUmnecT.d.mts} +1 -2
  22. package/dist/esm/index-BWUmnecT.d.mts.map +1 -0
  23. package/dist/esm/index-CYpAZCoc.d.mts.map +1 -1
  24. package/dist/esm/index.d.mts +1 -1
  25. package/dist/esm/index.mjs +1 -1
  26. package/dist/esm/utils.mjs +1 -1
  27. package/dist/esm/utils.mjs.map +1 -1
  28. package/dist/esm/validation.d.mts +1 -1
  29. package/package.json +4 -5
  30. package/dist/cjs/Router-IEGavTKk.js +0 -6
  31. package/dist/cjs/Router-IEGavTKk.js.map +0 -1
  32. package/dist/cjs/index-C-i6vx5Y.d.ts.map +0 -1
  33. package/dist/esm/Router-B3aeavRb.mjs +0 -6
  34. package/dist/esm/Router-B3aeavRb.mjs.map +0 -1
  35. package/dist/esm/index-C-i6vx5Y.d.mts.map +0 -1
  36. package/src/Router.ts +0 -737
  37. package/src/RouterError.ts +0 -324
  38. package/src/api/cloneRouter.ts +0 -159
  39. package/src/api/getDependenciesApi.ts +0 -160
  40. package/src/api/getLifecycleApi.ts +0 -65
  41. package/src/api/getPluginApi.ts +0 -228
  42. package/src/api/getRoutesApi.ts +0 -831
  43. package/src/api/helpers.ts +0 -10
  44. package/src/api/index.ts +0 -16
  45. package/src/api/types.ts +0 -12
  46. package/src/constants.ts +0 -101
  47. package/src/createRouter.ts +0 -32
  48. package/src/fsm/index.ts +0 -5
  49. package/src/fsm/routerFSM.ts +0 -130
  50. package/src/getNavigator.ts +0 -30
  51. package/src/guards.ts +0 -46
  52. package/src/helpers.ts +0 -197
  53. package/src/index.ts +0 -66
  54. package/src/internals.ts +0 -228
  55. package/src/namespaces/DependenciesNamespace/dependenciesStore.ts +0 -30
  56. package/src/namespaces/DependenciesNamespace/index.ts +0 -5
  57. package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +0 -522
  58. package/src/namespaces/EventBusNamespace/index.ts +0 -5
  59. package/src/namespaces/EventBusNamespace/types.ts +0 -11
  60. package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +0 -552
  61. package/src/namespaces/NavigationNamespace/constants.ts +0 -55
  62. package/src/namespaces/NavigationNamespace/index.ts +0 -5
  63. package/src/namespaces/NavigationNamespace/transition/completeTransition.ts +0 -108
  64. package/src/namespaces/NavigationNamespace/transition/errorHandling.ts +0 -124
  65. package/src/namespaces/NavigationNamespace/transition/guardPhase.ts +0 -283
  66. package/src/namespaces/NavigationNamespace/types.ts +0 -110
  67. package/src/namespaces/OptionsNamespace/OptionsNamespace.ts +0 -28
  68. package/src/namespaces/OptionsNamespace/constants.ts +0 -19
  69. package/src/namespaces/OptionsNamespace/helpers.ts +0 -50
  70. package/src/namespaces/OptionsNamespace/index.ts +0 -7
  71. package/src/namespaces/OptionsNamespace/validators.ts +0 -13
  72. package/src/namespaces/PluginsNamespace/PluginsNamespace.ts +0 -291
  73. package/src/namespaces/PluginsNamespace/constants.ts +0 -34
  74. package/src/namespaces/PluginsNamespace/index.ts +0 -7
  75. package/src/namespaces/PluginsNamespace/types.ts +0 -22
  76. package/src/namespaces/PluginsNamespace/validators.ts +0 -28
  77. package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +0 -558
  78. package/src/namespaces/RouteLifecycleNamespace/index.ts +0 -5
  79. package/src/namespaces/RouteLifecycleNamespace/types.ts +0 -10
  80. package/src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts +0 -81
  81. package/src/namespaces/RouterLifecycleNamespace/constants.ts +0 -25
  82. package/src/namespaces/RouterLifecycleNamespace/index.ts +0 -5
  83. package/src/namespaces/RouterLifecycleNamespace/types.ts +0 -30
  84. package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +0 -582
  85. package/src/namespaces/RoutesNamespace/constants.ts +0 -6
  86. package/src/namespaces/RoutesNamespace/forwardChain.ts +0 -34
  87. package/src/namespaces/RoutesNamespace/helpers.ts +0 -204
  88. package/src/namespaces/RoutesNamespace/index.ts +0 -11
  89. package/src/namespaces/RoutesNamespace/routeGuards.ts +0 -62
  90. package/src/namespaces/RoutesNamespace/routesStore.ts +0 -566
  91. package/src/namespaces/RoutesNamespace/types.ts +0 -81
  92. package/src/namespaces/StateNamespace/StateNamespace.ts +0 -224
  93. package/src/namespaces/StateNamespace/helpers.ts +0 -24
  94. package/src/namespaces/StateNamespace/index.ts +0 -5
  95. package/src/namespaces/StateNamespace/types.ts +0 -15
  96. package/src/namespaces/index.ts +0 -35
  97. package/src/stateMetaStore.ts +0 -15
  98. package/src/transitionPath.ts +0 -440
  99. package/src/typeGuards.ts +0 -59
  100. package/src/types/RouterValidator.ts +0 -156
  101. package/src/types.ts +0 -77
  102. package/src/utils/createRequestScope.ts +0 -174
  103. package/src/utils/getStaticPaths.ts +0 -50
  104. package/src/utils/hydrateRouter.ts +0 -89
  105. package/src/utils/index.ts +0 -27
  106. package/src/utils/serializeRouterState.ts +0 -120
  107. package/src/utils/serializeState.ts +0 -63
  108. package/src/validation.ts +0 -12
  109. package/src/wiring/RouterWiringBuilder.ts +0 -275
  110. package/src/wiring/index.ts +0 -7
  111. package/src/wiring/types.ts +0 -47
  112. package/src/wiring/wireRouter.ts +0 -26
@@ -1,10 +0,0 @@
1
- // packages/core/src/api/helpers.ts
2
-
3
- import { errorCodes } from "../constants";
4
- import { RouterError } from "../RouterError";
5
-
6
- export function throwIfDisposed(isDisposed: () => boolean): void {
7
- if (isDisposed()) {
8
- throw new RouterError(errorCodes.ROUTER_DISPOSED);
9
- }
10
- }
package/src/api/index.ts DELETED
@@ -1,16 +0,0 @@
1
- export { getPluginApi } from "./getPluginApi";
2
-
3
- export { getRoutesApi } from "./getRoutesApi";
4
-
5
- export { getDependenciesApi } from "./getDependenciesApi";
6
-
7
- export { getLifecycleApi } from "./getLifecycleApi";
8
-
9
- export { cloneRouter } from "./cloneRouter";
10
-
11
- export type {
12
- PluginApi,
13
- RoutesApi,
14
- DependenciesApi,
15
- LifecycleApi,
16
- } from "./types";
package/src/api/types.ts DELETED
@@ -1,12 +0,0 @@
1
- import type { PluginApi as BasePluginApi } from "@real-router/types";
2
- import type { RouteTree } from "route-tree";
3
-
4
- export interface PluginApi extends Omit<BasePluginApi, "getTree"> {
5
- getTree: () => RouteTree;
6
- }
7
-
8
- export {
9
- type RoutesApi,
10
- type LifecycleApi,
11
- type DependenciesApi,
12
- } from "@real-router/types";
package/src/constants.ts DELETED
@@ -1,101 +0,0 @@
1
- // packages/core/src/constants.ts
2
-
3
- import type {
4
- EventToNameMap,
5
- EventToPluginMap,
6
- ErrorCodeToValueMap,
7
- ErrorCodeKeys,
8
- ErrorCodeValues,
9
- TransitionMeta,
10
- } from "@real-router/types";
11
-
12
- export type ConstantsKeys = "UNKNOWN_ROUTE";
13
-
14
- export type Constants = Record<ConstantsKeys, string>;
15
-
16
- // =============================================================================
17
- // Error Codes (migrated from router-error)
18
- // =============================================================================
19
-
20
- export type ErrorCodes = Record<ErrorCodeKeys, ErrorCodeValues>;
21
-
22
- /**
23
- * Error codes for router operations.
24
- * Used to identify specific failure scenarios in navigation and lifecycle.
25
- * Frozen to prevent accidental modifications.
26
- */
27
- export const errorCodes: ErrorCodeToValueMap = Object.freeze({
28
- ROUTER_NOT_STARTED: "NOT_STARTED", // navigate() called before start()
29
- NO_START_PATH_OR_STATE: "NO_START_PATH_OR_STATE", // start() without initial route
30
- ROUTER_ALREADY_STARTED: "ALREADY_STARTED", // start() called twice
31
- ROUTE_NOT_FOUND: "ROUTE_NOT_FOUND", // Navigation to non-existent route
32
- SAME_STATES: "SAME_STATES", // Navigate to current route without reload
33
- CANNOT_DEACTIVATE: "CANNOT_DEACTIVATE", // canDeactivate guard blocked navigation
34
- CANNOT_ACTIVATE: "CANNOT_ACTIVATE", // canActivate guard blocked navigation
35
- TRANSITION_ERR: "TRANSITION_ERR", // Generic transition failure
36
- TRANSITION_CANCELLED: "CANCELLED", // Navigation cancelled by user or new navigation
37
- ROUTER_DISPOSED: "DISPOSED", // Router has been disposed
38
- PLUGIN_CONFLICT: "PLUGIN_CONFLICT", // Plugin tried to extend router with already-existing property
39
- CONTEXT_NAMESPACE_ALREADY_CLAIMED: "CONTEXT_NAMESPACE_ALREADY_CLAIMED", // Plugin tried to claim a context namespace already owned by another plugin
40
- });
41
-
42
- /**
43
- * General router constants.
44
- * Special route names and identifiers.
45
- */
46
- export const UNKNOWN_ROUTE = "@@router/UNKNOWN_ROUTE";
47
-
48
- export const constants: Constants = {
49
- UNKNOWN_ROUTE,
50
- };
51
-
52
- /**
53
- * Plugin method names.
54
- * Maps to methods that plugins can implement to hook into router lifecycle.
55
- */
56
- export const plugins: EventToPluginMap = {
57
- ROUTER_START: "onStart", // Plugin method called when router starts
58
- ROUTER_STOP: "onStop", // Plugin method called when router stops
59
- TRANSITION_START: "onTransitionStart", // Plugin method called when navigation begins
60
- TRANSITION_LEAVE_APPROVE: "onTransitionLeaveApprove", // Plugin method called when deactivation guards pass
61
- TRANSITION_CANCEL: "onTransitionCancel", // Plugin method called when navigation cancelled
62
- TRANSITION_SUCCESS: "onTransitionSuccess", // Plugin method called when navigation succeeds
63
- TRANSITION_ERROR: "onTransitionError", // Plugin method called when navigation fails
64
- };
65
-
66
- /**
67
- * Event names for router event system.
68
- * Used with addEventListener/removeEventListener for reactive subscriptions.
69
- */
70
- export const events: EventToNameMap = {
71
- ROUTER_START: "$start", // Emitted when router.start() succeeds
72
- ROUTER_STOP: "$stop", // Emitted when router.stop() is called
73
- TRANSITION_START: "$$start", // Emitted when navigation begins
74
- TRANSITION_LEAVE_APPROVE: "$$leaveApprove", // Emitted when deactivation guards pass
75
- TRANSITION_CANCEL: "$$cancel", // Emitted when navigation is cancelled
76
- TRANSITION_SUCCESS: "$$success", // Emitted when navigation completes successfully
77
- TRANSITION_ERROR: "$$error", // Emitted when navigation fails
78
- };
79
-
80
- export const DEFAULT_LIMITS = {
81
- maxDependencies: 100,
82
- maxPlugins: 50,
83
- maxListeners: 10_000,
84
- warnListeners: 1000,
85
- maxEventDepth: 5,
86
- maxLifecycleHandlers: 200,
87
- } as const;
88
-
89
- export const EMPTY_PARAMS: Readonly<Record<string, never>> = Object.freeze({});
90
-
91
- const FROZEN_EMPTY_SEGMENTS = Object.freeze({
92
- deactivated: Object.freeze([]) as unknown as string[],
93
- activated: Object.freeze([]) as unknown as string[],
94
- intersection: "",
95
- });
96
-
97
- export const DEFAULT_TRANSITION = Object.freeze({
98
- phase: "activating",
99
- reason: "success",
100
- segments: FROZEN_EMPTY_SEGMENTS,
101
- }) as TransitionMeta;
@@ -1,32 +0,0 @@
1
- // packages/core/src/createRouter.ts
2
-
3
- import { Router } from "./Router";
4
-
5
- import type { Route } from "./types";
6
- import type { DefaultDependencies, Options } from "@real-router/types";
7
-
8
- /**
9
- * Creates a new router instance.
10
- *
11
- * @param routes - Array of route definitions
12
- * @param options - Router configuration options
13
- * @param dependencies - Dependencies to inject into the router
14
- * @returns A new Router instance
15
- *
16
- * @example
17
- * const router = createRouter([
18
- * { name: 'home', path: '/' },
19
- * { name: 'users', path: '/users' },
20
- * ]);
21
- *
22
- * router.start('/');
23
- */
24
- export const createRouter = <
25
- Dependencies extends DefaultDependencies = DefaultDependencies,
26
- >(
27
- routes: Route<Dependencies>[] = [],
28
- options: Partial<Options> = {},
29
- dependencies: Dependencies = {} as Dependencies,
30
- ): Router<Dependencies> => {
31
- return new Router<Dependencies>(routes, options, dependencies);
32
- };
package/src/fsm/index.ts DELETED
@@ -1,5 +0,0 @@
1
- // packages/core/src/fsm/index.ts
2
-
3
- export { createRouterFSM, routerStates, routerEvents } from "./routerFSM";
4
-
5
- export type { RouterEvent, RouterPayloads, RouterState } from "./routerFSM";
@@ -1,130 +0,0 @@
1
- // packages/core/src/fsm/routerFSM.ts
2
-
3
- import { FSM } from "@real-router/fsm";
4
-
5
- import type { FSMConfig } from "@real-router/fsm";
6
-
7
- /**
8
- * Router FSM states.
9
- *
10
- * - IDLE: Router not started or stopped
11
- * - STARTING: Router is initializing
12
- * - READY: Router is ready for navigation
13
- * - TRANSITION_STARTED: Navigation in progress (before deactivation guards)
14
- * - LEAVE_APPROVED: Deactivation guards passed, activation guards pending
15
- * - DISPOSED: Router has been disposed (R2+)
16
- */
17
- export const routerStates = {
18
- IDLE: "IDLE",
19
- STARTING: "STARTING",
20
- READY: "READY",
21
- TRANSITION_STARTED: "TRANSITION_STARTED",
22
- LEAVE_APPROVED: "LEAVE_APPROVED",
23
- DISPOSED: "DISPOSED",
24
- } as const;
25
-
26
- export type RouterState = (typeof routerStates)[keyof typeof routerStates];
27
-
28
- /**
29
- * Router FSM events.
30
- *
31
- * - START: Begin router initialization
32
- * - STARTED: Router initialization complete
33
- * - NAVIGATE: Begin navigation
34
- * - COMPLETE: Navigation completed successfully
35
- * - FAIL: Navigation or initialization failed
36
- * - CANCEL: Navigation cancelled
37
- * - STOP: Stop router
38
- * - DISPOSE: Dispose router (R2+)
39
- */
40
- export const routerEvents = {
41
- START: "START",
42
- STARTED: "STARTED",
43
- NAVIGATE: "NAVIGATE",
44
- LEAVE_APPROVE: "LEAVE_APPROVE",
45
- COMPLETE: "COMPLETE",
46
- FAIL: "FAIL",
47
- CANCEL: "CANCEL",
48
- STOP: "STOP",
49
- DISPOSE: "DISPOSE",
50
- } as const;
51
-
52
- export type RouterEvent = (typeof routerEvents)[keyof typeof routerEvents];
53
-
54
- /**
55
- * Typed payloads for router FSM events.
56
- *
57
- * Events without entries have no payload.
58
- */
59
- // eslint-disable-next-line @typescript-eslint/no-empty-object-type -- payloads stored in EventBusNamespace fields (N8+N9 optimization)
60
- export interface RouterPayloads {}
61
-
62
- /**
63
- * Router FSM configuration.
64
- *
65
- * Transitions:
66
- * - IDLE → STARTING (START), DISPOSED (DISPOSE)
67
- * - STARTING → READY (STARTED), IDLE (FAIL), DISPOSED (DISPOSE)
68
- * - READY → TRANSITION_STARTED (NAVIGATE), READY (FAIL, self-loop for early validation errors), IDLE (STOP), DISPOSED (DISPOSE)
69
- * - TRANSITION_STARTED → LEAVE_APPROVED (LEAVE_APPROVE), TRANSITION_STARTED (NAVIGATE, self-loop), READY (CANCEL, FAIL), DISPOSED (DISPOSE)
70
- * - LEAVE_APPROVED → READY (COMPLETE, CANCEL, FAIL), TRANSITION_STARTED (NAVIGATE), DISPOSED (DISPOSE)
71
- * - DISPOSED → (no transitions)
72
- *
73
- * DISPOSE is wired from every non-DISPOSED state so `router.dispose()` always
74
- * settles the FSM at DISPOSED. The facade orchestrates cleanup through IDLE
75
- * for healthy flows; the direct transitions guarantee the FSM is not left
76
- * stuck if cleanup is skipped (e.g. dispose mid-STARTING when the start
77
- * pipeline threw before STARTED/FAIL).
78
- */
79
- const routerFSMConfig: FSMConfig<RouterState, RouterEvent, null> = {
80
- initial: routerStates.IDLE,
81
- context: null,
82
- transitions: {
83
- [routerStates.IDLE]: {
84
- [routerEvents.START]: routerStates.STARTING,
85
- [routerEvents.DISPOSE]: routerStates.DISPOSED,
86
- },
87
- [routerStates.STARTING]: {
88
- [routerEvents.STARTED]: routerStates.READY,
89
- [routerEvents.FAIL]: routerStates.IDLE,
90
- [routerEvents.DISPOSE]: routerStates.DISPOSED,
91
- },
92
- [routerStates.READY]: {
93
- [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,
94
- [routerEvents.FAIL]: routerStates.READY,
95
- [routerEvents.STOP]: routerStates.IDLE,
96
- [routerEvents.DISPOSE]: routerStates.DISPOSED,
97
- },
98
- [routerStates.TRANSITION_STARTED]: {
99
- [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,
100
- [routerEvents.LEAVE_APPROVE]: routerStates.LEAVE_APPROVED,
101
- [routerEvents.CANCEL]: routerStates.READY,
102
- [routerEvents.FAIL]: routerStates.READY,
103
- [routerEvents.DISPOSE]: routerStates.DISPOSED,
104
- },
105
- [routerStates.LEAVE_APPROVED]: {
106
- [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,
107
- [routerEvents.COMPLETE]: routerStates.READY,
108
- [routerEvents.CANCEL]: routerStates.READY,
109
- [routerEvents.FAIL]: routerStates.READY,
110
- [routerEvents.DISPOSE]: routerStates.DISPOSED,
111
- },
112
- [routerStates.DISPOSED]: {},
113
- },
114
- };
115
-
116
- /**
117
- * Factory function to create a router FSM instance.
118
- *
119
- * @returns FSM instance with initial state "IDLE"
120
- */
121
- export function createRouterFSM(): FSM<
122
- RouterState,
123
- RouterEvent,
124
- null,
125
- RouterPayloads
126
- > {
127
- return new FSM<RouterState, RouterEvent, null, RouterPayloads>(
128
- routerFSMConfig,
129
- );
130
- }
@@ -1,30 +0,0 @@
1
- import type {
2
- Navigator,
3
- DefaultDependencies,
4
- Router,
5
- } from "@real-router/types";
6
-
7
- const cache = new WeakMap<Router, Navigator>();
8
-
9
- export const getNavigator = <
10
- Dependencies extends DefaultDependencies = DefaultDependencies,
11
- >(
12
- router: Router<Dependencies>,
13
- ): Navigator => {
14
- let nav = cache.get(router);
15
-
16
- if (!nav) {
17
- nav = Object.freeze({
18
- navigate: router.navigate,
19
- getState: router.getState,
20
- isActiveRoute: router.isActiveRoute,
21
- canNavigateTo: router.canNavigateTo,
22
- subscribe: router.subscribe,
23
- subscribeLeave: router.subscribeLeave,
24
- isLeaveApproved: router.isLeaveApproved,
25
- } as Navigator);
26
- cache.set(router, nav);
27
- }
28
-
29
- return nav;
30
- };
package/src/guards.ts DELETED
@@ -1,46 +0,0 @@
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 DELETED
@@ -1,197 +0,0 @@
1
- // packages/core/src/helpers.ts
2
-
3
- import { DEFAULT_LIMITS } from "./constants";
4
-
5
- import type { Limits } from "./types";
6
- import type { Params, State, LimitsConfig } from "@real-router/types";
7
-
8
- // =============================================================================
9
- // State Helpers
10
- // =============================================================================
11
-
12
- /**
13
- * Structural type guard for State object.
14
- * Only checks required fields exist with correct types.
15
- * Does NOT validate params serializability (allows circular refs).
16
- *
17
- * Use `isState` from type-guards for full validation (serializable params).
18
- * Use this for internal operations like deepFreezeState that handle any object structure.
19
- *
20
- * @param value - Value to check
21
- * @returns true if value has State structure
22
- * @internal
23
- */
24
- function isStateStructural(value: unknown): value is State {
25
- if (value === null || typeof value !== "object") {
26
- return false;
27
- }
28
-
29
- const obj = value as Record<string, unknown>;
30
-
31
- return (
32
- typeof obj.name === "string" &&
33
- typeof obj.path === "string" &&
34
- typeof obj.params === "object" &&
35
- obj.params !== null
36
- );
37
- }
38
-
39
- /**
40
- * Deep freezes State object to prevent mutations.
41
- * Creates a deep clone first, then recursively freezes the clone and all nested objects.
42
- * Uses simple recursive freezing after cloning (no need for WeakSet since clone has no circular refs).
43
- *
44
- * @param state - The State object to freeze
45
- * @returns A frozen deep clone of the state
46
- * @throws {TypeError} If state is not a valid State object
47
- *
48
- * @example
49
- * const state = { name: 'home', params: {}, path: '/' };
50
- * const frozen = deepFreezeState(state);
51
- * // frozen.params is now immutable
52
- * // original state is unchanged
53
- */
54
- export function deepFreezeState<T extends State>(state: T): T {
55
- // Early return for null/undefined
56
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
57
- if (!state) {
58
- return state;
59
- }
60
-
61
- // Validate State structure (structural check, allows circular refs)
62
- if (!isStateStructural(state)) {
63
- throw new TypeError(
64
- `[deepFreezeState] Expected valid State object, got: ${typeof state}`,
65
- );
66
- }
67
-
68
- // Create a deep clone to avoid mutating the original
69
- // structuredClone preserves circular references, so we need to track visited objects
70
- const clonedState = structuredClone(state);
71
-
72
- // WeakSet to track visited objects (prevent infinite recursion with circular refs)
73
- const visited = new WeakSet<object>();
74
-
75
- // Recursive freeze function with circular reference protection
76
- function freezeClonedRecursive(obj: unknown): void {
77
- // Skip primitives, null, undefined
78
- // Note: typeof undefined === "undefined" !== "object", so checking undefined is redundant
79
- if (obj === null || typeof obj !== "object") {
80
- return;
81
- }
82
-
83
- // Skip already visited objects (circular reference protection)
84
- if (visited.has(obj)) {
85
- return;
86
- }
87
-
88
- // Mark as visited
89
- visited.add(obj);
90
-
91
- // Freeze the object/array itself
92
- Object.freeze(obj);
93
-
94
- // Iterate without Object.values() allocation
95
- if (Array.isArray(obj)) {
96
- for (const item of obj) {
97
- freezeClonedRecursive(item);
98
- }
99
- } else {
100
- for (const key in obj) {
101
- freezeClonedRecursive((obj as Record<string, unknown>)[key]);
102
- }
103
- }
104
- }
105
-
106
- // Freeze the entire cloned state tree
107
- freezeClonedRecursive(clonedState);
108
-
109
- return clonedState;
110
- }
111
-
112
- /**
113
- * Shallow-freezes a State object in place.
114
- *
115
- * Freezes only the top-level State object (blocks reassignment of `name`,
116
- * `params`, `path`, `transition`, `context`). Nested objects (`params`,
117
- * `transition`, `transition.segments`, `transition.segments.{deactivated,activated}`)
118
- * are expected to be **already frozen at creation time** by their producers:
119
- *
120
- * - `params` frozen in `makeState()` / `navigateToNotFound()`
121
- * - `transition`, `segments`, `deactivated`, `activated` frozen in
122
- * `buildTransitionMeta()` (or inline in `navigateToNotFound()`)
123
- *
124
- * `state.context` is **intentionally not frozen** — plugins write to it via
125
- * `claim.write(state, value)` after state creation.
126
- *
127
- * @internal
128
- */
129
- export function freezeStateInPlace<T extends State>(state: T): T {
130
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive guard against external misuse
131
- if (!state) {
132
- return state;
133
- }
134
-
135
- return Object.freeze(state);
136
- }
137
-
138
- /**
139
- * Merges user limits with defaults.
140
- * Returns frozen object for immutability.
141
- */
142
- export function createLimits(userLimits: Partial<LimitsConfig> = {}): Limits {
143
- return { ...DEFAULT_LIMITS, ...userLimits };
144
- }
145
-
146
- // =============================================================================
147
- // Params Helpers
148
- // =============================================================================
149
-
150
- /**
151
- * Strips `undefined` values from a params object before handoff to the query
152
- * string engine and state storage.
153
- *
154
- * **Why this exists:** `router.navigate(name, { x: undefined })` must not put
155
- * `x` into the resulting URL (publicly documented contract). The underlying
156
- * query engine (`search-params`) already does this, but the contract belongs
157
- * to `@real-router/core` — this function guarantees it at the core boundary
158
- * so that:
159
- * - Plugin interceptors on `forwardState` that inject `undefined` values are
160
- * caught before they reach the engine
161
- * - `state.params` never contains `undefined` values (roundtrip consistent
162
- * with URL)
163
- * - The contract is verifiable at core's own test surface (doesn't depend on
164
- * engine behavior for regression detection)
165
- *
166
- * Single pass. Always returns a fresh object when input is defined
167
- * (reference identity is not preserved — callers must not rely on it).
168
- */
169
- export function normalizeParams(params: Params): Params;
170
-
171
- export function normalizeParams(params: undefined): undefined;
172
-
173
- export function normalizeParams(params: Params | undefined): Params | undefined;
174
-
175
- export function normalizeParams(
176
- params: Params | undefined,
177
- ): Params | undefined {
178
- if (params === undefined) {
179
- return params;
180
- }
181
-
182
- const normalized: Params = {};
183
-
184
- for (const key in params) {
185
- if (!Object.hasOwn(params, key)) {
186
- continue;
187
- }
188
-
189
- const value = params[key];
190
-
191
- if (value !== undefined) {
192
- normalized[key] = value;
193
- }
194
- }
195
-
196
- return normalized;
197
- }
package/src/index.ts DELETED
@@ -1,66 +0,0 @@
1
- // packages/core/src/index.ts
2
-
3
- // Router-dependent types (re-exported from @real-router/types)
4
-
5
- export type {
6
- BuildStateResultWithSegments,
7
- GuardFnFactory,
8
- PluginFactory,
9
- Route,
10
- RouteConfigUpdate,
11
- } from "./types";
12
-
13
- export type { RouterValidator } from "./types/RouterValidator";
14
-
15
- // Router class (replaces Router interface from core-types)
16
- export { Router } from "./Router";
17
-
18
- // Types (re-exported from core-types - no Router dependency)
19
- export type {
20
- Config,
21
- DefaultDependencies,
22
- GuardFn,
23
- Listener,
24
- Navigator,
25
- NavigationOptions,
26
- Options,
27
- Params,
28
- Plugin,
29
- SimpleState,
30
- State,
31
- SubscribeFn,
32
- SubscribeState,
33
- Subscription,
34
- Unsubscribe,
35
- } from "@real-router/types";
36
-
37
- // Route-tree mutation event payloads (observed via getRoutesApi().subscribeChanges)
38
- export type {
39
- TreeChangedEvent,
40
- TreeChangedAdd,
41
- TreeChangedRemove,
42
- TreeChangedUpdate,
43
- TreeChangedReplace,
44
- TreeChangedClear,
45
- TreeStructuralPatch,
46
- } from "@real-router/types";
47
-
48
- export type { ErrorCodes, Constants } from "./constants";
49
-
50
- export { events, constants, errorCodes, UNKNOWN_ROUTE } from "./constants";
51
-
52
- // RouterError class (migrated from router-error package)
53
- export { RouterError } from "./RouterError";
54
-
55
- // Re-exported so end users can `instanceof RecursionDepthError` at CRUD call
56
- // sites — the only error that escapes a `subscribeChanges` handler (depth-limit
57
- // overflow propagates, unlike ordinary listener errors which are isolated).
58
- export { RecursionDepthError } from "event-emitter";
59
-
60
- export { createRouter } from "./createRouter";
61
-
62
- export { getNavigator } from "./getNavigator";
63
-
64
- export { resolveForwardChain } from "./namespaces/RoutesNamespace/forwardChain";
65
-
66
- export type { RouteTree } from "route-tree";