@real-router/core 0.45.0 → 0.45.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.
- package/package.json +6 -7
- package/src/Router.ts +0 -684
- package/src/RouterError.ts +0 -324
- package/src/api/cloneRouter.ts +0 -77
- package/src/api/getDependenciesApi.ts +0 -168
- package/src/api/getLifecycleApi.ts +0 -65
- package/src/api/getPluginApi.ts +0 -167
- package/src/api/getRoutesApi.ts +0 -573
- 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 -87
- package/src/createRouter.ts +0 -32
- package/src/fsm/index.ts +0 -5
- package/src/fsm/routerFSM.ts +0 -120
- package/src/getNavigator.ts +0 -30
- package/src/guards.ts +0 -46
- package/src/helpers.ts +0 -179
- package/src/index.ts +0 -50
- package/src/internals.ts +0 -173
- package/src/namespaces/DependenciesNamespace/dependenciesStore.ts +0 -30
- package/src/namespaces/DependenciesNamespace/index.ts +0 -5
- package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +0 -311
- package/src/namespaces/EventBusNamespace/index.ts +0 -5
- package/src/namespaces/EventBusNamespace/types.ts +0 -11
- package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +0 -405
- package/src/namespaces/NavigationNamespace/constants.ts +0 -55
- package/src/namespaces/NavigationNamespace/index.ts +0 -5
- package/src/namespaces/NavigationNamespace/transition/completeTransition.ts +0 -100
- package/src/namespaces/NavigationNamespace/transition/errorHandling.ts +0 -124
- package/src/namespaces/NavigationNamespace/transition/guardPhase.ts +0 -221
- package/src/namespaces/NavigationNamespace/types.ts +0 -100
- 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 -377
- 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 -26
- package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +0 -535
- package/src/namespaces/RoutesNamespace/constants.ts +0 -6
- package/src/namespaces/RoutesNamespace/forwardChain.ts +0 -34
- package/src/namespaces/RoutesNamespace/helpers.ts +0 -126
- package/src/namespaces/RoutesNamespace/index.ts +0 -11
- package/src/namespaces/RoutesNamespace/routeGuards.ts +0 -62
- package/src/namespaces/RoutesNamespace/routesStore.ts +0 -346
- package/src/namespaces/RoutesNamespace/types.ts +0 -81
- package/src/namespaces/StateNamespace/StateNamespace.ts +0 -211
- 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 -154
- package/src/types.ts +0 -69
- package/src/utils/getStaticPaths.ts +0 -50
- package/src/utils/index.ts +0 -5
- package/src/utils/serializeState.ts +0 -22
- package/src/validation.ts +0 -12
- package/src/wiring/RouterWiringBuilder.ts +0 -261
- package/src/wiring/index.ts +0 -7
- package/src/wiring/types.ts +0 -47
- package/src/wiring/wireRouter.ts +0 -26
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { logger } from "@real-router/logger";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Validates removeRoute constraints.
|
|
5
|
-
* Returns false if removal should be blocked (route is active).
|
|
6
|
-
* Logs warnings for edge cases.
|
|
7
|
-
*
|
|
8
|
-
* @param name - Route name to remove
|
|
9
|
-
* @param currentStateName - Current active route name (or undefined)
|
|
10
|
-
* @param isNavigating - Whether navigation is in progress
|
|
11
|
-
* @returns true if removal can proceed, false if blocked
|
|
12
|
-
*/
|
|
13
|
-
export function validateRemoveRoute(
|
|
14
|
-
name: string,
|
|
15
|
-
currentStateName: string | undefined,
|
|
16
|
-
isNavigating: boolean,
|
|
17
|
-
): boolean {
|
|
18
|
-
if (currentStateName) {
|
|
19
|
-
const isExactMatch = currentStateName === name;
|
|
20
|
-
const isParentOfCurrent = currentStateName.startsWith(`${name}.`);
|
|
21
|
-
|
|
22
|
-
if (isExactMatch || isParentOfCurrent) {
|
|
23
|
-
const suffix = isExactMatch ? "" : ` (current: "${currentStateName}")`;
|
|
24
|
-
|
|
25
|
-
logger.warn(
|
|
26
|
-
"router.removeRoute",
|
|
27
|
-
`Cannot remove route "${name}" — it is currently active${suffix}. Navigate away first.`,
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (isNavigating) {
|
|
35
|
-
logger.warn(
|
|
36
|
-
"router.removeRoute",
|
|
37
|
-
`Route "${name}" removed while navigation is in progress. This may cause unexpected behavior.`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Validates clearRoutes operation.
|
|
46
|
-
* Returns false if operation should be blocked (navigation in progress).
|
|
47
|
-
*
|
|
48
|
-
* @param isNavigating - Whether navigation is in progress
|
|
49
|
-
* @returns true if clearRoutes can proceed, false if blocked
|
|
50
|
-
*/
|
|
51
|
-
export function validateClearRoutes(isNavigating: boolean): boolean {
|
|
52
|
-
if (isNavigating) {
|
|
53
|
-
logger.error(
|
|
54
|
-
"router.clearRoutes",
|
|
55
|
-
"Cannot clear routes while navigation is in progress. Wait for navigation to complete.",
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RoutesNamespace/routesStore.ts
|
|
2
|
-
|
|
3
|
-
import { logger } from "@real-router/logger";
|
|
4
|
-
import { createMatcher, createRouteTree, nodeToDefinition } from "route-tree";
|
|
5
|
-
|
|
6
|
-
import { DEFAULT_ROUTE_NAME } from "./constants";
|
|
7
|
-
import { resolveForwardChain } from "./forwardChain";
|
|
8
|
-
import { createEmptyConfig, sanitizeRoute } from "./helpers";
|
|
9
|
-
|
|
10
|
-
import type { RouteConfig, RoutesDependencies } from "./types";
|
|
11
|
-
import type { GuardFnFactory, Route } from "../../types";
|
|
12
|
-
import type { RouteLifecycleNamespace } from "../RouteLifecycleNamespace";
|
|
13
|
-
import type { DefaultDependencies, Params } from "@real-router/types";
|
|
14
|
-
import type {
|
|
15
|
-
CreateMatcherOptions,
|
|
16
|
-
Matcher,
|
|
17
|
-
RouteDefinition,
|
|
18
|
-
RouteTree,
|
|
19
|
-
} from "route-tree";
|
|
20
|
-
|
|
21
|
-
// =============================================================================
|
|
22
|
-
// Interfaces
|
|
23
|
-
// =============================================================================
|
|
24
|
-
|
|
25
|
-
export interface RoutesStore<
|
|
26
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
27
|
-
> {
|
|
28
|
-
readonly definitions: RouteDefinition[];
|
|
29
|
-
readonly config: RouteConfig;
|
|
30
|
-
tree: RouteTree;
|
|
31
|
-
matcher: Matcher;
|
|
32
|
-
resolvedForwardMap: Record<string, string>;
|
|
33
|
-
routeCustomFields: Record<string, Record<string, unknown>>;
|
|
34
|
-
rootPath: string;
|
|
35
|
-
readonly matcherOptions: CreateMatcherOptions | undefined;
|
|
36
|
-
depsStore: RoutesDependencies<Dependencies> | undefined;
|
|
37
|
-
lifecycleNamespace: RouteLifecycleNamespace<Dependencies> | undefined;
|
|
38
|
-
readonly pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>;
|
|
39
|
-
readonly pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>;
|
|
40
|
-
readonly treeOperations: {
|
|
41
|
-
readonly commitTreeChanges: (store: RoutesStore<Dependencies>) => void;
|
|
42
|
-
readonly resetStore: (store: RoutesStore<Dependencies>) => void;
|
|
43
|
-
readonly nodeToDefinition: (node: RouteTree) => RouteDefinition;
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// =============================================================================
|
|
48
|
-
// Tree operations
|
|
49
|
-
// =============================================================================
|
|
50
|
-
|
|
51
|
-
function rebuildTree(
|
|
52
|
-
definitions: RouteDefinition[],
|
|
53
|
-
rootPath: string,
|
|
54
|
-
matcherOptions: CreateMatcherOptions | undefined,
|
|
55
|
-
): { tree: RouteTree; matcher: Matcher } {
|
|
56
|
-
const tree = createRouteTree(DEFAULT_ROUTE_NAME, rootPath, definitions);
|
|
57
|
-
const matcher = createMatcher(matcherOptions);
|
|
58
|
-
|
|
59
|
-
matcher.registerTree(tree);
|
|
60
|
-
|
|
61
|
-
return { tree, matcher };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function rebuildTreeInPlace<
|
|
65
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
66
|
-
>(store: RoutesStore<Dependencies>): void {
|
|
67
|
-
const result = rebuildTree(
|
|
68
|
-
store.definitions,
|
|
69
|
-
store.rootPath,
|
|
70
|
-
store.matcherOptions,
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
store.tree = result.tree;
|
|
74
|
-
store.matcher = result.matcher;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function commitTreeChanges<
|
|
78
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
79
|
-
>(store: RoutesStore<Dependencies>): void {
|
|
80
|
-
rebuildTreeInPlace(store);
|
|
81
|
-
store.resolvedForwardMap = refreshForwardMap(store.config);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// =============================================================================
|
|
85
|
-
// Store reset
|
|
86
|
-
// =============================================================================
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Clears all routes and resets config.
|
|
90
|
-
* Does NOT clear lifecycle handlers or state — caller handles that.
|
|
91
|
-
*/
|
|
92
|
-
export function resetStore<
|
|
93
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
94
|
-
>(store: RoutesStore<Dependencies>): void {
|
|
95
|
-
clearRouteData(store);
|
|
96
|
-
rebuildTreeInPlace(store);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Clears route data without rebuilding the tree.
|
|
101
|
-
* Used by replace() to avoid double rebuild (clearRouteData + commitTreeChanges).
|
|
102
|
-
*/
|
|
103
|
-
export function clearRouteData<
|
|
104
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
105
|
-
>(store: RoutesStore<Dependencies>): void {
|
|
106
|
-
store.definitions.length = 0;
|
|
107
|
-
|
|
108
|
-
Object.assign(store.config, createEmptyConfig());
|
|
109
|
-
|
|
110
|
-
store.resolvedForwardMap = Object.create(null) as Record<string, string>;
|
|
111
|
-
store.routeCustomFields = Object.create(null) as Record<
|
|
112
|
-
string,
|
|
113
|
-
Record<string, unknown>
|
|
114
|
-
>;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// =============================================================================
|
|
118
|
-
// Forward map
|
|
119
|
-
// =============================================================================
|
|
120
|
-
|
|
121
|
-
export function refreshForwardMap(config: RouteConfig): Record<string, string> {
|
|
122
|
-
const map = Object.create(null) as Record<string, string>;
|
|
123
|
-
|
|
124
|
-
for (const fromRoute of Object.keys(config.forwardMap)) {
|
|
125
|
-
map[fromRoute] = resolveForwardChain(fromRoute, config.forwardMap);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return map;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// =============================================================================
|
|
132
|
-
// Route handler registration
|
|
133
|
-
// =============================================================================
|
|
134
|
-
|
|
135
|
-
function registerForwardTo<Dependencies extends DefaultDependencies>(
|
|
136
|
-
route: Route<Dependencies>,
|
|
137
|
-
fullName: string,
|
|
138
|
-
config: RouteConfig,
|
|
139
|
-
): void {
|
|
140
|
-
if (route.canActivate) {
|
|
141
|
-
/* v8 ignore next -- @preserve: edge case, both string and function tested separately */
|
|
142
|
-
const forwardTarget =
|
|
143
|
-
typeof route.forwardTo === "string" ? route.forwardTo : "[dynamic]";
|
|
144
|
-
|
|
145
|
-
logger.warn(
|
|
146
|
-
"real-router",
|
|
147
|
-
`Route "${fullName}" has both forwardTo and canActivate. ` +
|
|
148
|
-
`canActivate will be ignored because forwardTo creates a redirect (industry standard). ` +
|
|
149
|
-
`Move canActivate to the target route "${forwardTarget}".`,
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (route.canDeactivate) {
|
|
154
|
-
/* v8 ignore next -- @preserve: edge case, both string and function tested separately */
|
|
155
|
-
const forwardTarget =
|
|
156
|
-
typeof route.forwardTo === "string" ? route.forwardTo : "[dynamic]";
|
|
157
|
-
|
|
158
|
-
logger.warn(
|
|
159
|
-
"real-router",
|
|
160
|
-
`Route "${fullName}" has both forwardTo and canDeactivate. ` +
|
|
161
|
-
`canDeactivate will be ignored because forwardTo creates a redirect (industry standard). ` +
|
|
162
|
-
`Move canDeactivate to the target route "${forwardTarget}".`,
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (typeof route.forwardTo === "function") {
|
|
167
|
-
const isNativeAsync =
|
|
168
|
-
(route.forwardTo as { constructor: { name: string } }).constructor
|
|
169
|
-
.name === "AsyncFunction";
|
|
170
|
-
const isTranspiledAsync = route.forwardTo.toString().includes("__awaiter");
|
|
171
|
-
|
|
172
|
-
if (isNativeAsync || isTranspiledAsync) {
|
|
173
|
-
throw new TypeError(
|
|
174
|
-
`forwardTo callback cannot be async for route "${fullName}". ` +
|
|
175
|
-
`Async functions break matchPath/buildPath.`,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// forwardTo is guaranteed to exist at this point
|
|
181
|
-
if (typeof route.forwardTo === "string") {
|
|
182
|
-
config.forwardMap[fullName] = route.forwardTo;
|
|
183
|
-
} else {
|
|
184
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
185
|
-
config.forwardFnMap[fullName] = route.forwardTo!;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function registerSingleRouteHandlers<Dependencies extends DefaultDependencies>(
|
|
190
|
-
route: Route<Dependencies>,
|
|
191
|
-
fullName: string,
|
|
192
|
-
config: RouteConfig,
|
|
193
|
-
routeCustomFields: Record<string, Record<string, unknown>>,
|
|
194
|
-
pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>,
|
|
195
|
-
pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>,
|
|
196
|
-
depsStore: RoutesDependencies<Dependencies> | undefined,
|
|
197
|
-
): void {
|
|
198
|
-
const standardKeys = new Set([
|
|
199
|
-
"name",
|
|
200
|
-
"path",
|
|
201
|
-
"children",
|
|
202
|
-
"canActivate",
|
|
203
|
-
"canDeactivate",
|
|
204
|
-
"forwardTo",
|
|
205
|
-
"encodeParams",
|
|
206
|
-
"decodeParams",
|
|
207
|
-
"defaultParams",
|
|
208
|
-
]);
|
|
209
|
-
const customFields = Object.fromEntries(
|
|
210
|
-
Object.entries(route).filter(([key]) => !standardKeys.has(key)),
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
if (Object.keys(customFields).length > 0) {
|
|
214
|
-
routeCustomFields[fullName] = customFields;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (route.canActivate) {
|
|
218
|
-
if (depsStore) {
|
|
219
|
-
depsStore.addActivateGuard(fullName, route.canActivate);
|
|
220
|
-
} else {
|
|
221
|
-
pendingCanActivate.set(fullName, route.canActivate);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (route.canDeactivate) {
|
|
226
|
-
if (depsStore) {
|
|
227
|
-
depsStore.addDeactivateGuard(fullName, route.canDeactivate);
|
|
228
|
-
} else {
|
|
229
|
-
pendingCanDeactivate.set(fullName, route.canDeactivate);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (route.forwardTo) {
|
|
234
|
-
registerForwardTo(route, fullName, config);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (route.decodeParams) {
|
|
238
|
-
config.decoders[fullName] = (params: Params): Params =>
|
|
239
|
-
route.decodeParams?.(params) ?? params;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
if (route.encodeParams) {
|
|
243
|
-
config.encoders[fullName] = (params: Params): Params =>
|
|
244
|
-
route.encodeParams?.(params) ?? params;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (route.defaultParams) {
|
|
248
|
-
config.defaultParams[fullName] = route.defaultParams;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export function registerAllRouteHandlers<
|
|
253
|
-
Dependencies extends DefaultDependencies,
|
|
254
|
-
>(
|
|
255
|
-
routes: readonly Route<Dependencies>[],
|
|
256
|
-
config: RouteConfig,
|
|
257
|
-
routeCustomFields: Record<string, Record<string, unknown>>,
|
|
258
|
-
pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>,
|
|
259
|
-
pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>,
|
|
260
|
-
depsStore: RoutesDependencies<Dependencies> | undefined,
|
|
261
|
-
parentName = "",
|
|
262
|
-
): void {
|
|
263
|
-
for (const route of routes) {
|
|
264
|
-
const fullName = parentName ? `${parentName}.${route.name}` : route.name;
|
|
265
|
-
|
|
266
|
-
registerSingleRouteHandlers(
|
|
267
|
-
route,
|
|
268
|
-
fullName,
|
|
269
|
-
config,
|
|
270
|
-
routeCustomFields,
|
|
271
|
-
pendingCanActivate,
|
|
272
|
-
pendingCanDeactivate,
|
|
273
|
-
depsStore,
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
if (route.children) {
|
|
277
|
-
registerAllRouteHandlers(
|
|
278
|
-
route.children,
|
|
279
|
-
config,
|
|
280
|
-
routeCustomFields,
|
|
281
|
-
pendingCanActivate,
|
|
282
|
-
pendingCanDeactivate,
|
|
283
|
-
depsStore,
|
|
284
|
-
fullName,
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// =============================================================================
|
|
291
|
-
// Factory
|
|
292
|
-
// =============================================================================
|
|
293
|
-
|
|
294
|
-
export function createRoutesStore<
|
|
295
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
296
|
-
>(
|
|
297
|
-
routes: Route<Dependencies>[],
|
|
298
|
-
matcherOptions?: CreateMatcherOptions,
|
|
299
|
-
): RoutesStore<Dependencies> {
|
|
300
|
-
const definitions: RouteDefinition[] = [];
|
|
301
|
-
const config: RouteConfig = createEmptyConfig();
|
|
302
|
-
const routeCustomFields: Record<
|
|
303
|
-
string,
|
|
304
|
-
Record<string, unknown>
|
|
305
|
-
> = Object.create(null) as Record<string, Record<string, unknown>>;
|
|
306
|
-
const pendingCanActivate = new Map<string, GuardFnFactory<Dependencies>>();
|
|
307
|
-
const pendingCanDeactivate = new Map<string, GuardFnFactory<Dependencies>>();
|
|
308
|
-
|
|
309
|
-
for (const route of routes) {
|
|
310
|
-
definitions.push(sanitizeRoute(route));
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const { tree, matcher } = rebuildTree(definitions, "", matcherOptions);
|
|
314
|
-
|
|
315
|
-
registerAllRouteHandlers(
|
|
316
|
-
routes,
|
|
317
|
-
config,
|
|
318
|
-
routeCustomFields,
|
|
319
|
-
pendingCanActivate,
|
|
320
|
-
pendingCanDeactivate,
|
|
321
|
-
undefined,
|
|
322
|
-
"",
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
const resolvedForwardMap = refreshForwardMap(config);
|
|
326
|
-
|
|
327
|
-
return {
|
|
328
|
-
definitions,
|
|
329
|
-
config,
|
|
330
|
-
tree,
|
|
331
|
-
matcher,
|
|
332
|
-
resolvedForwardMap,
|
|
333
|
-
routeCustomFields,
|
|
334
|
-
rootPath: "",
|
|
335
|
-
matcherOptions,
|
|
336
|
-
depsStore: undefined,
|
|
337
|
-
lifecycleNamespace: undefined,
|
|
338
|
-
pendingCanActivate,
|
|
339
|
-
pendingCanDeactivate,
|
|
340
|
-
treeOperations: {
|
|
341
|
-
commitTreeChanges,
|
|
342
|
-
resetStore,
|
|
343
|
-
nodeToDefinition,
|
|
344
|
-
},
|
|
345
|
-
};
|
|
346
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RoutesNamespace/types.ts
|
|
2
|
-
|
|
3
|
-
import type { GuardFnFactory } from "../../types";
|
|
4
|
-
import type {
|
|
5
|
-
DefaultDependencies,
|
|
6
|
-
ForwardToCallback,
|
|
7
|
-
Params,
|
|
8
|
-
SimpleState,
|
|
9
|
-
State,
|
|
10
|
-
} from "@real-router/types";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Dependencies injected into RoutesNamespace.
|
|
14
|
-
*
|
|
15
|
-
* These are function references from the Router facade,
|
|
16
|
-
* avoiding the need to pass the entire Router object.
|
|
17
|
-
*/
|
|
18
|
-
export interface RoutesDependencies<
|
|
19
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
20
|
-
> {
|
|
21
|
-
/** Register canActivate handler for a route */
|
|
22
|
-
addActivateGuard: (
|
|
23
|
-
name: string,
|
|
24
|
-
handler: GuardFnFactory<Dependencies>,
|
|
25
|
-
) => void;
|
|
26
|
-
|
|
27
|
-
/** Register canDeactivate handler for a route */
|
|
28
|
-
addDeactivateGuard: (
|
|
29
|
-
name: string,
|
|
30
|
-
handler: GuardFnFactory<Dependencies>,
|
|
31
|
-
) => void;
|
|
32
|
-
|
|
33
|
-
/** Create state object */
|
|
34
|
-
makeState: <P extends Params = Params>(
|
|
35
|
-
name: string,
|
|
36
|
-
params?: P,
|
|
37
|
-
path?: string,
|
|
38
|
-
meta?: Record<string, Record<string, "url" | "query">>,
|
|
39
|
-
) => State<P>;
|
|
40
|
-
|
|
41
|
-
/** Get current router state */
|
|
42
|
-
getState: () => State | undefined;
|
|
43
|
-
|
|
44
|
-
/** Compare two states for equality */
|
|
45
|
-
areStatesEqual: (
|
|
46
|
-
state1: State | undefined,
|
|
47
|
-
state2: State | undefined,
|
|
48
|
-
ignoreQueryParams?: boolean,
|
|
49
|
-
) => boolean;
|
|
50
|
-
|
|
51
|
-
/** Get a dependency by name */
|
|
52
|
-
getDependency: <K extends keyof Dependencies>(name: K) => Dependencies[K];
|
|
53
|
-
|
|
54
|
-
/** Forward state through facade (allows plugin interception) */
|
|
55
|
-
forwardState: <P extends Params = Params>(
|
|
56
|
-
name: string,
|
|
57
|
-
params: P,
|
|
58
|
-
) => SimpleState<P>;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Configuration storage for routes.
|
|
63
|
-
* Stores decoders, encoders, default params, and forward mappings.
|
|
64
|
-
*/
|
|
65
|
-
export interface RouteConfig {
|
|
66
|
-
/** Custom param decoders per route */
|
|
67
|
-
decoders: Record<string, (params: Params) => Params>;
|
|
68
|
-
|
|
69
|
-
/** Custom param encoders per route */
|
|
70
|
-
encoders: Record<string, (params: Params) => Params>;
|
|
71
|
-
|
|
72
|
-
/** Default params per route */
|
|
73
|
-
defaultParams: Record<string, Params>;
|
|
74
|
-
|
|
75
|
-
/** Forward mappings (source -> target) */
|
|
76
|
-
forwardMap: Record<string, string>;
|
|
77
|
-
|
|
78
|
-
/** Dynamic forward callbacks (source -> callback) */
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
80
|
-
forwardFnMap: Record<string, ForwardToCallback<any>>;
|
|
81
|
-
}
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/StateNamespace/StateNamespace.ts
|
|
2
|
-
|
|
3
|
-
import { areParamValuesEqual } from "./helpers";
|
|
4
|
-
import { EMPTY_PARAMS } from "../../constants";
|
|
5
|
-
import { freezeStateInPlace } from "../../helpers";
|
|
6
|
-
import { setStateMetaParams } from "../../stateMetaStore";
|
|
7
|
-
|
|
8
|
-
import type { StateNamespaceDependencies } from "./types";
|
|
9
|
-
import type { Params, State } from "@real-router/types";
|
|
10
|
-
import type { RouteTreeStateMeta } from "route-tree";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Independent namespace for managing router state storage and creation.
|
|
14
|
-
*
|
|
15
|
-
* Static methods handle validation (called by facade).
|
|
16
|
-
* Instance methods handle state storage, freezing, and creation.
|
|
17
|
-
*/
|
|
18
|
-
export class StateNamespace {
|
|
19
|
-
/**
|
|
20
|
-
* Cached frozen state - avoids structuredClone on every getState() call.
|
|
21
|
-
*/
|
|
22
|
-
#frozenState: State | undefined = undefined;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Previous state before the last setState call.
|
|
26
|
-
*/
|
|
27
|
-
#previousState: State | undefined = undefined;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Dependencies injected from Router.
|
|
31
|
-
*/
|
|
32
|
-
#deps!: StateNamespaceDependencies;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Cache for URL params by route name.
|
|
36
|
-
*/
|
|
37
|
-
readonly #urlParamsCache = new Map<string, string[]>();
|
|
38
|
-
|
|
39
|
-
// =========================================================================
|
|
40
|
-
// Instance methods (trust input - already validated by facade)
|
|
41
|
-
// =========================================================================
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Returns the current router state.
|
|
45
|
-
*
|
|
46
|
-
* The returned state is deeply frozen (immutable) for safety.
|
|
47
|
-
* Returns `undefined` if the router has not been started or has been stopped.
|
|
48
|
-
*/
|
|
49
|
-
get<P extends Params = Params>(): State<P> | undefined {
|
|
50
|
-
return this.#frozenState as State<P> | undefined; // NOSONAR -- generic narrowing needed for public API
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Sets the current router state.
|
|
55
|
-
*
|
|
56
|
-
* The state is deeply frozen before storage to ensure immutability.
|
|
57
|
-
* The previous state is preserved and accessible via `getPrevious()`.
|
|
58
|
-
*
|
|
59
|
-
* @param state - Already validated by facade, or undefined to clear
|
|
60
|
-
*/
|
|
61
|
-
set(state: State | undefined): void {
|
|
62
|
-
// Preserve current state as previous before updating
|
|
63
|
-
this.#previousState = this.#frozenState;
|
|
64
|
-
|
|
65
|
-
// If state is already frozen (from makeState()), use it directly.
|
|
66
|
-
// For external states, freeze in place without cloning.
|
|
67
|
-
this.#frozenState = state ? freezeStateInPlace(state) : undefined;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Returns the previous router state (before the last navigation).
|
|
72
|
-
*/
|
|
73
|
-
getPrevious(): State | undefined {
|
|
74
|
-
return this.#previousState;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
reset(): void {
|
|
78
|
-
this.#frozenState = undefined;
|
|
79
|
-
this.#previousState = undefined;
|
|
80
|
-
this.#urlParamsCache.clear();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// =========================================================================
|
|
84
|
-
// Dependency Injection
|
|
85
|
-
// =========================================================================
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Sets dependencies for state creation methods.
|
|
89
|
-
* Must be called before using makeState, areStatesEqual, etc.
|
|
90
|
-
*/
|
|
91
|
-
setDependencies(deps: StateNamespaceDependencies): void {
|
|
92
|
-
this.#deps = deps;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// =========================================================================
|
|
96
|
-
// State Creation Methods
|
|
97
|
-
// =========================================================================
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Creates a frozen state object for a route.
|
|
101
|
-
*/
|
|
102
|
-
makeState<P extends Params = Params>(
|
|
103
|
-
name: string,
|
|
104
|
-
params?: P,
|
|
105
|
-
path?: string,
|
|
106
|
-
meta?: RouteTreeStateMeta,
|
|
107
|
-
skipFreeze?: boolean,
|
|
108
|
-
): State<P> {
|
|
109
|
-
// Optimization: O(1) lookup instead of O(depth) ancestor iteration
|
|
110
|
-
const defaultParamsConfig = this.#deps.getDefaultParams();
|
|
111
|
-
const hasDefaultParams = Object.hasOwn(defaultParamsConfig, name);
|
|
112
|
-
|
|
113
|
-
// Conditional allocation: avoid spreading when no defaultParams exist
|
|
114
|
-
let mergedParams: P;
|
|
115
|
-
|
|
116
|
-
if (hasDefaultParams) {
|
|
117
|
-
mergedParams = { ...defaultParamsConfig[name], ...params } as P;
|
|
118
|
-
} else if (!params || params === EMPTY_PARAMS) {
|
|
119
|
-
mergedParams = EMPTY_PARAMS as P;
|
|
120
|
-
} else {
|
|
121
|
-
mergedParams = { ...params };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const state: State<P> = {
|
|
125
|
-
name,
|
|
126
|
-
params: mergedParams,
|
|
127
|
-
path: path ?? this.#deps.buildPath(name, params),
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
if (meta) {
|
|
131
|
-
setStateMetaParams(state, meta as unknown as Params);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return skipFreeze ? state : freezeStateInPlace(state);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// =========================================================================
|
|
138
|
-
// State Comparison Methods
|
|
139
|
-
// =========================================================================
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Compares two states for equality.
|
|
143
|
-
* By default, ignores query params (only compares URL params).
|
|
144
|
-
*/
|
|
145
|
-
areStatesEqual(
|
|
146
|
-
state1: State | undefined,
|
|
147
|
-
state2: State | undefined,
|
|
148
|
-
ignoreQueryParams = true,
|
|
149
|
-
): boolean {
|
|
150
|
-
if (!state1 || !state2) {
|
|
151
|
-
return !!state1 === !!state2;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (state1.name !== state2.name) {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (ignoreQueryParams) {
|
|
159
|
-
const urlParams = this.#getUrlParams(state1.name);
|
|
160
|
-
|
|
161
|
-
for (const urlParam of urlParams) {
|
|
162
|
-
if (
|
|
163
|
-
!areParamValuesEqual(state1.params[urlParam], state2.params[urlParam])
|
|
164
|
-
) {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return true;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const state1Keys = Object.keys(state1.params);
|
|
173
|
-
const state2Keys = Object.keys(state2.params);
|
|
174
|
-
|
|
175
|
-
if (state1Keys.length !== state2Keys.length) {
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
for (const param of state1Keys) {
|
|
180
|
-
if (
|
|
181
|
-
!(param in state2.params) ||
|
|
182
|
-
!areParamValuesEqual(state1.params[param], state2.params[param])
|
|
183
|
-
) {
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// =========================================================================
|
|
192
|
-
// Private Helpers
|
|
193
|
-
// =========================================================================
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Gets URL params for a route name, using cache for performance.
|
|
197
|
-
*/
|
|
198
|
-
#getUrlParams(name: string): string[] {
|
|
199
|
-
const cached = this.#urlParamsCache.get(name);
|
|
200
|
-
|
|
201
|
-
if (cached !== undefined) {
|
|
202
|
-
return cached;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const result = this.#deps.getUrlParams(name);
|
|
206
|
-
|
|
207
|
-
this.#urlParamsCache.set(name, result);
|
|
208
|
-
|
|
209
|
-
return result;
|
|
210
|
-
}
|
|
211
|
-
}
|