@real-router/core 0.55.0 → 0.57.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/dist/cjs/Router-BSGzVINO.js +6 -0
- package/dist/cjs/Router-BSGzVINO.js.map +1 -0
- package/dist/{esm/Router-Dg-zk8AS.d.mts → cjs/Router-hW6ivqrX.d.ts} +2 -2
- package/dist/cjs/Router-hW6ivqrX.d.ts.map +1 -0
- package/dist/cjs/api.d.ts +2 -2
- package/dist/cjs/api.d.ts.map +1 -1
- package/dist/cjs/api.js +1 -1
- package/dist/cjs/api.js.map +1 -1
- package/dist/cjs/{cloneRouter-C9Rth_8U.js → cloneRouter-7z-60z_f.js} +2 -2
- package/dist/cjs/{cloneRouter-C9Rth_8U.js.map → cloneRouter-7z-60z_f.js.map} +1 -1
- package/dist/cjs/{index-C-i6vx5Y.d.ts → index-BWUmnecT.d.ts} +1 -2
- package/dist/cjs/index-BWUmnecT.d.ts.map +1 -0
- package/dist/cjs/{RouterError-WhCzIWuc.d.ts → index-CYpAZCoc.d.ts} +19 -2
- package/dist/cjs/index-CYpAZCoc.d.ts.map +1 -0
- package/dist/cjs/{index-K1U_fqfJ.d.ts → index-D2WRiyWS.d.ts} +2 -2
- package/dist/cjs/index-D2WRiyWS.d.ts.map +1 -0
- package/dist/cjs/index.d.ts +5 -5
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/{internals-CWMOL1B8.js → internals-DJjgSePy.js} +2 -2
- package/dist/cjs/internals-DJjgSePy.js.map +1 -0
- package/dist/cjs/utils.d.ts +1 -1
- package/dist/cjs/utils.js +1 -1
- package/dist/cjs/utils.js.map +1 -1
- package/dist/cjs/validation.d.ts +17 -5
- package/dist/cjs/validation.d.ts.map +1 -1
- package/dist/cjs/validation.js +1 -1
- package/dist/esm/Router-B7txWo9N.mjs +6 -0
- package/dist/esm/Router-B7txWo9N.mjs.map +1 -0
- package/dist/{cjs/Router-Dg-zk8AS.d.ts → esm/Router-hW6ivqrX.d.mts} +2 -2
- package/dist/esm/Router-hW6ivqrX.d.mts.map +1 -0
- package/dist/esm/api.d.mts +2 -2
- package/dist/esm/api.d.mts.map +1 -1
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/api.mjs.map +1 -1
- package/dist/esm/{cloneRouter-BYNiwchg.mjs → cloneRouter-BNCQ7tIa.mjs} +2 -2
- package/dist/esm/{cloneRouter-BYNiwchg.mjs.map → cloneRouter-BNCQ7tIa.mjs.map} +1 -1
- package/dist/esm/{index-C-i6vx5Y.d.mts → index-BWUmnecT.d.mts} +1 -2
- package/dist/esm/index-BWUmnecT.d.mts.map +1 -0
- package/dist/esm/{RouterError-WhCzIWuc.d.mts → index-CYpAZCoc.d.mts} +19 -2
- package/dist/esm/index-CYpAZCoc.d.mts.map +1 -0
- package/dist/esm/{index-DKzxav48.d.mts → index-CjWKWPY6.d.mts} +2 -2
- package/dist/esm/index-CjWKWPY6.d.mts.map +1 -0
- package/dist/esm/index.d.mts +5 -5
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/{internals-DT4mneSz.mjs → internals-C8mRvTxc.mjs} +2 -2
- package/dist/esm/internals-C8mRvTxc.mjs.map +1 -0
- package/dist/esm/utils.d.mts +1 -1
- package/dist/esm/utils.mjs +1 -1
- package/dist/esm/utils.mjs.map +1 -1
- package/dist/esm/validation.d.mts +17 -5
- package/dist/esm/validation.d.mts.map +1 -1
- package/dist/esm/validation.mjs +1 -1
- package/package.json +3 -4
- package/dist/cjs/Router-C7eE1kIK.js +0 -6
- package/dist/cjs/Router-C7eE1kIK.js.map +0 -1
- package/dist/cjs/Router-Dg-zk8AS.d.ts.map +0 -1
- package/dist/cjs/RouterError-WhCzIWuc.d.ts.map +0 -1
- package/dist/cjs/index-C-i6vx5Y.d.ts.map +0 -1
- package/dist/cjs/index-K1U_fqfJ.d.ts.map +0 -1
- package/dist/cjs/internals-CWMOL1B8.js.map +0 -1
- package/dist/esm/Router-Dg-zk8AS.d.mts.map +0 -1
- package/dist/esm/Router-DiZbYMLx.mjs +0 -6
- package/dist/esm/Router-DiZbYMLx.mjs.map +0 -1
- package/dist/esm/RouterError-WhCzIWuc.d.mts.map +0 -1
- package/dist/esm/index-C-i6vx5Y.d.mts.map +0 -1
- package/dist/esm/index-DKzxav48.d.mts.map +0 -1
- package/dist/esm/internals-DT4mneSz.mjs.map +0 -1
- package/src/Router.ts +0 -725
- package/src/RouterError.ts +0 -324
- package/src/api/cloneRouter.ts +0 -159
- package/src/api/getDependenciesApi.ts +0 -160
- package/src/api/getLifecycleApi.ts +0 -65
- package/src/api/getPluginApi.ts +0 -228
- package/src/api/getRoutesApi.ts +0 -546
- package/src/api/helpers.ts +0 -10
- package/src/api/index.ts +0 -16
- package/src/api/types.ts +0 -12
- package/src/constants.ts +0 -101
- package/src/createRouter.ts +0 -32
- package/src/fsm/index.ts +0 -5
- package/src/fsm/routerFSM.ts +0 -130
- package/src/getNavigator.ts +0 -30
- package/src/guards.ts +0 -46
- package/src/helpers.ts +0 -197
- package/src/index.ts +0 -50
- package/src/internals.ts +0 -200
- package/src/namespaces/DependenciesNamespace/dependenciesStore.ts +0 -30
- package/src/namespaces/DependenciesNamespace/index.ts +0 -5
- package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +0 -485
- package/src/namespaces/EventBusNamespace/index.ts +0 -5
- package/src/namespaces/EventBusNamespace/types.ts +0 -11
- package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +0 -552
- package/src/namespaces/NavigationNamespace/constants.ts +0 -55
- package/src/namespaces/NavigationNamespace/index.ts +0 -5
- package/src/namespaces/NavigationNamespace/transition/completeTransition.ts +0 -108
- package/src/namespaces/NavigationNamespace/transition/errorHandling.ts +0 -124
- package/src/namespaces/NavigationNamespace/transition/guardPhase.ts +0 -283
- package/src/namespaces/NavigationNamespace/types.ts +0 -110
- package/src/namespaces/OptionsNamespace/OptionsNamespace.ts +0 -28
- package/src/namespaces/OptionsNamespace/constants.ts +0 -19
- package/src/namespaces/OptionsNamespace/helpers.ts +0 -50
- package/src/namespaces/OptionsNamespace/index.ts +0 -7
- package/src/namespaces/OptionsNamespace/validators.ts +0 -13
- package/src/namespaces/PluginsNamespace/PluginsNamespace.ts +0 -291
- package/src/namespaces/PluginsNamespace/constants.ts +0 -34
- package/src/namespaces/PluginsNamespace/index.ts +0 -7
- package/src/namespaces/PluginsNamespace/types.ts +0 -22
- package/src/namespaces/PluginsNamespace/validators.ts +0 -28
- package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +0 -558
- package/src/namespaces/RouteLifecycleNamespace/index.ts +0 -5
- package/src/namespaces/RouteLifecycleNamespace/types.ts +0 -10
- package/src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts +0 -81
- package/src/namespaces/RouterLifecycleNamespace/constants.ts +0 -25
- package/src/namespaces/RouterLifecycleNamespace/index.ts +0 -5
- package/src/namespaces/RouterLifecycleNamespace/types.ts +0 -30
- package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +0 -582
- package/src/namespaces/RoutesNamespace/constants.ts +0 -6
- package/src/namespaces/RoutesNamespace/forwardChain.ts +0 -34
- package/src/namespaces/RoutesNamespace/helpers.ts +0 -204
- package/src/namespaces/RoutesNamespace/index.ts +0 -11
- package/src/namespaces/RoutesNamespace/routeGuards.ts +0 -62
- package/src/namespaces/RoutesNamespace/routesStore.ts +0 -566
- package/src/namespaces/RoutesNamespace/types.ts +0 -81
- package/src/namespaces/StateNamespace/StateNamespace.ts +0 -224
- package/src/namespaces/StateNamespace/helpers.ts +0 -24
- package/src/namespaces/StateNamespace/index.ts +0 -5
- package/src/namespaces/StateNamespace/types.ts +0 -15
- package/src/namespaces/index.ts +0 -35
- package/src/stateMetaStore.ts +0 -15
- package/src/transitionPath.ts +0 -436
- package/src/typeGuards.ts +0 -59
- package/src/types/RouterValidator.ts +0 -156
- package/src/types.ts +0 -69
- package/src/utils/createRequestScope.ts +0 -174
- package/src/utils/getStaticPaths.ts +0 -50
- package/src/utils/hydrateRouter.ts +0 -89
- package/src/utils/index.ts +0 -27
- package/src/utils/serializeRouterState.ts +0 -120
- package/src/utils/serializeState.ts +0 -63
- package/src/validation.ts +0 -12
- package/src/wiring/RouterWiringBuilder.ts +0 -275
- package/src/wiring/index.ts +0 -7
- package/src/wiring/types.ts +0 -47
- package/src/wiring/wireRouter.ts +0 -26
package/src/createRouter.ts
DELETED
|
@@ -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
package/src/fsm/routerFSM.ts
DELETED
|
@@ -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
|
-
}
|
package/src/getNavigator.ts
DELETED
|
@@ -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,50 +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
|
-
export type { ErrorCodes, Constants } from "./constants";
|
|
38
|
-
|
|
39
|
-
export { events, constants, errorCodes, UNKNOWN_ROUTE } from "./constants";
|
|
40
|
-
|
|
41
|
-
// RouterError class (migrated from router-error package)
|
|
42
|
-
export { RouterError } from "./RouterError";
|
|
43
|
-
|
|
44
|
-
export { createRouter } from "./createRouter";
|
|
45
|
-
|
|
46
|
-
export { getNavigator } from "./getNavigator";
|
|
47
|
-
|
|
48
|
-
export { resolveForwardChain } from "./namespaces/RoutesNamespace/forwardChain";
|
|
49
|
-
|
|
50
|
-
export type { RouteTree } from "route-tree";
|
package/src/internals.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import type { DependenciesStore } from "./namespaces";
|
|
2
|
-
import type { RoutesStore } from "./namespaces/RoutesNamespace";
|
|
3
|
-
import type { Router as RouterClass } from "./Router";
|
|
4
|
-
import type { EventMethodMap, GuardFnFactory, PluginFactory } from "./types";
|
|
5
|
-
import type { RouterValidator } from "./types/RouterValidator";
|
|
6
|
-
import type { SerializedRouterState } from "./utils";
|
|
7
|
-
import type {
|
|
8
|
-
DefaultDependencies,
|
|
9
|
-
EventName,
|
|
10
|
-
NavigationOptions,
|
|
11
|
-
Options,
|
|
12
|
-
Params,
|
|
13
|
-
Plugin,
|
|
14
|
-
Router as RouterInterface,
|
|
15
|
-
RouteTreeState,
|
|
16
|
-
SimpleState,
|
|
17
|
-
State,
|
|
18
|
-
Unsubscribe,
|
|
19
|
-
} from "@real-router/types";
|
|
20
|
-
import type { RouteTree } from "route-tree";
|
|
21
|
-
|
|
22
|
-
export interface RouterInternals<
|
|
23
|
-
D extends DefaultDependencies = DefaultDependencies,
|
|
24
|
-
> {
|
|
25
|
-
readonly makeState: <P extends Params = Params>(
|
|
26
|
-
name: string,
|
|
27
|
-
params?: P,
|
|
28
|
-
path?: string,
|
|
29
|
-
meta?: Record<string, Record<string, "url" | "query">>,
|
|
30
|
-
) => State<P>;
|
|
31
|
-
|
|
32
|
-
readonly forwardState: <P extends Params = Params>(
|
|
33
|
-
routeName: string,
|
|
34
|
-
routeParams: P,
|
|
35
|
-
) => SimpleState<P>;
|
|
36
|
-
|
|
37
|
-
readonly buildStateResolved: (
|
|
38
|
-
resolvedName: string,
|
|
39
|
-
resolvedParams: Params,
|
|
40
|
-
) => RouteTreeState | undefined;
|
|
41
|
-
|
|
42
|
-
readonly matchPath: <P extends Params = Params>(
|
|
43
|
-
path: string,
|
|
44
|
-
options?: Options,
|
|
45
|
-
) => State<P> | undefined;
|
|
46
|
-
|
|
47
|
-
readonly getOptions: () => Options;
|
|
48
|
-
|
|
49
|
-
readonly addEventListener: <E extends EventName>(
|
|
50
|
-
eventName: E,
|
|
51
|
-
cb: Plugin[EventMethodMap[E]],
|
|
52
|
-
) => Unsubscribe;
|
|
53
|
-
|
|
54
|
-
readonly buildPath: (route: string, params?: Params) => string;
|
|
55
|
-
|
|
56
|
-
readonly emitTransitionError: (error: Error) => void;
|
|
57
|
-
|
|
58
|
-
readonly start: (path: string) => Promise<State>;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Plugin-only navigation entry point — delegates to
|
|
62
|
-
* `NavigationNamespace.navigateToState` (`getPluginApi(router).navigateToState`).
|
|
63
|
-
* Hidden from `Router`/`Navigator` to keep the userland surface minimal;
|
|
64
|
-
* see `core-types/src/api.ts` for usage docs.
|
|
65
|
-
*/
|
|
66
|
-
readonly navigateToState: (
|
|
67
|
-
state: State,
|
|
68
|
-
options?: NavigationOptions,
|
|
69
|
-
) => Promise<State>;
|
|
70
|
-
|
|
71
|
-
/* eslint-disable @typescript-eslint/no-explicit-any -- heterogeneous map: stores different InterceptorFn<M> types under different keys */
|
|
72
|
-
readonly interceptors: Map<
|
|
73
|
-
string,
|
|
74
|
-
((next: (...args: any[]) => any, ...args: any[]) => any)[]
|
|
75
|
-
>;
|
|
76
|
-
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
77
|
-
|
|
78
|
-
readonly setRootPath: (rootPath: string) => void;
|
|
79
|
-
readonly getRootPath: () => string;
|
|
80
|
-
|
|
81
|
-
readonly getTree: () => RouteTree;
|
|
82
|
-
|
|
83
|
-
readonly isDisposed: () => boolean;
|
|
84
|
-
|
|
85
|
-
validator: RouterValidator | null;
|
|
86
|
-
|
|
87
|
-
// Dependencies (issue #172)
|
|
88
|
-
readonly dependenciesGetStore: () => DependenciesStore<D>;
|
|
89
|
-
|
|
90
|
-
// Clone support (issue #173)
|
|
91
|
-
readonly cloneOptions: () => Options;
|
|
92
|
-
readonly cloneDependencies: () => Record<string, unknown>;
|
|
93
|
-
readonly getLifecycleFactories: () => [
|
|
94
|
-
Record<string, GuardFnFactory<D>>,
|
|
95
|
-
Record<string, GuardFnFactory<D>>,
|
|
96
|
-
];
|
|
97
|
-
readonly getPluginFactories: () => PluginFactory<D>[];
|
|
98
|
-
|
|
99
|
-
// Consolidated route data store (issue #174 Phase 2)
|
|
100
|
-
readonly routeGetStore: () => RoutesStore<D>;
|
|
101
|
-
|
|
102
|
-
// Cross-namespace state (issue #174)
|
|
103
|
-
readonly getStateName: () => string | undefined;
|
|
104
|
-
readonly isTransitioning: () => boolean;
|
|
105
|
-
readonly clearState: () => void;
|
|
106
|
-
readonly setState: (state: State) => void;
|
|
107
|
-
readonly routerExtensions: { keys: string[] }[];
|
|
108
|
-
readonly contextClaimRecords: Set<string>;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* One-shot hydration scratchpad populated by `hydrateRouter` immediately
|
|
112
|
-
* before delegating to `router.start(parsed.path)` and cleared in the
|
|
113
|
-
* matching `finally`. SSR loader plugins read this slot directly via
|
|
114
|
-
* `getInternals(router).hydrationState` to short-circuit their own loader
|
|
115
|
-
* call when the server-resolved namespace value is already present in the
|
|
116
|
-
* parsed state (#596). `null` outside of an active `hydrateRouter`
|
|
117
|
-
* invocation.
|
|
118
|
-
*/
|
|
119
|
-
hydrationState: SerializedRouterState | null;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- existential type: stores RouterInternals for all Dependencies types
|
|
123
|
-
const internals = new WeakMap<object, RouterInternals<any>>();
|
|
124
|
-
|
|
125
|
-
export function getInternals<D extends DefaultDependencies>(
|
|
126
|
-
router: RouterInterface<D>,
|
|
127
|
-
): RouterInternals<D> {
|
|
128
|
-
const ctx = internals.get(router);
|
|
129
|
-
|
|
130
|
-
if (!ctx) {
|
|
131
|
-
throw new TypeError(
|
|
132
|
-
"[real-router] Invalid router instance — not found in internals registry",
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return ctx as RouterInternals<D>;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function registerInternals<D extends DefaultDependencies>(
|
|
140
|
-
router: RouterClass<D>,
|
|
141
|
-
ctx: RouterInternals<D>,
|
|
142
|
-
): void {
|
|
143
|
-
internals.set(router, ctx);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument -- internal chain execution: type safety enforced at public API boundary (PluginApi.addInterceptor) */
|
|
147
|
-
function executeInterceptorChain<T>(
|
|
148
|
-
interceptors: ((next: (...args: any[]) => any, ...args: any[]) => any)[],
|
|
149
|
-
original: (...args: any[]) => T,
|
|
150
|
-
args: any[],
|
|
151
|
-
): T {
|
|
152
|
-
let chain = original as (...args: any[]) => any;
|
|
153
|
-
|
|
154
|
-
for (const interceptor of interceptors) {
|
|
155
|
-
const prev = chain;
|
|
156
|
-
|
|
157
|
-
chain = (...chainArgs: any[]) => interceptor(prev, ...chainArgs);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return chain(...args) as T;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function createInterceptable<T extends (...args: any[]) => any>(
|
|
164
|
-
name: string,
|
|
165
|
-
original: T,
|
|
166
|
-
interceptors: Map<
|
|
167
|
-
string,
|
|
168
|
-
((next: (...args: any[]) => any, ...args: any[]) => any)[]
|
|
169
|
-
>,
|
|
170
|
-
): T {
|
|
171
|
-
return ((...args: any[]) => {
|
|
172
|
-
const chain = interceptors.get(name);
|
|
173
|
-
|
|
174
|
-
if (!chain || chain.length === 0) {
|
|
175
|
-
return original(...args);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return executeInterceptorChain(chain, original, args);
|
|
179
|
-
}) as T;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function createInterceptable2<A, B, R>(
|
|
183
|
-
name: string,
|
|
184
|
-
original: (a: A, b: B) => R,
|
|
185
|
-
interceptors: Map<
|
|
186
|
-
string,
|
|
187
|
-
((next: (...args: any[]) => any, ...args: any[]) => any)[]
|
|
188
|
-
>,
|
|
189
|
-
): (a: A, b: B) => R {
|
|
190
|
-
return (arg1: A, arg2: B) => {
|
|
191
|
-
const chain = interceptors.get(name);
|
|
192
|
-
|
|
193
|
-
if (!chain || chain.length === 0) {
|
|
194
|
-
return original(arg1, arg2);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return executeInterceptorChain(chain, original, [arg1, arg2]);
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
|