@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,108 +0,0 @@
|
|
|
1
|
-
import { errorCodes, constants } from "../../../constants";
|
|
2
|
-
import { RouterError } from "../../../RouterError";
|
|
3
|
-
|
|
4
|
-
import type { NavigationDependencies, NavigationContext } from "../types";
|
|
5
|
-
import type {
|
|
6
|
-
NavigationOptions,
|
|
7
|
-
State,
|
|
8
|
-
TransitionMeta,
|
|
9
|
-
} from "@real-router/types";
|
|
10
|
-
|
|
11
|
-
type MutableTransitionMeta = {
|
|
12
|
-
-readonly [K in keyof TransitionMeta]: TransitionMeta[K];
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
function buildTransitionMeta(
|
|
16
|
-
fromState: State | undefined,
|
|
17
|
-
opts: NavigationOptions,
|
|
18
|
-
toDeactivate: string[],
|
|
19
|
-
toActivate: string[],
|
|
20
|
-
intersection: string,
|
|
21
|
-
): TransitionMeta {
|
|
22
|
-
Object.freeze(toDeactivate);
|
|
23
|
-
Object.freeze(toActivate);
|
|
24
|
-
|
|
25
|
-
const segments = Object.freeze({
|
|
26
|
-
deactivated: toDeactivate,
|
|
27
|
-
activated: toActivate,
|
|
28
|
-
intersection,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const meta: MutableTransitionMeta = {
|
|
32
|
-
phase: "activating",
|
|
33
|
-
reason: "success",
|
|
34
|
-
segments,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
if (fromState?.name !== undefined) {
|
|
38
|
-
meta.from = fromState.name;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (opts.reload !== undefined) {
|
|
42
|
-
meta.reload = opts.reload;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (opts.replace !== undefined) {
|
|
46
|
-
meta.replace = opts.replace;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (opts.redirected !== undefined) {
|
|
50
|
-
meta.redirected = opts.redirected;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return Object.freeze(meta);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function stripSignal({
|
|
57
|
-
signal: _,
|
|
58
|
-
...rest
|
|
59
|
-
}: NavigationOptions): NavigationOptions {
|
|
60
|
-
return rest;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function completeTransition(
|
|
64
|
-
deps: NavigationDependencies,
|
|
65
|
-
nav: NavigationContext,
|
|
66
|
-
): State {
|
|
67
|
-
const { toState, fromState, opts, toDeactivate, toActivate, intersection } =
|
|
68
|
-
nav;
|
|
69
|
-
|
|
70
|
-
if (
|
|
71
|
-
toState.name !== constants.UNKNOWN_ROUTE &&
|
|
72
|
-
!deps.hasRoute(toState.name)
|
|
73
|
-
) {
|
|
74
|
-
const err = new RouterError(errorCodes.ROUTE_NOT_FOUND, {
|
|
75
|
-
routeName: toState.name,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
deps.sendTransitionFail(toState, fromState, err);
|
|
79
|
-
|
|
80
|
-
throw err;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (fromState) {
|
|
84
|
-
for (const name of toDeactivate) {
|
|
85
|
-
if (!toActivate.includes(name) && nav.canDeactivateFunctions.has(name)) {
|
|
86
|
-
deps.clearCanDeactivate(name);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
(toState as { transition: TransitionMeta }).transition = buildTransitionMeta(
|
|
92
|
-
fromState,
|
|
93
|
-
opts,
|
|
94
|
-
toDeactivate,
|
|
95
|
-
toActivate,
|
|
96
|
-
intersection,
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const finalState = Object.freeze(toState);
|
|
100
|
-
|
|
101
|
-
deps.setState(finalState);
|
|
102
|
-
|
|
103
|
-
const transitionOpts = opts.signal === undefined ? opts : stripSignal(opts);
|
|
104
|
-
|
|
105
|
-
deps.sendTransitionDone(finalState, fromState, transitionOpts);
|
|
106
|
-
|
|
107
|
-
return finalState;
|
|
108
|
-
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/NavigationNamespace/transition/errorHandling.ts
|
|
2
|
-
|
|
3
|
-
import { errorCodes } from "../../../constants";
|
|
4
|
-
import { RouterError } from "../../../RouterError";
|
|
5
|
-
|
|
6
|
-
import type { NavigationDependencies } from "../types";
|
|
7
|
-
import type { State } from "@real-router/types";
|
|
8
|
-
|
|
9
|
-
export function routeTransitionError(
|
|
10
|
-
deps: NavigationDependencies,
|
|
11
|
-
error: unknown,
|
|
12
|
-
toState: State,
|
|
13
|
-
fromState: State | undefined,
|
|
14
|
-
): void {
|
|
15
|
-
const routerError = error as RouterError;
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
routerError.code === errorCodes.TRANSITION_CANCELLED ||
|
|
19
|
-
routerError.code === errorCodes.ROUTE_NOT_FOUND
|
|
20
|
-
) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
deps.sendTransitionFail(toState, fromState, routerError);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function handleGuardError(
|
|
28
|
-
error: unknown,
|
|
29
|
-
errorCode: string,
|
|
30
|
-
segment: string,
|
|
31
|
-
): never {
|
|
32
|
-
if (error instanceof DOMException && error.name === "AbortError") {
|
|
33
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
rethrowAsRouterError(error, errorCode, segment);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Error metadata structure for transition errors.
|
|
41
|
-
* Contains information extracted from caught exceptions.
|
|
42
|
-
*/
|
|
43
|
-
export interface SyncErrorMetadata {
|
|
44
|
-
[key: string]: unknown;
|
|
45
|
-
message?: string;
|
|
46
|
-
stack?: string | undefined;
|
|
47
|
-
cause?: unknown;
|
|
48
|
-
segment?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Re-throws a caught error as a RouterError with the given error code.
|
|
53
|
-
* If the error is already a RouterError, sets the code directly.
|
|
54
|
-
* Otherwise wraps it with wrapSyncError metadata.
|
|
55
|
-
*/
|
|
56
|
-
export function rethrowAsRouterError(
|
|
57
|
-
error: unknown,
|
|
58
|
-
errorCode: string,
|
|
59
|
-
segment: string,
|
|
60
|
-
): never {
|
|
61
|
-
if (error instanceof RouterError) {
|
|
62
|
-
error.setCode(errorCode);
|
|
63
|
-
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
throw new RouterError(errorCode, wrapSyncError(error, segment));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const reservedRouterErrorProps = new Set([
|
|
71
|
-
"code",
|
|
72
|
-
"segment",
|
|
73
|
-
"path",
|
|
74
|
-
"redirect",
|
|
75
|
-
]);
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Wraps a synchronously thrown value into structured error metadata.
|
|
79
|
-
*
|
|
80
|
-
* This helper extracts useful debugging information from various thrown values:
|
|
81
|
-
* - Error instances: extracts message, stack, and cause (ES2022+)
|
|
82
|
-
* - Plain objects: spreads properties into metadata
|
|
83
|
-
* - Primitives (string, number, etc.): returns minimal metadata
|
|
84
|
-
*
|
|
85
|
-
* @param thrown - The value caught in a try-catch block
|
|
86
|
-
* @param segment - Route segment name (for lifecycle hooks)
|
|
87
|
-
* @returns Structured error metadata for RouterError
|
|
88
|
-
*/
|
|
89
|
-
export function wrapSyncError(
|
|
90
|
-
thrown: unknown,
|
|
91
|
-
segment: string,
|
|
92
|
-
): SyncErrorMetadata {
|
|
93
|
-
const base: SyncErrorMetadata = { segment };
|
|
94
|
-
|
|
95
|
-
// Handle Error instances - extract all useful properties
|
|
96
|
-
if (thrown instanceof Error) {
|
|
97
|
-
return {
|
|
98
|
-
...base,
|
|
99
|
-
message: thrown.message,
|
|
100
|
-
stack: thrown.stack,
|
|
101
|
-
// Error.cause requires ES2022+ - safely access if present
|
|
102
|
-
...("cause" in thrown &&
|
|
103
|
-
thrown.cause !== undefined && { cause: thrown.cause }),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Handle plain objects - spread properties into metadata, filtering reserved props
|
|
108
|
-
if (thrown && typeof thrown === "object") {
|
|
109
|
-
const filtered: Record<string, unknown> = {};
|
|
110
|
-
|
|
111
|
-
for (const [key, value] of Object.entries(thrown)) {
|
|
112
|
-
// Issue #39: Skip reserved properties to avoid RouterError constructor TypeError
|
|
113
|
-
if (!reservedRouterErrorProps.has(key)) {
|
|
114
|
-
filtered[key] = value;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return { ...base, ...filtered };
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Primitives (string, number, boolean, null, undefined, symbol, bigint)
|
|
122
|
-
// Return base metadata only - the primitive value isn't useful as metadata
|
|
123
|
-
return base;
|
|
124
|
-
}
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import { handleGuardError } from "./errorHandling";
|
|
2
|
-
import { errorCodes } from "../../../constants";
|
|
3
|
-
import { RouterError } from "../../../RouterError";
|
|
4
|
-
|
|
5
|
-
import type { GuardFn, State } from "@real-router/types";
|
|
6
|
-
|
|
7
|
-
async function resolveAsyncGuard(
|
|
8
|
-
promise: Promise<boolean>,
|
|
9
|
-
errorCode: string,
|
|
10
|
-
segment: string,
|
|
11
|
-
): Promise<void> {
|
|
12
|
-
let result: boolean;
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
result = await promise;
|
|
16
|
-
} catch (error: unknown) {
|
|
17
|
-
handleGuardError(error, errorCode, segment);
|
|
18
|
-
|
|
19
|
-
return; // unreachable — handleGuardError returns never
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!result) {
|
|
23
|
-
throw new RouterError(errorCode, { segment });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function resolveRemainingGuards( // NOSONAR -- params kept flat to avoid object allocation on hot path
|
|
28
|
-
guards: Map<string, GuardFn>,
|
|
29
|
-
segments: string[],
|
|
30
|
-
errorCode: string,
|
|
31
|
-
toState: State,
|
|
32
|
-
fromState: State | undefined,
|
|
33
|
-
signal: AbortSignal | undefined,
|
|
34
|
-
isActive: () => boolean,
|
|
35
|
-
startIndex: number,
|
|
36
|
-
firstResult: Promise<boolean>,
|
|
37
|
-
firstSegment: string,
|
|
38
|
-
): Promise<void> {
|
|
39
|
-
await resolveAsyncGuard(firstResult, errorCode, firstSegment);
|
|
40
|
-
|
|
41
|
-
for (let i = startIndex; i < segments.length; i++) {
|
|
42
|
-
if (!isActive()) {
|
|
43
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const segment = segments[i];
|
|
47
|
-
const guardFn = guards.get(segment);
|
|
48
|
-
|
|
49
|
-
if (!guardFn) {
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let guardResult: boolean | Promise<boolean> = false;
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
guardResult = guardFn(toState, fromState, signal);
|
|
57
|
-
} catch (error: unknown) {
|
|
58
|
-
handleGuardError(error, errorCode, segment);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (guardResult instanceof Promise) {
|
|
62
|
-
await resolveAsyncGuard(guardResult, errorCode, segment);
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!guardResult) {
|
|
67
|
-
throw new RouterError(errorCode, { segment });
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async function finishAsyncPipeline( // NOSONAR
|
|
73
|
-
deactivateCompletion: Promise<void>,
|
|
74
|
-
activateGuards: Map<string, GuardFn>,
|
|
75
|
-
toActivate: string[],
|
|
76
|
-
shouldActivate: boolean,
|
|
77
|
-
toState: State,
|
|
78
|
-
fromState: State | undefined,
|
|
79
|
-
signal: AbortSignal,
|
|
80
|
-
isActive: () => boolean,
|
|
81
|
-
emitLeaveApprove: () => Promise<void> | undefined,
|
|
82
|
-
): Promise<void> {
|
|
83
|
-
await deactivateCompletion;
|
|
84
|
-
|
|
85
|
-
if (!isActive()) {
|
|
86
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const leaveResult = emitLeaveApprove();
|
|
90
|
-
|
|
91
|
-
if (leaveResult !== undefined) {
|
|
92
|
-
await leaveResult;
|
|
93
|
-
|
|
94
|
-
/* v8 ignore next 3 -- @preserve: V8 cannot track cancellation check through async leave continuation after Promise.allSettled */
|
|
95
|
-
if (!isActive()) {
|
|
96
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (shouldActivate) {
|
|
101
|
-
const pending = runGuards(
|
|
102
|
-
activateGuards,
|
|
103
|
-
toActivate,
|
|
104
|
-
errorCodes.CANNOT_ACTIVATE,
|
|
105
|
-
toState,
|
|
106
|
-
fromState,
|
|
107
|
-
signal,
|
|
108
|
-
isActive,
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
if (pending !== undefined) {
|
|
112
|
-
await pending;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (!isActive()) {
|
|
116
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export function executeGuardPipeline( // NOSONAR
|
|
122
|
-
deactivateGuards: Map<string, GuardFn>,
|
|
123
|
-
activateGuards: Map<string, GuardFn>,
|
|
124
|
-
toDeactivate: string[],
|
|
125
|
-
toActivate: string[],
|
|
126
|
-
shouldDeactivate: boolean,
|
|
127
|
-
shouldActivate: boolean,
|
|
128
|
-
toState: State,
|
|
129
|
-
fromState: State | undefined,
|
|
130
|
-
signal: AbortSignal,
|
|
131
|
-
isActive: () => boolean,
|
|
132
|
-
emitLeaveApprove: () => Promise<void> | undefined,
|
|
133
|
-
): Promise<void> | undefined {
|
|
134
|
-
if (shouldDeactivate) {
|
|
135
|
-
const pending = runGuards(
|
|
136
|
-
deactivateGuards,
|
|
137
|
-
toDeactivate,
|
|
138
|
-
errorCodes.CANNOT_DEACTIVATE,
|
|
139
|
-
toState,
|
|
140
|
-
fromState,
|
|
141
|
-
signal,
|
|
142
|
-
isActive,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
if (pending !== undefined) {
|
|
146
|
-
return finishAsyncPipeline(
|
|
147
|
-
pending,
|
|
148
|
-
activateGuards,
|
|
149
|
-
toActivate,
|
|
150
|
-
shouldActivate,
|
|
151
|
-
toState,
|
|
152
|
-
fromState,
|
|
153
|
-
signal,
|
|
154
|
-
isActive,
|
|
155
|
-
emitLeaveApprove,
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (!isActive()) {
|
|
161
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const leaveResult = emitLeaveApprove();
|
|
165
|
-
|
|
166
|
-
if (leaveResult !== undefined) {
|
|
167
|
-
return finishAfterAsyncLeave(
|
|
168
|
-
leaveResult,
|
|
169
|
-
/* v8 ignore next -- @preserve: false-branch unreachable — navigateToNotFound bypasses guards pipeline */
|
|
170
|
-
shouldActivate ? activateGuards : undefined,
|
|
171
|
-
toActivate,
|
|
172
|
-
toState,
|
|
173
|
-
fromState,
|
|
174
|
-
signal,
|
|
175
|
-
isActive,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (shouldActivate) {
|
|
180
|
-
return runGuards(
|
|
181
|
-
activateGuards,
|
|
182
|
-
toActivate,
|
|
183
|
-
errorCodes.CANNOT_ACTIVATE,
|
|
184
|
-
toState,
|
|
185
|
-
fromState,
|
|
186
|
-
signal,
|
|
187
|
-
isActive,
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return undefined;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async function finishAfterAsyncLeave(
|
|
195
|
-
leaveCompletion: Promise<void>,
|
|
196
|
-
activateGuards: Map<string, GuardFn> | undefined,
|
|
197
|
-
toActivate: string[],
|
|
198
|
-
toState: State,
|
|
199
|
-
fromState: State | undefined,
|
|
200
|
-
signal: AbortSignal,
|
|
201
|
-
isActive: () => boolean,
|
|
202
|
-
): Promise<void> {
|
|
203
|
-
await leaveCompletion;
|
|
204
|
-
|
|
205
|
-
/* v8 ignore next 3 -- @preserve: unreachable after #663 — signal abort
|
|
206
|
-
mid-leave rejects via settleLeavePromises, so `await leaveCompletion`
|
|
207
|
-
throws directly instead of completing with a stale isActive() */
|
|
208
|
-
if (!isActive()) {
|
|
209
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/* v8 ignore next -- @preserve: false-branch unreachable — navigateToNotFound bypasses guards pipeline */
|
|
213
|
-
if (activateGuards !== undefined) {
|
|
214
|
-
const pending = runGuards(
|
|
215
|
-
activateGuards,
|
|
216
|
-
toActivate,
|
|
217
|
-
errorCodes.CANNOT_ACTIVATE,
|
|
218
|
-
toState,
|
|
219
|
-
fromState,
|
|
220
|
-
signal,
|
|
221
|
-
isActive,
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
if (pending !== undefined) {
|
|
225
|
-
await pending;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (!isActive()) {
|
|
229
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function runGuards(
|
|
235
|
-
guards: Map<string, GuardFn>,
|
|
236
|
-
segments: string[],
|
|
237
|
-
errorCode: string,
|
|
238
|
-
toState: State,
|
|
239
|
-
fromState: State | undefined,
|
|
240
|
-
signal: AbortSignal | undefined,
|
|
241
|
-
isActive: () => boolean,
|
|
242
|
-
): Promise<void> | undefined {
|
|
243
|
-
for (const [i, segment] of segments.entries()) {
|
|
244
|
-
if (!isActive()) {
|
|
245
|
-
throw new RouterError(errorCodes.TRANSITION_CANCELLED);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const guardFn = guards.get(segment);
|
|
249
|
-
|
|
250
|
-
if (!guardFn) {
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
let guardResult: boolean | Promise<boolean> = false;
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
guardResult = guardFn(toState, fromState, signal);
|
|
258
|
-
} catch (error: unknown) {
|
|
259
|
-
handleGuardError(error, errorCode, segment);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (guardResult instanceof Promise) {
|
|
263
|
-
return resolveRemainingGuards(
|
|
264
|
-
guards,
|
|
265
|
-
segments,
|
|
266
|
-
errorCode,
|
|
267
|
-
toState,
|
|
268
|
-
fromState,
|
|
269
|
-
signal,
|
|
270
|
-
isActive,
|
|
271
|
-
i + 1,
|
|
272
|
-
guardResult,
|
|
273
|
-
segment,
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (!guardResult) {
|
|
278
|
-
throw new RouterError(errorCode, { segment });
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
return undefined;
|
|
283
|
-
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/NavigationNamespace/types.ts
|
|
2
|
-
|
|
3
|
-
import type {
|
|
4
|
-
GuardFn,
|
|
5
|
-
NavigationOptions,
|
|
6
|
-
Options,
|
|
7
|
-
Params,
|
|
8
|
-
State,
|
|
9
|
-
} from "@real-router/types";
|
|
10
|
-
|
|
11
|
-
export interface NavigationContext {
|
|
12
|
-
toState: State;
|
|
13
|
-
fromState: State | undefined;
|
|
14
|
-
opts: NavigationOptions;
|
|
15
|
-
toDeactivate: string[];
|
|
16
|
-
toActivate: string[];
|
|
17
|
-
intersection: string;
|
|
18
|
-
canDeactivateFunctions: Map<string, GuardFn>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Dependencies injected into NavigationNamespace.
|
|
23
|
-
*
|
|
24
|
-
* These are function references from other namespaces/facade,
|
|
25
|
-
* avoiding the need to pass the entire Router object.
|
|
26
|
-
*/
|
|
27
|
-
export interface NavigationDependencies {
|
|
28
|
-
/** Get router options */
|
|
29
|
-
getOptions: () => Options;
|
|
30
|
-
|
|
31
|
-
/** Check if route exists */
|
|
32
|
-
hasRoute: (name: string) => boolean;
|
|
33
|
-
|
|
34
|
-
/** Get current state */
|
|
35
|
-
getState: () => State | undefined;
|
|
36
|
-
|
|
37
|
-
/** Set router state */
|
|
38
|
-
setState: (state: State) => void;
|
|
39
|
-
|
|
40
|
-
/** Build complete navigate state: forwardState + route check + buildPath + makeState in one step */
|
|
41
|
-
buildNavigateState: (
|
|
42
|
-
routeName: string,
|
|
43
|
-
routeParams: Params,
|
|
44
|
-
) => State | undefined;
|
|
45
|
-
|
|
46
|
-
/** Resolve defaultRoute and defaultParams options (static value or callback) */
|
|
47
|
-
resolveDefault: () => { route: string; params: Params };
|
|
48
|
-
|
|
49
|
-
/** Start transition and send NAVIGATE event to routerFSM */
|
|
50
|
-
startTransition: (toState: State, fromState: State | undefined) => void;
|
|
51
|
-
|
|
52
|
-
/** Cancel navigation if transition is running */
|
|
53
|
-
cancelNavigation: () => void;
|
|
54
|
-
|
|
55
|
-
/** Send COMPLETE event to routerFSM */
|
|
56
|
-
sendTransitionDone: (
|
|
57
|
-
state: State,
|
|
58
|
-
fromState: State | undefined,
|
|
59
|
-
opts: NavigationOptions,
|
|
60
|
-
) => void;
|
|
61
|
-
|
|
62
|
-
/** Send FAIL event to routerFSM */
|
|
63
|
-
sendTransitionFail: (
|
|
64
|
-
toState: State,
|
|
65
|
-
fromState: State | undefined,
|
|
66
|
-
error: unknown,
|
|
67
|
-
) => void;
|
|
68
|
-
|
|
69
|
-
/** Emit TRANSITION_ERROR event to listeners */
|
|
70
|
-
emitTransitionError: (
|
|
71
|
-
toState: State | undefined,
|
|
72
|
-
fromState: State | undefined,
|
|
73
|
-
error: unknown,
|
|
74
|
-
) => void;
|
|
75
|
-
|
|
76
|
-
/** Emit TRANSITION_SUCCESS event to listeners (without FSM transition) */
|
|
77
|
-
emitTransitionSuccess: (
|
|
78
|
-
toState: State,
|
|
79
|
-
fromState?: State,
|
|
80
|
-
opts?: NavigationOptions,
|
|
81
|
-
) => void;
|
|
82
|
-
|
|
83
|
-
/** Send LEAVE_APPROVE event to routerFSM and emit to listeners */
|
|
84
|
-
sendLeaveApprove: (toState: State, fromState: State | undefined) => void;
|
|
85
|
-
|
|
86
|
-
/** Check if navigation can begin (router is started) */
|
|
87
|
-
canNavigate: () => boolean;
|
|
88
|
-
|
|
89
|
-
/** Get lifecycle functions (canDeactivate, canActivate maps) */
|
|
90
|
-
getLifecycleFunctions: () => [Map<string, GuardFn>, Map<string, GuardFn>];
|
|
91
|
-
|
|
92
|
-
/** Check if router is active (for cancellation check on stop()) */
|
|
93
|
-
isActive: () => boolean;
|
|
94
|
-
|
|
95
|
-
/** Check if a transition is currently in progress */
|
|
96
|
-
isTransitioning: () => boolean;
|
|
97
|
-
|
|
98
|
-
/** Clear canDeactivate guard for a route */
|
|
99
|
-
clearCanDeactivate: (name: string) => void;
|
|
100
|
-
|
|
101
|
-
/** Check if any leave listeners are registered */
|
|
102
|
-
hasLeaveListeners: () => boolean;
|
|
103
|
-
|
|
104
|
-
/** Call all leave listeners — returns Promise if any are async, undefined otherwise */
|
|
105
|
-
awaitLeaveListeners: (
|
|
106
|
-
toState: State,
|
|
107
|
-
fromState: State | undefined,
|
|
108
|
-
signal: AbortSignal,
|
|
109
|
-
) => Promise<void> | undefined;
|
|
110
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/OptionsNamespace/OptionsNamespace.ts
|
|
2
|
-
|
|
3
|
-
import { defaultOptions } from "./constants";
|
|
4
|
-
import { deepFreeze } from "./helpers";
|
|
5
|
-
import { validateOptionsIsObject } from "./validators";
|
|
6
|
-
|
|
7
|
-
import type { Options } from "@real-router/types";
|
|
8
|
-
|
|
9
|
-
export class OptionsNamespace {
|
|
10
|
-
readonly #options: Readonly<Options>;
|
|
11
|
-
|
|
12
|
-
constructor(initialOptions: Partial<Options> = {}) {
|
|
13
|
-
this.#options = deepFreeze({
|
|
14
|
-
...defaultOptions,
|
|
15
|
-
...initialOptions,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static validateOptionsIsObject(
|
|
20
|
-
options: unknown,
|
|
21
|
-
): asserts options is Record<string, unknown> {
|
|
22
|
-
validateOptionsIsObject(options);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
get(): Readonly<Options> {
|
|
26
|
-
return this.#options;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/OptionsNamespace/constants.ts
|
|
2
|
-
|
|
3
|
-
import { DEFAULT_QUERY_PARAMS } from "route-tree";
|
|
4
|
-
|
|
5
|
-
import type { Options } from "@real-router/types";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Default options for the router.
|
|
9
|
-
*/
|
|
10
|
-
export const defaultOptions: Options = {
|
|
11
|
-
defaultRoute: "",
|
|
12
|
-
defaultParams: {},
|
|
13
|
-
trailingSlash: "preserve",
|
|
14
|
-
queryParamsMode: "loose",
|
|
15
|
-
queryParams: DEFAULT_QUERY_PARAMS,
|
|
16
|
-
urlParamsEncoding: "default",
|
|
17
|
-
allowNotFound: true,
|
|
18
|
-
rewritePathOnMatch: true,
|
|
19
|
-
} satisfies Options;
|