@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
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RouterLifecycleNamespace/types.ts
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
NavigationOptions,
|
|
5
|
-
Options,
|
|
6
|
-
Params,
|
|
7
|
-
State,
|
|
8
|
-
} from "@real-router/types";
|
|
9
|
-
|
|
10
|
-
export interface RouterLifecycleDependencies {
|
|
11
|
-
getOptions: () => Options;
|
|
12
|
-
/**
|
|
13
|
-
* Commit a fully-resolved State without re-running `forwardState`/`buildPath`.
|
|
14
|
-
* `start(path)` uses this to commit `matchPath(path)` directly — the same
|
|
15
|
-
* primitive URL plugins use on popstate / navigate-event (#525). Keeps
|
|
16
|
-
* `state.path` identical to the source URL (preserves trailing slash in
|
|
17
|
-
* `trailingSlash:"preserve"` mode) and avoids the redundant
|
|
18
|
-
* forwardState+buildPath round-trip in `buildNavigateState`.
|
|
19
|
-
*/
|
|
20
|
-
navigateToState: (state: State, opts: NavigationOptions) => Promise<State>;
|
|
21
|
-
navigateToNotFound: (path: string) => State;
|
|
22
|
-
clearState: () => void;
|
|
23
|
-
matchPath: <P extends Params = Params>(path: string) => State<P> | undefined;
|
|
24
|
-
completeStart: () => void;
|
|
25
|
-
emitTransitionError: (
|
|
26
|
-
toState: State | undefined,
|
|
27
|
-
fromState: State | undefined,
|
|
28
|
-
error: Error,
|
|
29
|
-
) => void;
|
|
30
|
-
}
|
|
@@ -1,582 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RoutesNamespace/RoutesNamespace.ts
|
|
2
|
-
|
|
3
|
-
import { DEFAULT_ROUTE_NAME } from "./constants";
|
|
4
|
-
import {
|
|
5
|
-
matchSourceTrailingSlash,
|
|
6
|
-
paramsMatch,
|
|
7
|
-
paramsMatchExcluding,
|
|
8
|
-
stripQueryDefaults,
|
|
9
|
-
} from "./helpers";
|
|
10
|
-
import {
|
|
11
|
-
createRoutesStore,
|
|
12
|
-
rebuildTreeInPlace,
|
|
13
|
-
resetStore,
|
|
14
|
-
} from "./routesStore";
|
|
15
|
-
import { constants, DEFAULT_TRANSITION } from "../../constants";
|
|
16
|
-
import { getTransitionPath } from "../../transitionPath";
|
|
17
|
-
|
|
18
|
-
import type { RoutesStore } from "./routesStore";
|
|
19
|
-
import type { RoutesDependencies } from "./types";
|
|
20
|
-
import type { Route } from "../../types";
|
|
21
|
-
import type { RouteLifecycleNamespace } from "../RouteLifecycleNamespace";
|
|
22
|
-
import type {
|
|
23
|
-
DefaultDependencies,
|
|
24
|
-
ForwardToCallback,
|
|
25
|
-
Options,
|
|
26
|
-
Params,
|
|
27
|
-
State,
|
|
28
|
-
} from "@real-router/types";
|
|
29
|
-
import type {
|
|
30
|
-
CreateMatcherOptions,
|
|
31
|
-
RouteParams,
|
|
32
|
-
RouteTree,
|
|
33
|
-
RouteTreeState,
|
|
34
|
-
} from "route-tree";
|
|
35
|
-
|
|
36
|
-
function collectUrlParamsArray(segments: readonly RouteTree[]): string[] {
|
|
37
|
-
const params: string[] = [];
|
|
38
|
-
|
|
39
|
-
for (const segment of segments) {
|
|
40
|
-
for (const param of segment.paramMeta.urlParams) {
|
|
41
|
-
params.push(param);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return params;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function buildNameFromSegments(
|
|
49
|
-
segments: readonly { fullName: string }[],
|
|
50
|
-
): string {
|
|
51
|
-
return segments.at(-1)?.fullName ?? "";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function createRouteState<P extends RouteParams = RouteParams>(
|
|
55
|
-
matchResult: {
|
|
56
|
-
readonly segments: readonly { fullName: string }[];
|
|
57
|
-
readonly params: Readonly<Record<string, unknown>>;
|
|
58
|
-
readonly meta: Readonly<Record<string, Record<string, "url" | "query">>>;
|
|
59
|
-
},
|
|
60
|
-
name?: string,
|
|
61
|
-
): RouteTreeState<P> {
|
|
62
|
-
const resolvedName = name ?? buildNameFromSegments(matchResult.segments);
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
name: resolvedName,
|
|
66
|
-
params: matchResult.params as P,
|
|
67
|
-
meta: matchResult.meta,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
interface CachedBuildPathOpts {
|
|
72
|
-
readonly trailingSlash?: "always" | "never" | undefined;
|
|
73
|
-
readonly queryParamsMode?: "default" | "strict" | "loose" | undefined;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Independent namespace for managing routes.
|
|
78
|
-
*
|
|
79
|
-
* Static methods handle validation (called by facade).
|
|
80
|
-
* Instance methods handle storage and business logic.
|
|
81
|
-
*/
|
|
82
|
-
export class RoutesNamespace<
|
|
83
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
84
|
-
> {
|
|
85
|
-
readonly #store: RoutesStore<Dependencies>;
|
|
86
|
-
#cachedBuildPathOpts: CachedBuildPathOpts | undefined;
|
|
87
|
-
|
|
88
|
-
get #deps(): RoutesDependencies<Dependencies> {
|
|
89
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
90
|
-
return this.#store.depsStore!;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
constructor(
|
|
94
|
-
routes: Route<Dependencies>[] = [],
|
|
95
|
-
matcherOptions?: CreateMatcherOptions,
|
|
96
|
-
) {
|
|
97
|
-
this.#store = createRoutesStore(routes, matcherOptions);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Creates a predicate function to check if a route node should be updated.
|
|
102
|
-
* Note: Argument validation is done by facade (Router.ts) via validateShouldUpdateNodeArgs.
|
|
103
|
-
*/
|
|
104
|
-
static shouldUpdateNode(
|
|
105
|
-
nodeName: string,
|
|
106
|
-
): (toState: State, fromState?: State) => boolean {
|
|
107
|
-
return (toState: State, fromState?: State): boolean => {
|
|
108
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
109
|
-
if (!(toState && typeof toState === "object" && "name" in toState)) {
|
|
110
|
-
throw new TypeError(
|
|
111
|
-
"[router.shouldUpdateNode] toState must be valid State object",
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (toState.transition.reload) {
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Root node (DEFAULT_ROUTE_NAME === "") has no route-level identity — it
|
|
120
|
-
// represents "any route". It must update on every transition so that
|
|
121
|
-
// consumers subscribed via useRouteNode("") (including RouteView at
|
|
122
|
-
// the top of the tree) see every change. This matches the documented
|
|
123
|
-
// contract in adapter docs: `useRouteNode("")` — Root — ALL route
|
|
124
|
-
// changes. See #519 for the missed transitions it was suffering from
|
|
125
|
-
// (users → users.user had intersection="users", leaving the root node
|
|
126
|
-
// un-updated under a flat <Match segment="users.user" exact> pattern).
|
|
127
|
-
if (nodeName === DEFAULT_ROUTE_NAME) {
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const { intersection, toActivate, toDeactivate } = getTransitionPath(
|
|
132
|
-
toState,
|
|
133
|
-
fromState,
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
if (nodeName === intersection) {
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (toActivate.includes(nodeName)) {
|
|
141
|
-
return true;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return toDeactivate.includes(nodeName);
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// =========================================================================
|
|
149
|
-
// Dependency injection
|
|
150
|
-
// =========================================================================
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Sets dependencies and registers pending canActivate handlers.
|
|
154
|
-
* canActivate handlers from initial routes are deferred until deps are set.
|
|
155
|
-
*/
|
|
156
|
-
setDependencies(deps: RoutesDependencies<Dependencies>): void {
|
|
157
|
-
this.#store.depsStore = deps;
|
|
158
|
-
|
|
159
|
-
for (const [routeName, handler] of this.#store.pendingCanActivate) {
|
|
160
|
-
deps.addActivateGuard(routeName, handler);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
this.#store.pendingCanActivate.clear();
|
|
164
|
-
|
|
165
|
-
for (const [routeName, handler] of this.#store.pendingCanDeactivate) {
|
|
166
|
-
deps.addDeactivateGuard(routeName, handler);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
this.#store.pendingCanDeactivate.clear();
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Sets the lifecycle namespace reference.
|
|
174
|
-
*/
|
|
175
|
-
setLifecycleNamespace(
|
|
176
|
-
namespace: RouteLifecycleNamespace<Dependencies> | undefined,
|
|
177
|
-
): void {
|
|
178
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
179
|
-
this.#store.lifecycleNamespace = namespace!;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// =========================================================================
|
|
183
|
-
// Route tree operations
|
|
184
|
-
// =========================================================================
|
|
185
|
-
|
|
186
|
-
setRootPath(newRootPath: string): void {
|
|
187
|
-
this.#store.rootPath = newRootPath;
|
|
188
|
-
rebuildTreeInPlace(this.#store);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
hasRoute(name: string): boolean {
|
|
192
|
-
return this.#store.matcher.hasRoute(name);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
clearRoutes(): void {
|
|
196
|
-
resetStore(this.#store);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// =========================================================================
|
|
200
|
-
// Path operations
|
|
201
|
-
// =========================================================================
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Builds a URL path for a route.
|
|
205
|
-
* Note: Argument validation is done by facade (Router.ts) via validateBuildPathArgs.
|
|
206
|
-
*
|
|
207
|
-
* @param route - Route name
|
|
208
|
-
* @param params - Route parameters
|
|
209
|
-
* @param options - Router options
|
|
210
|
-
*/
|
|
211
|
-
buildPath(route: string, params?: Params, options?: Options): string {
|
|
212
|
-
if (route === constants.UNKNOWN_ROUTE) {
|
|
213
|
-
return typeof params?.path === "string" ? params.path : "";
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const paramsWithDefault = Object.hasOwn(
|
|
217
|
-
this.#store.config.defaultParams,
|
|
218
|
-
route,
|
|
219
|
-
)
|
|
220
|
-
? { ...this.#store.config.defaultParams[route], ...params }
|
|
221
|
-
: /* v8 ignore next -- @preserve: V8 can't track ?? branch in ternary; covered by buildPath tests without params */ (params ??
|
|
222
|
-
{});
|
|
223
|
-
|
|
224
|
-
const encodedParams =
|
|
225
|
-
typeof this.#store.config.encoders[route] === "function"
|
|
226
|
-
? this.#store.config.encoders[route]({ ...paramsWithDefault })
|
|
227
|
-
: paramsWithDefault;
|
|
228
|
-
|
|
229
|
-
return this.#store.matcher.buildPath(
|
|
230
|
-
route,
|
|
231
|
-
encodedParams,
|
|
232
|
-
this.#getBuildPathOptions(options),
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Matches a URL path to a route in the tree.
|
|
238
|
-
* Note: Argument validation is done by facade (Router.ts) via validateMatchPathArgs.
|
|
239
|
-
*/
|
|
240
|
-
matchPath<P extends Params = Params>(
|
|
241
|
-
path: string,
|
|
242
|
-
options?: Options,
|
|
243
|
-
): State<P> | undefined {
|
|
244
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Router.ts always passes options
|
|
245
|
-
const opts = options!;
|
|
246
|
-
|
|
247
|
-
const matchResult = this.#store.matcher.match(path);
|
|
248
|
-
|
|
249
|
-
if (!matchResult) {
|
|
250
|
-
return undefined;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const routeState = createRouteState(matchResult);
|
|
254
|
-
const { name, params, meta } = routeState;
|
|
255
|
-
|
|
256
|
-
const decodedParams =
|
|
257
|
-
typeof this.#store.config.decoders[name] === "function"
|
|
258
|
-
? this.#store.config.decoders[name](params)
|
|
259
|
-
: params;
|
|
260
|
-
|
|
261
|
-
const { name: routeName, params: routeParams } = this.#deps.forwardState<P>(
|
|
262
|
-
name,
|
|
263
|
-
decodedParams as P,
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
let builtPath = path;
|
|
267
|
-
|
|
268
|
-
if (opts.rewritePathOnMatch) {
|
|
269
|
-
const buildParams =
|
|
270
|
-
typeof this.#store.config.encoders[routeName] === "function"
|
|
271
|
-
? this.#store.config.encoders[routeName]({
|
|
272
|
-
...(routeParams as Params),
|
|
273
|
-
})
|
|
274
|
-
: (routeParams as Record<string, unknown>);
|
|
275
|
-
|
|
276
|
-
const ts = opts.trailingSlash;
|
|
277
|
-
|
|
278
|
-
builtPath = this.#store.matcher.buildPath(routeName, buildParams, {
|
|
279
|
-
trailingSlash: ts === "never" || ts === "always" ? ts : undefined,
|
|
280
|
-
queryParamsMode: opts.queryParamsMode,
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
if (ts === "preserve") {
|
|
284
|
-
builtPath = matchSourceTrailingSlash(path, builtPath);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return this.#deps.makeState<P>(routeName, routeParams, builtPath, meta);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Applies forwardTo and returns resolved state with merged defaultParams.
|
|
293
|
-
*
|
|
294
|
-
* Merges params in order:
|
|
295
|
-
* 1. Source route defaultParams
|
|
296
|
-
* 2. Provided params
|
|
297
|
-
* 3. Target route defaultParams (after resolving forwardTo)
|
|
298
|
-
*/
|
|
299
|
-
forwardState<P extends Params = Params>(
|
|
300
|
-
name: string,
|
|
301
|
-
params: P,
|
|
302
|
-
): { name: string; params: P } {
|
|
303
|
-
if (Object.hasOwn(this.#store.config.forwardFnMap, name)) {
|
|
304
|
-
const paramsWithSourceDefaults = this.#mergeDefaultParams(name, params);
|
|
305
|
-
const dynamicForward = this.#store.config.forwardFnMap[name];
|
|
306
|
-
const resolved = this.#resolveDynamicForward(
|
|
307
|
-
name,
|
|
308
|
-
dynamicForward,
|
|
309
|
-
params,
|
|
310
|
-
);
|
|
311
|
-
|
|
312
|
-
return {
|
|
313
|
-
name: resolved,
|
|
314
|
-
params: this.#mergeDefaultParams(resolved, paramsWithSourceDefaults),
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const staticForward = this.#store.resolvedForwardMap[name] ?? name;
|
|
319
|
-
|
|
320
|
-
if (
|
|
321
|
-
staticForward !== name &&
|
|
322
|
-
Object.hasOwn(this.#store.config.forwardFnMap, staticForward)
|
|
323
|
-
) {
|
|
324
|
-
const paramsWithSourceDefaults = this.#mergeDefaultParams(name, params);
|
|
325
|
-
const targetDynamicForward =
|
|
326
|
-
this.#store.config.forwardFnMap[staticForward];
|
|
327
|
-
const resolved = this.#resolveDynamicForward(
|
|
328
|
-
staticForward,
|
|
329
|
-
targetDynamicForward,
|
|
330
|
-
params,
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
return {
|
|
334
|
-
name: resolved,
|
|
335
|
-
params: this.#mergeDefaultParams(resolved, paramsWithSourceDefaults),
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
if (staticForward !== name) {
|
|
340
|
-
const paramsWithSourceDefaults = this.#mergeDefaultParams(name, params);
|
|
341
|
-
|
|
342
|
-
return {
|
|
343
|
-
name: staticForward,
|
|
344
|
-
params: this.#mergeDefaultParams(
|
|
345
|
-
staticForward,
|
|
346
|
-
paramsWithSourceDefaults,
|
|
347
|
-
),
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
return { name, params: this.#mergeDefaultParams(name, params) };
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Builds a RouteTreeState from already-resolved route name and params.
|
|
356
|
-
* Called by Router.buildState after forwardState is applied at facade level.
|
|
357
|
-
* This allows plugins to intercept forwardState.
|
|
358
|
-
*/
|
|
359
|
-
buildStateResolved(
|
|
360
|
-
resolvedName: string,
|
|
361
|
-
resolvedParams: Params,
|
|
362
|
-
): RouteTreeState | undefined {
|
|
363
|
-
const segments = this.#store.matcher.getSegmentsByName(resolvedName);
|
|
364
|
-
|
|
365
|
-
if (!segments) {
|
|
366
|
-
return undefined;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
370
|
-
const meta = this.#store.matcher.getMetaByName(resolvedName)!;
|
|
371
|
-
|
|
372
|
-
return createRouteState(
|
|
373
|
-
{ segments, params: resolvedParams, meta },
|
|
374
|
-
resolvedName,
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// =========================================================================
|
|
379
|
-
// Query operations
|
|
380
|
-
// =========================================================================
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Checks if a route is currently active.
|
|
384
|
-
*/
|
|
385
|
-
isActiveRoute(
|
|
386
|
-
name: string,
|
|
387
|
-
params: Params = {},
|
|
388
|
-
strictEquality = false,
|
|
389
|
-
ignoreQueryParams = true,
|
|
390
|
-
): boolean {
|
|
391
|
-
// Note: empty string check is handled by Router.ts facade
|
|
392
|
-
const activeState = this.#deps.getState();
|
|
393
|
-
|
|
394
|
-
if (!activeState) {
|
|
395
|
-
return false;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
const activeName = activeState.name;
|
|
399
|
-
|
|
400
|
-
// Fast path: check if routes are related before expensive operations
|
|
401
|
-
if (
|
|
402
|
-
activeName !== name &&
|
|
403
|
-
!activeName.startsWith(`${name}.`) &&
|
|
404
|
-
!name.startsWith(`${activeName}.`)
|
|
405
|
-
) {
|
|
406
|
-
return false;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const defaultParams = this.#store.config.defaultParams[name] as
|
|
410
|
-
| Params
|
|
411
|
-
| undefined;
|
|
412
|
-
|
|
413
|
-
// Exact match case
|
|
414
|
-
if (strictEquality || activeName === name) {
|
|
415
|
-
const effectiveParams = defaultParams
|
|
416
|
-
? { ...defaultParams, ...params }
|
|
417
|
-
: params;
|
|
418
|
-
|
|
419
|
-
const targetState: State = {
|
|
420
|
-
name,
|
|
421
|
-
params: effectiveParams,
|
|
422
|
-
path: "",
|
|
423
|
-
transition: DEFAULT_TRANSITION,
|
|
424
|
-
context: {},
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
return this.#deps.areStatesEqual(
|
|
428
|
-
targetState,
|
|
429
|
-
activeState,
|
|
430
|
-
ignoreQueryParams,
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// The fast path above lets through three relations: exact (handled in
|
|
435
|
-
// the previous block), `activeName` descendant of `name`, and `name`
|
|
436
|
-
// descendant of `activeName`. Only the first two count as "active" —
|
|
437
|
-
// a link pointing DEEPER than the current state is a navigation option,
|
|
438
|
-
// not an active state. Reject the descendant-of-active case explicitly.
|
|
439
|
-
if (!activeName.startsWith(`${name}.`)) {
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// Hierarchical check: activeState is a descendant of target (name)
|
|
444
|
-
const activeParams = activeState.params;
|
|
445
|
-
|
|
446
|
-
if (!paramsMatch(params, activeParams)) {
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
if (!defaultParams) {
|
|
451
|
-
return true;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Honor `ignoreQueryParams` symmetrically with the exact-match branch
|
|
455
|
-
// above: query-only param differences (e.g. parent has
|
|
456
|
-
// `defaultParams: { sort: "asc" }` while the active descendant is
|
|
457
|
-
// `products.detail` with `params: { id: "6" }` and no sort) must not
|
|
458
|
-
// disqualify an ancestor link from being active. Strip query-typed
|
|
459
|
-
// keys of `name` from the defaults before comparison; URL-typed keys
|
|
460
|
-
// (`:id`, `:role`, etc.) are still enforced.
|
|
461
|
-
// `name` reaches this point only after the fast-path established a valid
|
|
462
|
-
// hierarchical relation AND `defaultParams` is non-null — both imply the
|
|
463
|
-
// matcher has registered the route, so `getMetaByName(name)![name]` is
|
|
464
|
-
// always defined here. Non-null assertions trade a defensive guard for
|
|
465
|
-
// honest 100% coverage.
|
|
466
|
-
const defaultsToCheck = ignoreQueryParams
|
|
467
|
-
? stripQueryDefaults(
|
|
468
|
-
defaultParams,
|
|
469
|
-
this.#store.matcher.getMetaByName(name)?.[name],
|
|
470
|
-
)
|
|
471
|
-
: defaultParams;
|
|
472
|
-
|
|
473
|
-
return paramsMatchExcluding(defaultsToCheck, activeParams, params);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
getMetaForState(
|
|
477
|
-
name: string,
|
|
478
|
-
): Record<string, Record<string, "url" | "query">> | undefined {
|
|
479
|
-
return this.#store.matcher.hasRoute(name)
|
|
480
|
-
? this.#store.matcher.getMetaByName(name)
|
|
481
|
-
: undefined;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
getUrlParams(name: string): string[] {
|
|
485
|
-
const segments = this.#store.matcher.getSegmentsByName(name);
|
|
486
|
-
|
|
487
|
-
if (!segments) {
|
|
488
|
-
return [];
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
return collectUrlParamsArray(segments as readonly RouteTree[]);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
getStore(): RoutesStore<Dependencies> {
|
|
495
|
-
return this.#store;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
#mergeDefaultParams<P extends Params = Params>(
|
|
499
|
-
routeName: string,
|
|
500
|
-
params: P,
|
|
501
|
-
): P {
|
|
502
|
-
if (Object.hasOwn(this.#store.config.defaultParams, routeName)) {
|
|
503
|
-
return {
|
|
504
|
-
...this.#store.config.defaultParams[routeName],
|
|
505
|
-
...params,
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
return params;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
#getBuildPathOptions(options?: Options): CachedBuildPathOpts {
|
|
513
|
-
if (this.#cachedBuildPathOpts) {
|
|
514
|
-
return this.#cachedBuildPathOpts;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
const ts = options?.trailingSlash;
|
|
518
|
-
|
|
519
|
-
this.#cachedBuildPathOpts = Object.freeze({
|
|
520
|
-
trailingSlash: ts === "never" || ts === "always" ? ts : undefined,
|
|
521
|
-
queryParamsMode: options?.queryParamsMode,
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
return this.#cachedBuildPathOpts;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
#resolveDynamicForward(
|
|
528
|
-
startName: string,
|
|
529
|
-
startFn: ForwardToCallback<Dependencies>,
|
|
530
|
-
params: Params,
|
|
531
|
-
): string {
|
|
532
|
-
const visited = new Set<string>([startName]);
|
|
533
|
-
|
|
534
|
-
let current = startFn(this.#deps.getDependency, params);
|
|
535
|
-
let depth = 0;
|
|
536
|
-
const MAX_DEPTH = 100;
|
|
537
|
-
|
|
538
|
-
if (typeof current !== "string") {
|
|
539
|
-
throw new TypeError(
|
|
540
|
-
`forwardTo callback must return a string, got ${typeof current}`,
|
|
541
|
-
);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
while (depth < MAX_DEPTH) {
|
|
545
|
-
if (this.#store.matcher.getSegmentsByName(current) === undefined) {
|
|
546
|
-
throw new Error(`Route "${current}" does not exist`);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
if (visited.has(current)) {
|
|
550
|
-
const chain = [...visited, current].join(" → ");
|
|
551
|
-
|
|
552
|
-
throw new Error(`Circular forwardTo detected: ${chain}`);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
visited.add(current);
|
|
556
|
-
|
|
557
|
-
if (Object.hasOwn(this.#store.config.forwardFnMap, current)) {
|
|
558
|
-
const fn = this.#store.config.forwardFnMap[
|
|
559
|
-
current
|
|
560
|
-
] as ForwardToCallback<Dependencies>;
|
|
561
|
-
|
|
562
|
-
current = fn(this.#deps.getDependency, params);
|
|
563
|
-
|
|
564
|
-
depth++;
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const staticForward = this.#store.config.forwardMap[current];
|
|
569
|
-
|
|
570
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
571
|
-
if (staticForward !== undefined) {
|
|
572
|
-
current = staticForward;
|
|
573
|
-
depth++;
|
|
574
|
-
continue;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
return current;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
throw new Error(`forwardTo exceeds maximum depth of ${MAX_DEPTH}`);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RoutesNamespace/forwardChain.ts
|
|
2
|
-
|
|
3
|
-
export function resolveForwardChain(
|
|
4
|
-
startRoute: string,
|
|
5
|
-
forwardMap: Record<string, string>,
|
|
6
|
-
maxDepth = 100,
|
|
7
|
-
): string {
|
|
8
|
-
const visited = new Set<string>();
|
|
9
|
-
const chain: string[] = [startRoute];
|
|
10
|
-
let current = startRoute;
|
|
11
|
-
|
|
12
|
-
while (forwardMap[current]) {
|
|
13
|
-
const next = forwardMap[current];
|
|
14
|
-
|
|
15
|
-
if (visited.has(next)) {
|
|
16
|
-
const cycleStart = chain.indexOf(next);
|
|
17
|
-
const cycle = [...chain.slice(cycleStart), next];
|
|
18
|
-
|
|
19
|
-
throw new Error(`Circular forwardTo: ${cycle.join(" → ")}`);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
visited.add(current);
|
|
23
|
-
chain.push(next);
|
|
24
|
-
current = next;
|
|
25
|
-
|
|
26
|
-
if (chain.length > maxDepth) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
`forwardTo chain exceeds maximum depth (${maxDepth}): ${chain.join(" → ")}`,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return current;
|
|
34
|
-
}
|