@real-router/core 0.56.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/cjs/api.d.ts +1 -1
- package/dist/cjs/api.js +1 -1
- package/dist/cjs/{cloneRouter-DRieJvam.js → cloneRouter-7z-60z_f.js} +2 -2
- package/dist/cjs/{cloneRouter-DRieJvam.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/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/utils.js +1 -1
- package/dist/cjs/validation.d.ts +1 -1
- package/dist/esm/Router-B7txWo9N.mjs +6 -0
- package/dist/esm/Router-B7txWo9N.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-BNCQ7tIa.mjs} +2 -2
- package/dist/esm/{cloneRouter-DHrH6D_z.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/index.d.mts +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/utils.mjs +1 -1
- package/dist/esm/validation.d.mts +1 -1
- package/package.json +2 -3
- 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,558 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts
|
|
2
|
-
|
|
3
|
-
import { DEFAULT_LIMITS } from "../../constants";
|
|
4
|
-
|
|
5
|
-
import type { RouteLifecycleDependencies } from "./types";
|
|
6
|
-
import type { GuardFnFactory, Limits } from "../../types";
|
|
7
|
-
import type { RouterValidator } from "../../types/RouterValidator";
|
|
8
|
-
import type { DefaultDependencies, GuardFn, State } from "@real-router/types";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Converts a boolean value to a guard function factory.
|
|
12
|
-
* Used for the shorthand syntax where true/false is passed instead of a function.
|
|
13
|
-
*/
|
|
14
|
-
function booleanToFactory<Dependencies extends DefaultDependencies>(
|
|
15
|
-
value: boolean,
|
|
16
|
-
): GuardFnFactory<Dependencies> {
|
|
17
|
-
const guardFn: GuardFn = () => value;
|
|
18
|
-
|
|
19
|
-
return () => guardFn;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Origin of a registered guard. `"definition"` — declared in route config and
|
|
24
|
-
* thus subject to `clearDefinitionGuards()` cleanup during `replace()`.
|
|
25
|
-
* `"external"` — registered post-hoc via `getLifecycleApi().add*Guard(...)`
|
|
26
|
-
* and survives `replace()`.
|
|
27
|
-
*
|
|
28
|
-
* Storage is split per origin (two Maps per kind, four Maps total) so origin
|
|
29
|
-
* is a primary invariant rather than a derived Set-tracked property. The
|
|
30
|
-
* compiled function (`#canActivateFunctions` / `#canDeactivateFunctions`)
|
|
31
|
-
* reflects the **last add wins** semantic for the slot (regardless of
|
|
32
|
-
* origin) — matching the pre-refactor behaviour and the
|
|
33
|
-
* `replaceRoutes.test.ts` "definition wins on replace" contract. On clear,
|
|
34
|
-
* the function falls back to whichever origin Map still holds an entry
|
|
35
|
-
* (external preferred if both remain after a partial clear).
|
|
36
|
-
*/
|
|
37
|
-
export type GuardOrigin = "definition" | "external";
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Independent namespace for managing route lifecycle handlers.
|
|
41
|
-
*
|
|
42
|
-
* Static methods handle input validation (called by facade).
|
|
43
|
-
* Instance methods handle state-dependent validation, storage and business logic.
|
|
44
|
-
*/
|
|
45
|
-
export class RouteLifecycleNamespace<
|
|
46
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
47
|
-
> {
|
|
48
|
-
// Storage split by origin: definition vs external. External wins at compile
|
|
49
|
-
// time for the same slot; clearDefinitionGuards / removeXGuard semantics are
|
|
50
|
-
// expressed in terms of these primary Maps.
|
|
51
|
-
readonly #definitionActivateFactories = new Map<
|
|
52
|
-
string,
|
|
53
|
-
GuardFnFactory<Dependencies>
|
|
54
|
-
>();
|
|
55
|
-
readonly #externalActivateFactories = new Map<
|
|
56
|
-
string,
|
|
57
|
-
GuardFnFactory<Dependencies>
|
|
58
|
-
>();
|
|
59
|
-
readonly #definitionDeactivateFactories = new Map<
|
|
60
|
-
string,
|
|
61
|
-
GuardFnFactory<Dependencies>
|
|
62
|
-
>();
|
|
63
|
-
readonly #externalDeactivateFactories = new Map<
|
|
64
|
-
string,
|
|
65
|
-
GuardFnFactory<Dependencies>
|
|
66
|
-
>();
|
|
67
|
-
// Compiled-function view. Single Map per kind because navigation does not
|
|
68
|
-
// distinguish origin — it just runs the effective guard. Set on add (last
|
|
69
|
-
// add wins) and recompiled on clear from whichever origin Map still holds
|
|
70
|
-
// the slot.
|
|
71
|
-
readonly #canDeactivateFunctions = new Map<string, GuardFn>();
|
|
72
|
-
readonly #canActivateFunctions = new Map<string, GuardFn>();
|
|
73
|
-
// Cached tuple — Maps never change reference, so this is stable
|
|
74
|
-
readonly #functionsTuple: [Map<string, GuardFn>, Map<string, GuardFn>] = [
|
|
75
|
-
this.#canDeactivateFunctions,
|
|
76
|
-
this.#canActivateFunctions,
|
|
77
|
-
];
|
|
78
|
-
|
|
79
|
-
readonly #registering = new Set<string>();
|
|
80
|
-
|
|
81
|
-
#deps!: RouteLifecycleDependencies<Dependencies>;
|
|
82
|
-
#limits: Limits = DEFAULT_LIMITS;
|
|
83
|
-
#getValidator: (() => RouterValidator | null) | null = null;
|
|
84
|
-
|
|
85
|
-
setDependencies(deps: RouteLifecycleDependencies<Dependencies>): void {
|
|
86
|
-
this.#deps = deps;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Updates handler registration limits (max lifecycle handlers threshold).
|
|
91
|
-
*
|
|
92
|
-
* @param limits - Limits configuration with maxLifecycleHandlers
|
|
93
|
-
*/
|
|
94
|
-
setLimits(limits: Limits): void {
|
|
95
|
-
this.#limits = limits;
|
|
96
|
-
// eslint-disable-next-line sonarjs/void-use -- @preserve: Wave 3 validator reads limits via RouterInternals; void suppresses TS6133 until then
|
|
97
|
-
void this.#limits;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
setValidatorGetter(getter: () => RouterValidator | null): void {
|
|
101
|
-
this.#getValidator = getter;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
getHandlerCount(type: "activate" | "deactivate"): number {
|
|
105
|
-
const definitionMap =
|
|
106
|
-
type === "activate"
|
|
107
|
-
? this.#definitionActivateFactories
|
|
108
|
-
: this.#definitionDeactivateFactories;
|
|
109
|
-
const externalMap =
|
|
110
|
-
type === "activate"
|
|
111
|
-
? this.#externalActivateFactories
|
|
112
|
-
: this.#externalDeactivateFactories;
|
|
113
|
-
|
|
114
|
-
if (definitionMap.size === 0) {
|
|
115
|
-
return externalMap.size;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (externalMap.size === 0) {
|
|
119
|
-
return definitionMap.size;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const names = new Set(definitionMap.keys());
|
|
123
|
-
|
|
124
|
-
for (const name of externalMap.keys()) {
|
|
125
|
-
names.add(name);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return names.size;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// =========================================================================
|
|
132
|
-
// Instance methods
|
|
133
|
-
// =========================================================================
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Adds a canActivate guard for a route.
|
|
137
|
-
*
|
|
138
|
-
* @param name - Route name (input-validated by facade)
|
|
139
|
-
* @param handler - Guard function or boolean (input-validated by facade)
|
|
140
|
-
* @param isFromDefinition - True when guard comes from route definition
|
|
141
|
-
* (lands in the definition Map; subject to `clearDefinitionGuards()`).
|
|
142
|
-
* False (default) when added via `getLifecycleApi().addActivateGuard(...)`
|
|
143
|
-
* (lands in the external Map; survives `replace()`).
|
|
144
|
-
*
|
|
145
|
-
* Last add wins at runtime: the compiled function reflects the factory
|
|
146
|
-
* passed to the most recent call, regardless of origin. Origin only
|
|
147
|
-
* determines which Map the factory is filed under (relevant for
|
|
148
|
-
* `clearDefinitionGuards()` and `cloneRouter` re-registration).
|
|
149
|
-
*/
|
|
150
|
-
addCanActivate(
|
|
151
|
-
name: string,
|
|
152
|
-
handler: GuardFnFactory<Dependencies> | boolean,
|
|
153
|
-
isFromDefinition = false,
|
|
154
|
-
): void {
|
|
155
|
-
this.#registerHandler(
|
|
156
|
-
"activate",
|
|
157
|
-
name,
|
|
158
|
-
handler,
|
|
159
|
-
isFromDefinition,
|
|
160
|
-
"canActivate",
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Adds a canDeactivate guard for a route.
|
|
166
|
-
*
|
|
167
|
-
* Symmetric counterpart to {@link addCanActivate}.
|
|
168
|
-
*/
|
|
169
|
-
addCanDeactivate(
|
|
170
|
-
name: string,
|
|
171
|
-
handler: GuardFnFactory<Dependencies> | boolean,
|
|
172
|
-
isFromDefinition = false,
|
|
173
|
-
): void {
|
|
174
|
-
this.#registerHandler(
|
|
175
|
-
"deactivate",
|
|
176
|
-
name,
|
|
177
|
-
handler,
|
|
178
|
-
isFromDefinition,
|
|
179
|
-
"canDeactivate",
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Removes a canActivate guard for a route.
|
|
185
|
-
*
|
|
186
|
-
* @param name - Route name (already validated by facade)
|
|
187
|
-
* @param origin - Optional origin filter. `"definition"` clears only the
|
|
188
|
-
* definition slot; `"external"` clears only the external slot; omitted
|
|
189
|
-
* (default) clears both — backward compatible with pre-refactor semantics.
|
|
190
|
-
*/
|
|
191
|
-
clearCanActivate(name: string, origin?: GuardOrigin): void {
|
|
192
|
-
let cleared = false;
|
|
193
|
-
|
|
194
|
-
if (origin !== "external") {
|
|
195
|
-
cleared = this.#definitionActivateFactories.delete(name) || cleared;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (origin !== "definition") {
|
|
199
|
-
cleared = this.#externalActivateFactories.delete(name) || cleared;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (cleared) {
|
|
203
|
-
this.#recompileSlot("activate", name);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Removes a canDeactivate guard for a route.
|
|
209
|
-
*
|
|
210
|
-
* Symmetric counterpart to {@link clearCanActivate}.
|
|
211
|
-
*/
|
|
212
|
-
clearCanDeactivate(name: string, origin?: GuardOrigin): void {
|
|
213
|
-
let cleared = false;
|
|
214
|
-
|
|
215
|
-
if (origin !== "external") {
|
|
216
|
-
cleared = this.#definitionDeactivateFactories.delete(name) || cleared;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (origin !== "definition") {
|
|
220
|
-
cleared = this.#externalDeactivateFactories.delete(name) || cleared;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (cleared) {
|
|
224
|
-
this.#recompileSlot("deactivate", name);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Clears all lifecycle handlers (canActivate and canDeactivate).
|
|
230
|
-
* Used by clearRoutes to reset all lifecycle state.
|
|
231
|
-
*/
|
|
232
|
-
clearAll(): void {
|
|
233
|
-
this.#definitionActivateFactories.clear();
|
|
234
|
-
this.#externalActivateFactories.clear();
|
|
235
|
-
this.#definitionDeactivateFactories.clear();
|
|
236
|
-
this.#externalDeactivateFactories.clear();
|
|
237
|
-
this.#canActivateFunctions.clear();
|
|
238
|
-
this.#canDeactivateFunctions.clear();
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Clears only lifecycle handlers that were registered from route definitions.
|
|
243
|
-
* Used by HMR `replace()` to remove definition-sourced guards without
|
|
244
|
-
* touching externally-added guards.
|
|
245
|
-
*
|
|
246
|
-
* For slots where both definition and external exist, the external factory
|
|
247
|
-
* stays and the compiled function is unchanged (external already won at
|
|
248
|
-
* registration time). For definition-only slots, the compiled function is
|
|
249
|
-
* dropped.
|
|
250
|
-
*/
|
|
251
|
-
clearDefinitionGuards(): void {
|
|
252
|
-
for (const name of this.#definitionActivateFactories.keys()) {
|
|
253
|
-
if (!this.#externalActivateFactories.has(name)) {
|
|
254
|
-
this.#canActivateFunctions.delete(name);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
for (const name of this.#definitionDeactivateFactories.keys()) {
|
|
259
|
-
if (!this.#externalDeactivateFactories.has(name)) {
|
|
260
|
-
this.#canDeactivateFunctions.delete(name);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
this.#definitionActivateFactories.clear();
|
|
265
|
-
this.#definitionDeactivateFactories.clear();
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Returns lifecycle factories as a flat `[deactivate, activate]` tuple of
|
|
270
|
-
* `Record<name, factory>` — the effective view where external wins over
|
|
271
|
-
* definition for the same slot. Used by `getRoutesApi` to enrich route
|
|
272
|
-
* objects with their current canActivate / canDeactivate factories and by
|
|
273
|
-
* the route-removal cleanup path.
|
|
274
|
-
*
|
|
275
|
-
* For cloneRouter (which needs to preserve origin on re-registration), use
|
|
276
|
-
* {@link getFactoriesByOrigin} instead.
|
|
277
|
-
*/
|
|
278
|
-
getFactories(): [
|
|
279
|
-
Record<string, GuardFnFactory<Dependencies>>,
|
|
280
|
-
Record<string, GuardFnFactory<Dependencies>>,
|
|
281
|
-
] {
|
|
282
|
-
const deactivateRecord: Record<string, GuardFnFactory<Dependencies>> = {};
|
|
283
|
-
const activateRecord: Record<string, GuardFnFactory<Dependencies>> = {};
|
|
284
|
-
|
|
285
|
-
for (const [name, factory] of this.#definitionDeactivateFactories) {
|
|
286
|
-
deactivateRecord[name] = factory;
|
|
287
|
-
}
|
|
288
|
-
for (const [name, factory] of this.#externalDeactivateFactories) {
|
|
289
|
-
deactivateRecord[name] = factory;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
for (const [name, factory] of this.#definitionActivateFactories) {
|
|
293
|
-
activateRecord[name] = factory;
|
|
294
|
-
}
|
|
295
|
-
for (const [name, factory] of this.#externalActivateFactories) {
|
|
296
|
-
activateRecord[name] = factory;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return [deactivateRecord, activateRecord];
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Returns factories tagged by origin — definition and external as separate
|
|
304
|
-
* `[deactivate, activate]` tuples. Used by `cloneRouter` to re-register
|
|
305
|
-
* guards on the clone with their original origin flag preserved.
|
|
306
|
-
*/
|
|
307
|
-
getFactoriesByOrigin(): {
|
|
308
|
-
definition: [
|
|
309
|
-
Record<string, GuardFnFactory<Dependencies>>,
|
|
310
|
-
Record<string, GuardFnFactory<Dependencies>>,
|
|
311
|
-
];
|
|
312
|
-
external: [
|
|
313
|
-
Record<string, GuardFnFactory<Dependencies>>,
|
|
314
|
-
Record<string, GuardFnFactory<Dependencies>>,
|
|
315
|
-
];
|
|
316
|
-
} {
|
|
317
|
-
const defDeact: Record<string, GuardFnFactory<Dependencies>> = {};
|
|
318
|
-
const defAct: Record<string, GuardFnFactory<Dependencies>> = {};
|
|
319
|
-
const extensionDeact: Record<string, GuardFnFactory<Dependencies>> = {};
|
|
320
|
-
const extensionAct: Record<string, GuardFnFactory<Dependencies>> = {};
|
|
321
|
-
|
|
322
|
-
for (const [name, factory] of this.#definitionDeactivateFactories) {
|
|
323
|
-
defDeact[name] = factory;
|
|
324
|
-
}
|
|
325
|
-
for (const [name, factory] of this.#definitionActivateFactories) {
|
|
326
|
-
defAct[name] = factory;
|
|
327
|
-
}
|
|
328
|
-
for (const [name, factory] of this.#externalDeactivateFactories) {
|
|
329
|
-
extensionDeact[name] = factory;
|
|
330
|
-
}
|
|
331
|
-
for (const [name, factory] of this.#externalActivateFactories) {
|
|
332
|
-
extensionAct[name] = factory;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
return {
|
|
336
|
-
definition: [defDeact, defAct],
|
|
337
|
-
external: [extensionDeact, extensionAct],
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Returns compiled lifecycle functions for transition execution.
|
|
343
|
-
*
|
|
344
|
-
* @returns Tuple of [canDeactivateFunctions, canActivateFunctions] as Maps
|
|
345
|
-
*/
|
|
346
|
-
getFunctions(): [Map<string, GuardFn>, Map<string, GuardFn>] {
|
|
347
|
-
return this.#functionsTuple;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
canNavigateTo(
|
|
351
|
-
toDeactivate: string[],
|
|
352
|
-
toActivate: string[],
|
|
353
|
-
toState: State,
|
|
354
|
-
fromState: State | undefined,
|
|
355
|
-
): boolean {
|
|
356
|
-
for (const segment of toDeactivate) {
|
|
357
|
-
if (
|
|
358
|
-
!this.#checkGuardSync(
|
|
359
|
-
this.#canDeactivateFunctions,
|
|
360
|
-
segment,
|
|
361
|
-
toState,
|
|
362
|
-
fromState,
|
|
363
|
-
"canNavigateTo",
|
|
364
|
-
)
|
|
365
|
-
) {
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
for (const segment of toActivate) {
|
|
371
|
-
if (
|
|
372
|
-
!this.#checkGuardSync(
|
|
373
|
-
this.#canActivateFunctions,
|
|
374
|
-
segment,
|
|
375
|
-
toState,
|
|
376
|
-
fromState,
|
|
377
|
-
"canNavigateTo",
|
|
378
|
-
)
|
|
379
|
-
) {
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return true;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// =========================================================================
|
|
388
|
-
// Private methods (business logic)
|
|
389
|
-
// =========================================================================
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Routes a registration into the origin-specific factory Map and compiles
|
|
393
|
-
* the just-added factory (last add wins for the compiled function).
|
|
394
|
-
* Emits overwrite / threshold warnings symmetric with the pre-refactor
|
|
395
|
-
* single-Map behaviour: any prior entry for the slot — same origin or
|
|
396
|
-
* cross-origin — counts as an overwrite for the warning surface; only a
|
|
397
|
-
* brand-new slot (no entry in either Map) increments the threshold check.
|
|
398
|
-
*/
|
|
399
|
-
#registerHandler(
|
|
400
|
-
type: "activate" | "deactivate",
|
|
401
|
-
name: string,
|
|
402
|
-
handler: GuardFnFactory<Dependencies> | boolean,
|
|
403
|
-
isFromDefinition: boolean,
|
|
404
|
-
methodName: string,
|
|
405
|
-
): void {
|
|
406
|
-
const factoryMaps = this.#getFactoryMaps(type);
|
|
407
|
-
const functions =
|
|
408
|
-
type === "activate"
|
|
409
|
-
? this.#canActivateFunctions
|
|
410
|
-
: this.#canDeactivateFunctions;
|
|
411
|
-
const targetMap = isFromDefinition
|
|
412
|
-
? factoryMaps.definition
|
|
413
|
-
: factoryMaps.external;
|
|
414
|
-
const otherMap = isFromDefinition
|
|
415
|
-
? factoryMaps.external
|
|
416
|
-
: factoryMaps.definition;
|
|
417
|
-
|
|
418
|
-
const isOverwrite = targetMap.has(name) || otherMap.has(name);
|
|
419
|
-
|
|
420
|
-
if (isOverwrite) {
|
|
421
|
-
this.#getValidator?.()?.lifecycle.warnOverwrite(name, type, methodName);
|
|
422
|
-
} else {
|
|
423
|
-
this.#getValidator?.()?.lifecycle.validateCountThresholds(
|
|
424
|
-
this.getHandlerCount(type) + 1,
|
|
425
|
-
methodName,
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const factory =
|
|
430
|
-
typeof handler === "boolean"
|
|
431
|
-
? booleanToFactory<Dependencies>(handler)
|
|
432
|
-
: handler;
|
|
433
|
-
|
|
434
|
-
targetMap.set(name, factory);
|
|
435
|
-
|
|
436
|
-
// Mark route as being registered before calling user factory
|
|
437
|
-
this.#registering.add(name);
|
|
438
|
-
|
|
439
|
-
try {
|
|
440
|
-
const fn = this.#deps.compileFactory(factory);
|
|
441
|
-
|
|
442
|
-
if (typeof fn !== "function") {
|
|
443
|
-
throw new TypeError(
|
|
444
|
-
`[router.${methodName}] Factory must return a function, got ${typeof fn}`,
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
functions.set(name, fn);
|
|
449
|
-
} catch (error) {
|
|
450
|
-
// Rollback the slot we just touched to keep storage consistent. If a
|
|
451
|
-
// cross-origin entry exists for the same name, its compiled function
|
|
452
|
-
// remains in place — recompile so navigation still sees a valid guard.
|
|
453
|
-
targetMap.delete(name);
|
|
454
|
-
|
|
455
|
-
if (otherMap.has(name)) {
|
|
456
|
-
this.#recompileSlot(type, name);
|
|
457
|
-
} else {
|
|
458
|
-
functions.delete(name);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
throw error;
|
|
462
|
-
} finally {
|
|
463
|
-
this.#registering.delete(name);
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Recompiles the compiled-function slot from whichever origin Map still has
|
|
469
|
-
* an entry for `name` after a clear. External wins over definition; if
|
|
470
|
-
* neither has an entry, the compiled function is deleted.
|
|
471
|
-
*/
|
|
472
|
-
#recompileSlot(type: "activate" | "deactivate", name: string): void {
|
|
473
|
-
const factoryMaps = this.#getFactoryMaps(type);
|
|
474
|
-
const functions =
|
|
475
|
-
type === "activate"
|
|
476
|
-
? this.#canActivateFunctions
|
|
477
|
-
: this.#canDeactivateFunctions;
|
|
478
|
-
|
|
479
|
-
const effective =
|
|
480
|
-
factoryMaps.external.get(name) ?? factoryMaps.definition.get(name);
|
|
481
|
-
|
|
482
|
-
if (!effective) {
|
|
483
|
-
functions.delete(name);
|
|
484
|
-
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
try {
|
|
489
|
-
const fn = this.#deps.compileFactory(effective);
|
|
490
|
-
|
|
491
|
-
/* v8 ignore next 4 -- @preserve: stored factories were validated at add time, compileFactory should yield a function on second call too */
|
|
492
|
-
if (typeof fn !== "function") {
|
|
493
|
-
functions.delete(name);
|
|
494
|
-
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
functions.set(name, fn);
|
|
499
|
-
} catch {
|
|
500
|
-
/* v8 ignore next 2 -- @preserve: defensive — a user-provided factory could theoretically throw on re-compile (state changed since add time); deleting the function blocks navigation on that slot */
|
|
501
|
-
functions.delete(name);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
#getFactoryMaps(type: "activate" | "deactivate"): {
|
|
506
|
-
definition: Map<string, GuardFnFactory<Dependencies>>;
|
|
507
|
-
external: Map<string, GuardFnFactory<Dependencies>>;
|
|
508
|
-
} {
|
|
509
|
-
return type === "activate"
|
|
510
|
-
? {
|
|
511
|
-
definition: this.#definitionActivateFactories,
|
|
512
|
-
external: this.#externalActivateFactories,
|
|
513
|
-
}
|
|
514
|
-
: {
|
|
515
|
-
definition: this.#definitionDeactivateFactories,
|
|
516
|
-
external: this.#externalDeactivateFactories,
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* Shared implementation for synchronous guard checks.
|
|
522
|
-
* Warns if a guard returns a Promise (async guards are not supported in sync mode).
|
|
523
|
-
* Catches exceptions and treats them as navigation-blocking (`false`).
|
|
524
|
-
*
|
|
525
|
-
* @param functions - Map of compiled guard functions to look up
|
|
526
|
-
* @param name - Route name to check the guard for
|
|
527
|
-
* @param toState - Target navigation state
|
|
528
|
-
* @param fromState - Current state (`undefined` on initial navigation)
|
|
529
|
-
* @param methodName - Public API method name for warning messages
|
|
530
|
-
*/
|
|
531
|
-
#checkGuardSync(
|
|
532
|
-
functions: Map<string, GuardFn>,
|
|
533
|
-
name: string,
|
|
534
|
-
toState: State,
|
|
535
|
-
fromState: State | undefined,
|
|
536
|
-
methodName: string,
|
|
537
|
-
): boolean {
|
|
538
|
-
const guardFn = functions.get(name);
|
|
539
|
-
|
|
540
|
-
if (!guardFn) {
|
|
541
|
-
return true;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
try {
|
|
545
|
-
const result = guardFn(toState, fromState);
|
|
546
|
-
|
|
547
|
-
if (typeof result === "boolean") {
|
|
548
|
-
return result;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
this.#getValidator?.()?.lifecycle.warnAsyncGuardSync(name, methodName);
|
|
552
|
-
|
|
553
|
-
return false;
|
|
554
|
-
} catch {
|
|
555
|
-
return false;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RouteLifecycleNamespace/types.ts
|
|
2
|
-
|
|
3
|
-
import type { GuardFnFactory } from "../../types";
|
|
4
|
-
import type { DefaultDependencies, GuardFn } from "@real-router/types";
|
|
5
|
-
|
|
6
|
-
export interface RouteLifecycleDependencies<
|
|
7
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
8
|
-
> {
|
|
9
|
-
compileFactory: (factory: GuardFnFactory<Dependencies>) => GuardFn;
|
|
10
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts
|
|
2
|
-
|
|
3
|
-
import { errorCodes } from "../../constants";
|
|
4
|
-
import { RouterError } from "../../RouterError";
|
|
5
|
-
|
|
6
|
-
import type { RouterLifecycleDependencies } from "./types";
|
|
7
|
-
import type { NavigationOptions, State } from "@real-router/types";
|
|
8
|
-
|
|
9
|
-
const REPLACE_OPTS: NavigationOptions = { replace: true };
|
|
10
|
-
|
|
11
|
-
Object.freeze(REPLACE_OPTS);
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Independent namespace for managing router lifecycle.
|
|
15
|
-
*
|
|
16
|
-
* Handles start() and stop(). Lifecycle state (isActive, isStarted) is managed
|
|
17
|
-
* by RouterFSM in the facade (Router.ts).
|
|
18
|
-
*/
|
|
19
|
-
export class RouterLifecycleNamespace {
|
|
20
|
-
#deps!: RouterLifecycleDependencies;
|
|
21
|
-
|
|
22
|
-
// =========================================================================
|
|
23
|
-
// Dependency injection
|
|
24
|
-
// =========================================================================
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Sets dependencies for lifecycle operations.
|
|
28
|
-
* Must be called before using lifecycle methods.
|
|
29
|
-
*/
|
|
30
|
-
setDependencies(deps: RouterLifecycleDependencies): void {
|
|
31
|
-
this.#deps = deps;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// =========================================================================
|
|
35
|
-
// Instance methods
|
|
36
|
-
// =========================================================================
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Starts the router with the given path.
|
|
40
|
-
*
|
|
41
|
-
* Guards (concurrent start, already started) are handled by the facade via
|
|
42
|
-
* RouterFSM state checks before this method is called.
|
|
43
|
-
*/
|
|
44
|
-
async start(startPath: string): Promise<State> {
|
|
45
|
-
const deps = this.#deps;
|
|
46
|
-
const options = deps.getOptions();
|
|
47
|
-
|
|
48
|
-
const matchedState = deps.matchPath(startPath);
|
|
49
|
-
|
|
50
|
-
if (!matchedState && !options.allowNotFound) {
|
|
51
|
-
const err = new RouterError(errorCodes.ROUTE_NOT_FOUND, {
|
|
52
|
-
path: startPath,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
deps.emitTransitionError(undefined, undefined, err);
|
|
56
|
-
|
|
57
|
-
throw err;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
deps.completeStart();
|
|
61
|
-
|
|
62
|
-
if (matchedState) {
|
|
63
|
-
// navigateToState commits matchedState verbatim — same primitive URL
|
|
64
|
-
// plugins use on popstate / navigate-event (#525). Keeps trailing-slash
|
|
65
|
-
// and any other source-URL flavor that matchPath produced; skips the
|
|
66
|
-
// redundant forwardState+buildPath round-trip in buildNavigateState.
|
|
67
|
-
return deps.navigateToState(matchedState, REPLACE_OPTS);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return deps.navigateToNotFound(startPath);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Stops the router and resets state.
|
|
75
|
-
*
|
|
76
|
-
* Called only for READY/TRANSITION_STARTED states (facade handles STARTING/IDLE/DISPOSED).
|
|
77
|
-
*/
|
|
78
|
-
stop(): void {
|
|
79
|
-
this.#deps.clearState();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RouterLifecycleNamespace/constants.ts
|
|
2
|
-
|
|
3
|
-
import { errorCodes } from "../../constants";
|
|
4
|
-
import { RouterError } from "../../RouterError";
|
|
5
|
-
|
|
6
|
-
// =============================================================================
|
|
7
|
-
// Cached Errors (Performance Optimization)
|
|
8
|
-
// =============================================================================
|
|
9
|
-
// Pre-create error instances to avoid object allocation on hot paths.
|
|
10
|
-
// Error creation involves: new object, stack trace capture (~500ns-2μs).
|
|
11
|
-
// Cached errors skip this overhead entirely.
|
|
12
|
-
//
|
|
13
|
-
// Trade-off: All error instances share the same stack trace (points here).
|
|
14
|
-
// This is acceptable because:
|
|
15
|
-
// 1. These errors indicate user misconfiguration, not internal bugs
|
|
16
|
-
// 2. Error code and message are sufficient for debugging
|
|
17
|
-
// 3. Performance gain (~80% for error paths) outweighs stack trace loss
|
|
18
|
-
// =============================================================================
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Cached error for start() called when router is already started/starting.
|
|
22
|
-
*/
|
|
23
|
-
export const CACHED_ALREADY_STARTED_ERROR = new RouterError(
|
|
24
|
-
errorCodes.ROUTER_ALREADY_STARTED,
|
|
25
|
-
);
|