@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.
- package/dist/cjs/Router-Brp6_4FE.js +6 -0
- package/dist/cjs/Router-Brp6_4FE.js.map +1 -0
- package/dist/cjs/api.d.ts +1 -1
- package/dist/cjs/api.js +1 -1
- package/dist/cjs/{cloneRouter-DRieJvam.js → cloneRouter-CZx0T0RQ.js} +2 -2
- package/dist/cjs/{cloneRouter-DRieJvam.js.map → cloneRouter-CZx0T0RQ.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/index-CYpAZCoc.d.ts.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/utils.js +1 -1
- package/dist/cjs/utils.js.map +1 -1
- package/dist/cjs/validation.d.ts +1 -1
- package/dist/esm/Router-LT61erYH.mjs +6 -0
- package/dist/esm/Router-LT61erYH.mjs.map +1 -0
- package/dist/esm/api.d.mts +1 -1
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/{cloneRouter-DHrH6D_z.mjs → cloneRouter-DAscsmmF.mjs} +2 -2
- package/dist/esm/{cloneRouter-DHrH6D_z.mjs.map → cloneRouter-DAscsmmF.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/index-CYpAZCoc.d.mts.map +1 -1
- package/dist/esm/index.d.mts +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/utils.mjs +1 -1
- package/dist/esm/utils.mjs.map +1 -1
- package/dist/esm/validation.d.mts +1 -1
- package/package.json +4 -5
- package/dist/cjs/Router-IEGavTKk.js +0 -6
- package/dist/cjs/Router-IEGavTKk.js.map +0 -1
- package/dist/cjs/index-C-i6vx5Y.d.ts.map +0 -1
- package/dist/esm/Router-B3aeavRb.mjs +0 -6
- package/dist/esm/Router-B3aeavRb.mjs.map +0 -1
- package/dist/esm/index-C-i6vx5Y.d.mts.map +0 -1
- package/src/Router.ts +0 -737
- 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 -831
- 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 -66
- package/src/internals.ts +0 -228
- package/src/namespaces/DependenciesNamespace/dependenciesStore.ts +0 -30
- package/src/namespaces/DependenciesNamespace/index.ts +0 -5
- package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +0 -522
- 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 -440
- package/src/typeGuards.ts +0 -59
- package/src/types/RouterValidator.ts +0 -156
- package/src/types.ts +0 -77
- 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
|
@@ -1,566 +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
|
-
/**
|
|
136
|
-
* Throws if `forwardTo` is an async function (native or transpiled). Async
|
|
137
|
-
* forwardTo callbacks break the synchronous matchPath/buildPath contract.
|
|
138
|
-
* Runs inside `registerForwardTo`, which the prepare-phase build invokes via
|
|
139
|
-
* `registerAllRouteHandlers` — so the check fires before any store mutation.
|
|
140
|
-
*/
|
|
141
|
-
function assertForwardToNotAsync(forwardTo: unknown, fullName: string): void {
|
|
142
|
-
if (typeof forwardTo !== "function") {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const isNativeAsync =
|
|
147
|
-
(forwardTo as { constructor: { name: string } }).constructor.name ===
|
|
148
|
-
"AsyncFunction";
|
|
149
|
-
const isTranspiledAsync = (forwardTo as { toString: () => string })
|
|
150
|
-
.toString()
|
|
151
|
-
.includes("__awaiter");
|
|
152
|
-
|
|
153
|
-
if (isNativeAsync || isTranspiledAsync) {
|
|
154
|
-
throw new TypeError(
|
|
155
|
-
`forwardTo callback cannot be async for route "${fullName}". ` +
|
|
156
|
-
`Async functions break matchPath/buildPath.`,
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function registerForwardTo<Dependencies extends DefaultDependencies>(
|
|
162
|
-
route: Route<Dependencies>,
|
|
163
|
-
fullName: string,
|
|
164
|
-
config: RouteConfig,
|
|
165
|
-
): void {
|
|
166
|
-
if (route.canActivate) {
|
|
167
|
-
/* v8 ignore next -- @preserve: edge case, both string and function tested separately */
|
|
168
|
-
const forwardTarget =
|
|
169
|
-
typeof route.forwardTo === "string" ? route.forwardTo : "[dynamic]";
|
|
170
|
-
|
|
171
|
-
logger.warn(
|
|
172
|
-
"real-router",
|
|
173
|
-
`Route "${fullName}" has both forwardTo and canActivate. ` +
|
|
174
|
-
`canActivate will be ignored because forwardTo creates a redirect (industry standard). ` +
|
|
175
|
-
`Move canActivate to the target route "${forwardTarget}".`,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (route.canDeactivate) {
|
|
180
|
-
/* v8 ignore next -- @preserve: edge case, both string and function tested separately */
|
|
181
|
-
const forwardTarget =
|
|
182
|
-
typeof route.forwardTo === "string" ? route.forwardTo : "[dynamic]";
|
|
183
|
-
|
|
184
|
-
logger.warn(
|
|
185
|
-
"real-router",
|
|
186
|
-
`Route "${fullName}" has both forwardTo and canDeactivate. ` +
|
|
187
|
-
`canDeactivate will be ignored because forwardTo creates a redirect (industry standard). ` +
|
|
188
|
-
`Move canDeactivate to the target route "${forwardTarget}".`,
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
assertForwardToNotAsync(route.forwardTo, fullName);
|
|
193
|
-
|
|
194
|
-
// forwardTo is guaranteed to exist at this point
|
|
195
|
-
if (typeof route.forwardTo === "string") {
|
|
196
|
-
config.forwardMap[fullName] = route.forwardTo;
|
|
197
|
-
} else {
|
|
198
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
199
|
-
config.forwardFnMap[fullName] = route.forwardTo!;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function registerSingleRouteHandlers<Dependencies extends DefaultDependencies>(
|
|
204
|
-
route: Route<Dependencies>,
|
|
205
|
-
fullName: string,
|
|
206
|
-
config: RouteConfig,
|
|
207
|
-
routeCustomFields: Record<string, Record<string, unknown>>,
|
|
208
|
-
pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>,
|
|
209
|
-
pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>,
|
|
210
|
-
): void {
|
|
211
|
-
const standardKeys = new Set([
|
|
212
|
-
"name",
|
|
213
|
-
"path",
|
|
214
|
-
"children",
|
|
215
|
-
"canActivate",
|
|
216
|
-
"canDeactivate",
|
|
217
|
-
"forwardTo",
|
|
218
|
-
"encodeParams",
|
|
219
|
-
"decodeParams",
|
|
220
|
-
"defaultParams",
|
|
221
|
-
]);
|
|
222
|
-
const customFields = Object.fromEntries(
|
|
223
|
-
Object.entries(route).filter(([key]) => !standardKeys.has(key)),
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
if (Object.keys(customFields).length > 0) {
|
|
227
|
-
routeCustomFields[fullName] = customFields;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Guards are collected here and registered into the lifecycle later — by
|
|
231
|
-
// `adoptRouteArtifacts` (add/replace) or `setDependencies` (initial routes) —
|
|
232
|
-
// so the build stays a pure, side-effect-free preparation step.
|
|
233
|
-
if (route.canActivate) {
|
|
234
|
-
pendingCanActivate.set(fullName, route.canActivate);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (route.canDeactivate) {
|
|
238
|
-
pendingCanDeactivate.set(fullName, route.canDeactivate);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (route.forwardTo) {
|
|
242
|
-
registerForwardTo(route, fullName, config);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (route.decodeParams) {
|
|
246
|
-
config.decoders[fullName] = (params: Params): Params =>
|
|
247
|
-
route.decodeParams?.(params) ?? params;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (route.encodeParams) {
|
|
251
|
-
config.encoders[fullName] = (params: Params): Params =>
|
|
252
|
-
route.encodeParams?.(params) ?? params;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (route.defaultParams) {
|
|
256
|
-
config.defaultParams[fullName] = route.defaultParams;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function registerAllRouteHandlers<Dependencies extends DefaultDependencies>(
|
|
261
|
-
routes: readonly Route<Dependencies>[],
|
|
262
|
-
config: RouteConfig,
|
|
263
|
-
routeCustomFields: Record<string, Record<string, unknown>>,
|
|
264
|
-
pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>,
|
|
265
|
-
pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>,
|
|
266
|
-
parentName = "",
|
|
267
|
-
): void {
|
|
268
|
-
for (const route of routes) {
|
|
269
|
-
const fullName = parentName ? `${parentName}.${route.name}` : route.name;
|
|
270
|
-
|
|
271
|
-
registerSingleRouteHandlers(
|
|
272
|
-
route,
|
|
273
|
-
fullName,
|
|
274
|
-
config,
|
|
275
|
-
routeCustomFields,
|
|
276
|
-
pendingCanActivate,
|
|
277
|
-
pendingCanDeactivate,
|
|
278
|
-
);
|
|
279
|
-
|
|
280
|
-
if (route.children) {
|
|
281
|
-
registerAllRouteHandlers(
|
|
282
|
-
route.children,
|
|
283
|
-
config,
|
|
284
|
-
routeCustomFields,
|
|
285
|
-
pendingCanActivate,
|
|
286
|
-
pendingCanDeactivate,
|
|
287
|
-
fullName,
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// =============================================================================
|
|
294
|
-
// Prepare-then-commit (issue #698)
|
|
295
|
-
//
|
|
296
|
-
// add()/replace() build the complete new store state into LOCAL structures, and
|
|
297
|
-
// only swap it into the store once every core-level error has surfaced from the
|
|
298
|
-
// build itself (async/circular forwardTo throw in registerAllRouteHandlers /
|
|
299
|
-
// refreshForwardMap; invalid path constraint throws in rebuildTree). The store
|
|
300
|
-
// is mutated only by `adoptRouteArtifacts`, which cannot throw — so a rejected
|
|
301
|
-
// build leaves the existing routes untouched. The two silent-corruption cases
|
|
302
|
-
// route-tree never throws on (duplicate name vs an existing route, missing
|
|
303
|
-
// parent) are caught up front by `assertAddable`.
|
|
304
|
-
// =============================================================================
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* The fully-built, ready-to-swap result of preparing a route mutation. Holds
|
|
308
|
-
* everything `adoptRouteArtifacts` assigns into the store.
|
|
309
|
-
*/
|
|
310
|
-
interface RouteArtifacts<
|
|
311
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
312
|
-
> {
|
|
313
|
-
readonly definitions: RouteDefinition[];
|
|
314
|
-
readonly config: RouteConfig;
|
|
315
|
-
readonly routeCustomFields: Record<string, Record<string, unknown>>;
|
|
316
|
-
readonly pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>;
|
|
317
|
-
readonly pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>;
|
|
318
|
-
readonly tree: RouteTree;
|
|
319
|
-
readonly matcher: Matcher;
|
|
320
|
-
readonly resolvedForwardMap: Record<string, string>;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/** Null-proto shallow clone of a RouteConfig (preserves the 5 maps' contents). */
|
|
324
|
-
function cloneConfig(config: RouteConfig): RouteConfig {
|
|
325
|
-
const clone = createEmptyConfig();
|
|
326
|
-
|
|
327
|
-
Object.assign(clone.decoders, config.decoders);
|
|
328
|
-
Object.assign(clone.encoders, config.encoders);
|
|
329
|
-
Object.assign(clone.defaultParams, config.defaultParams);
|
|
330
|
-
Object.assign(clone.forwardMap, config.forwardMap);
|
|
331
|
-
Object.assign(clone.forwardFnMap, config.forwardFnMap);
|
|
332
|
-
|
|
333
|
-
return clone;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Returns a new definitions array with `added` inserted, without mutating the
|
|
338
|
-
* input. For a top-level add the existing definitions are shallow-copied and
|
|
339
|
-
* `added` appended. For a parented add the spine down to the parent is cloned
|
|
340
|
-
* (siblings/other branches are shared by reference) and `added` appended to the
|
|
341
|
-
* parent's children. Caller guarantees the parent path exists (see assertAddable).
|
|
342
|
-
*/
|
|
343
|
-
function insertAddedDefinitions(
|
|
344
|
-
definitions: readonly RouteDefinition[],
|
|
345
|
-
added: RouteDefinition[],
|
|
346
|
-
parentSegments: readonly string[],
|
|
347
|
-
): RouteDefinition[] {
|
|
348
|
-
if (parentSegments.length === 0) {
|
|
349
|
-
return [...definitions, ...added];
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const [head, ...rest] = parentSegments;
|
|
353
|
-
|
|
354
|
-
return definitions.map((def) => {
|
|
355
|
-
if (def.name !== head) {
|
|
356
|
-
return def;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const children = def.children ?? [];
|
|
360
|
-
|
|
361
|
-
return {
|
|
362
|
-
...def,
|
|
363
|
-
children:
|
|
364
|
-
rest.length === 0
|
|
365
|
-
? [...children, ...added]
|
|
366
|
-
: insertAddedDefinitions(children, added, rest),
|
|
367
|
-
};
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/** Depth-first walk yielding each route's full dotted name (no side effects). */
|
|
372
|
-
function walkRouteNames<Dependencies extends DefaultDependencies>(
|
|
373
|
-
routes: readonly Route<Dependencies>[],
|
|
374
|
-
parentName: string,
|
|
375
|
-
onName: (fullName: string) => void,
|
|
376
|
-
): void {
|
|
377
|
-
for (const route of routes) {
|
|
378
|
-
const fullName = parentName ? `${parentName}.${route.name}` : route.name;
|
|
379
|
-
|
|
380
|
-
onName(fullName);
|
|
381
|
-
|
|
382
|
-
if (route.children) {
|
|
383
|
-
walkRouteNames(route.children, fullName, onName);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Up-front guard for `add` against the two corruptions route-tree stays silent
|
|
390
|
-
* on: a missing `parent`, and a name that collides with an EXISTING route
|
|
391
|
-
* (which would otherwise be silently overwritten). Throws before any build.
|
|
392
|
-
*/
|
|
393
|
-
export function assertAddable<Dependencies extends DefaultDependencies>(
|
|
394
|
-
store: RoutesStore<Dependencies>,
|
|
395
|
-
routes: readonly Route<Dependencies>[],
|
|
396
|
-
parentName: string | undefined,
|
|
397
|
-
): void {
|
|
398
|
-
if (parentName !== undefined && !store.matcher.hasRoute(parentName)) {
|
|
399
|
-
throw new Error(
|
|
400
|
-
`[router.addRoute] Parent route "${parentName}" does not exist`,
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
walkRouteNames(routes, parentName ?? "", (fullName) => {
|
|
405
|
-
if (store.matcher.hasRoute(fullName)) {
|
|
406
|
-
throw new Error(`[router.addRoute] Route "${fullName}" already exists`);
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Builds RouteArtifacts from a final definitions array and the routes whose
|
|
413
|
-
* handlers (config + guards) populate `config`/`routeCustomFields`. Guards are
|
|
414
|
-
* collected into the returned pending maps (depsStore is intentionally omitted
|
|
415
|
-
* so nothing compiles or touches the lifecycle here). THROWS on async/circular
|
|
416
|
-
* forwardTo and invalid path constraint — before the caller mutates the store.
|
|
417
|
-
*/
|
|
418
|
-
function buildArtifacts<Dependencies extends DefaultDependencies>(
|
|
419
|
-
definitions: RouteDefinition[],
|
|
420
|
-
routesForHandlers: readonly Route<Dependencies>[],
|
|
421
|
-
config: RouteConfig,
|
|
422
|
-
routeCustomFields: Record<string, Record<string, unknown>>,
|
|
423
|
-
handlerParentName: string,
|
|
424
|
-
rootPath: string,
|
|
425
|
-
matcherOptions: CreateMatcherOptions | undefined,
|
|
426
|
-
): RouteArtifacts<Dependencies> {
|
|
427
|
-
const pendingCanActivate = new Map<string, GuardFnFactory<Dependencies>>();
|
|
428
|
-
const pendingCanDeactivate = new Map<string, GuardFnFactory<Dependencies>>();
|
|
429
|
-
|
|
430
|
-
registerAllRouteHandlers(
|
|
431
|
-
routesForHandlers,
|
|
432
|
-
config,
|
|
433
|
-
routeCustomFields,
|
|
434
|
-
pendingCanActivate,
|
|
435
|
-
pendingCanDeactivate,
|
|
436
|
-
handlerParentName,
|
|
437
|
-
);
|
|
438
|
-
|
|
439
|
-
const resolvedForwardMap = refreshForwardMap(config);
|
|
440
|
-
const { tree, matcher } = rebuildTree(definitions, rootPath, matcherOptions);
|
|
441
|
-
|
|
442
|
-
return {
|
|
443
|
-
definitions,
|
|
444
|
-
config,
|
|
445
|
-
routeCustomFields,
|
|
446
|
-
pendingCanActivate,
|
|
447
|
-
pendingCanDeactivate,
|
|
448
|
-
tree,
|
|
449
|
-
matcher,
|
|
450
|
-
resolvedForwardMap,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/** Builds the merged artifacts for an incremental `add` (existing ∪ new). */
|
|
455
|
-
export function buildAddArtifacts<Dependencies extends DefaultDependencies>(
|
|
456
|
-
store: RoutesStore<Dependencies>,
|
|
457
|
-
routes: readonly Route<Dependencies>[],
|
|
458
|
-
parentName: string | undefined,
|
|
459
|
-
): RouteArtifacts<Dependencies> {
|
|
460
|
-
const definitions = insertAddedDefinitions(
|
|
461
|
-
store.definitions,
|
|
462
|
-
routes.map((route) => sanitizeRoute(route)),
|
|
463
|
-
parentName === undefined ? [] : parentName.split("."),
|
|
464
|
-
);
|
|
465
|
-
|
|
466
|
-
return buildArtifacts(
|
|
467
|
-
definitions,
|
|
468
|
-
routes,
|
|
469
|
-
cloneConfig(store.config),
|
|
470
|
-
Object.assign(
|
|
471
|
-
Object.create(null) as Record<string, Record<string, unknown>>,
|
|
472
|
-
store.routeCustomFields,
|
|
473
|
-
),
|
|
474
|
-
parentName ?? "",
|
|
475
|
-
store.rootPath,
|
|
476
|
-
store.matcherOptions,
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
/** Builds the fresh artifacts for a full `replace` (standalone new set). */
|
|
481
|
-
export function buildReplaceArtifacts<Dependencies extends DefaultDependencies>(
|
|
482
|
-
routes: readonly Route<Dependencies>[],
|
|
483
|
-
rootPath: string,
|
|
484
|
-
matcherOptions: CreateMatcherOptions | undefined,
|
|
485
|
-
): RouteArtifacts<Dependencies> {
|
|
486
|
-
return buildArtifacts(
|
|
487
|
-
routes.map((route) => sanitizeRoute(route)),
|
|
488
|
-
routes,
|
|
489
|
-
createEmptyConfig(),
|
|
490
|
-
Object.create(null) as Record<string, Record<string, unknown>>,
|
|
491
|
-
"",
|
|
492
|
-
rootPath,
|
|
493
|
-
matcherOptions,
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Commits prepared artifacts into the store in place. Pure assignment — never
|
|
499
|
-
* throws — so it is the single atomic swap point of the prepare-then-commit
|
|
500
|
-
* pipeline. Guard registration is deferred to here (the build collected guards
|
|
501
|
-
* without compiling); `depsStore` is always set on a wired router, which is the
|
|
502
|
-
* only path that reaches `add`/`replace`.
|
|
503
|
-
*/
|
|
504
|
-
export function adoptRouteArtifacts<Dependencies extends DefaultDependencies>(
|
|
505
|
-
store: RoutesStore<Dependencies>,
|
|
506
|
-
artifacts: RouteArtifacts<Dependencies>,
|
|
507
|
-
): void {
|
|
508
|
-
store.definitions.length = 0;
|
|
509
|
-
|
|
510
|
-
for (const def of artifacts.definitions) {
|
|
511
|
-
store.definitions.push(def);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
Object.assign(store.config, artifacts.config);
|
|
515
|
-
store.routeCustomFields = artifacts.routeCustomFields;
|
|
516
|
-
store.tree = artifacts.tree;
|
|
517
|
-
store.matcher = artifacts.matcher;
|
|
518
|
-
store.resolvedForwardMap = artifacts.resolvedForwardMap;
|
|
519
|
-
|
|
520
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- depsStore is set once the router is wired; add/replace only run on a wired router (constructor-time registration uses createRoutesStore)
|
|
521
|
-
const deps = store.depsStore!;
|
|
522
|
-
|
|
523
|
-
for (const [name, handler] of artifacts.pendingCanActivate) {
|
|
524
|
-
deps.addActivateGuard(name, handler);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
for (const [name, handler] of artifacts.pendingCanDeactivate) {
|
|
528
|
-
deps.addDeactivateGuard(name, handler);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
// =============================================================================
|
|
533
|
-
// Factory
|
|
534
|
-
// =============================================================================
|
|
535
|
-
|
|
536
|
-
export function createRoutesStore<
|
|
537
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
538
|
-
>(
|
|
539
|
-
routes: Route<Dependencies>[],
|
|
540
|
-
matcherOptions?: CreateMatcherOptions,
|
|
541
|
-
): RoutesStore<Dependencies> {
|
|
542
|
-
// Initial routes are a standalone set at rootPath "" — same build the
|
|
543
|
-
// prepare-then-commit `replace` path uses. Guards land in the pending maps
|
|
544
|
-
// (depsStore is wired later via setDependencies, which flushes them).
|
|
545
|
-
const artifacts = buildReplaceArtifacts(routes, "", matcherOptions);
|
|
546
|
-
|
|
547
|
-
return {
|
|
548
|
-
definitions: artifacts.definitions,
|
|
549
|
-
config: artifacts.config,
|
|
550
|
-
tree: artifacts.tree,
|
|
551
|
-
matcher: artifacts.matcher,
|
|
552
|
-
resolvedForwardMap: artifacts.resolvedForwardMap,
|
|
553
|
-
routeCustomFields: artifacts.routeCustomFields,
|
|
554
|
-
rootPath: "",
|
|
555
|
-
matcherOptions,
|
|
556
|
-
depsStore: undefined,
|
|
557
|
-
lifecycleNamespace: undefined,
|
|
558
|
-
pendingCanActivate: artifacts.pendingCanActivate,
|
|
559
|
-
pendingCanDeactivate: artifacts.pendingCanDeactivate,
|
|
560
|
-
treeOperations: {
|
|
561
|
-
commitTreeChanges,
|
|
562
|
-
resetStore,
|
|
563
|
-
nodeToDefinition,
|
|
564
|
-
},
|
|
565
|
-
};
|
|
566
|
-
}
|
|
@@ -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
|
-
}
|