@real-router/core 0.38.0 → 0.40.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/README.md +27 -5
- package/dist/cjs/Router-B-Pev7K2.d.ts +46 -0
- package/dist/cjs/RouterValidator-mx2Zooya.d.ts +136 -0
- package/dist/cjs/api.d.ts +2 -1
- package/dist/cjs/api.js +1 -1
- package/dist/cjs/api.js.map +1 -1
- package/dist/cjs/index.d-y2b-8_3Y.d.ts +236 -0
- package/dist/cjs/index.d.ts +7 -24
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/metafile-cjs.json +1 -1
- package/dist/cjs/utils.d.ts +6 -1
- package/dist/cjs/utils.js +1 -1
- package/dist/cjs/utils.js.map +1 -1
- package/dist/cjs/validation.d.ts +184 -0
- package/dist/cjs/validation.js +1 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/Router-B-Pev7K2.d.mts +46 -0
- package/dist/esm/RouterValidator-mx2Zooya.d.mts +136 -0
- package/dist/esm/api.d.mts +2 -1
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/api.mjs.map +1 -1
- package/dist/esm/chunk-5QXFUUDL.mjs +1 -0
- package/dist/esm/chunk-5QXFUUDL.mjs.map +1 -0
- package/dist/esm/chunk-HHIXK5UM.mjs +1 -0
- package/dist/esm/chunk-HHIXK5UM.mjs.map +1 -0
- package/dist/esm/chunk-QUUNDESP.mjs +1 -0
- package/dist/esm/chunk-QUUNDESP.mjs.map +1 -0
- package/dist/esm/chunk-RA5VYM7M.mjs +1 -0
- package/dist/esm/chunk-RA5VYM7M.mjs.map +1 -0
- package/dist/esm/index.d-y2b-8_3Y.d.mts +236 -0
- package/dist/esm/index.d.mts +7 -24
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/metafile-esm.json +1 -1
- package/dist/esm/utils.d.mts +6 -1
- package/dist/esm/utils.mjs +1 -1
- package/dist/esm/utils.mjs.map +1 -1
- package/dist/esm/validation.d.mts +184 -0
- package/dist/esm/validation.mjs +1 -0
- package/dist/esm/validation.mjs.map +1 -0
- package/package.json +18 -5
- package/src/Router.ts +73 -99
- package/src/api/cloneRouter.ts +1 -30
- package/src/api/getDependenciesApi.ts +45 -86
- package/src/api/getLifecycleApi.ts +24 -19
- package/src/api/getPluginApi.ts +20 -28
- package/src/api/getRoutesApi.ts +49 -106
- package/src/constants.ts +0 -30
- package/src/guards.ts +46 -0
- package/src/helpers.ts +0 -17
- package/src/index.ts +4 -0
- package/src/internals.ts +6 -5
- package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +2 -2
- package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +0 -25
- package/src/namespaces/OptionsNamespace/OptionsNamespace.ts +4 -26
- package/src/namespaces/OptionsNamespace/constants.ts +0 -20
- package/src/namespaces/OptionsNamespace/index.ts +1 -5
- package/src/namespaces/OptionsNamespace/validators.ts +6 -245
- package/src/namespaces/PluginsNamespace/PluginsNamespace.ts +18 -59
- package/src/namespaces/PluginsNamespace/constants.ts +3 -6
- package/src/namespaces/PluginsNamespace/validators.ts +2 -57
- package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +27 -84
- package/src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts +0 -16
- package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +3 -12
- package/src/namespaces/RoutesNamespace/constants.ts +0 -8
- package/src/namespaces/RoutesNamespace/forwardChain.ts +34 -0
- package/src/namespaces/RoutesNamespace/index.ts +1 -1
- package/src/namespaces/RoutesNamespace/routeGuards.ts +62 -0
- package/src/namespaces/RoutesNamespace/routesStore.ts +7 -51
- package/src/namespaces/StateNamespace/StateNamespace.ts +0 -33
- package/src/namespaces/StateNamespace/helpers.ts +1 -1
- package/src/namespaces/index.ts +0 -3
- package/src/typeGuards.ts +1 -15
- package/src/types/RouterValidator.ts +155 -0
- package/src/utils/getStaticPaths.ts +50 -0
- package/src/utils/index.ts +4 -0
- package/src/validation.ts +12 -0
- package/src/wiring/RouterWiringBuilder.ts +32 -9
- package/dist/cjs/index.d-DDimDpYc.d.ts +0 -165
- package/dist/esm/chunk-CG7TKDP3.mjs +0 -1
- package/dist/esm/chunk-CG7TKDP3.mjs.map +0 -1
- package/dist/esm/index.d-DDimDpYc.d.mts +0 -165
- package/src/namespaces/DependenciesNamespace/validators.ts +0 -103
- package/src/namespaces/EventBusNamespace/validators.ts +0 -36
- package/src/namespaces/NavigationNamespace/validators.ts +0 -47
- package/src/namespaces/RouteLifecycleNamespace/validators.ts +0 -65
- package/src/namespaces/RoutesNamespace/forwardToValidation.ts +0 -408
- package/src/namespaces/RoutesNamespace/validators.ts +0 -566
- package/src/namespaces/StateNamespace/validators.ts +0 -46
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { L as Limits, R as RouterValidator } from './RouterValidator-mx2Zooya.mjs';
|
|
2
|
+
import { DefaultDependencies, Params, ForwardToCallback, GuardFnFactory, State, SimpleState, GuardFn, RouteTreeState, Options, EventName, Plugin, EventMethodMap, Unsubscribe, PluginFactory, Router } from '@real-router/types';
|
|
3
|
+
import { a as RouteDefinition, R as RouteTree, M as Matcher, C as CreateMatcherOptions } from './index.d-y2b-8_3Y.mjs';
|
|
4
|
+
|
|
5
|
+
interface DependenciesStore<Dependencies extends DefaultDependencies = DefaultDependencies> {
|
|
6
|
+
dependencies: Partial<Dependencies>;
|
|
7
|
+
limits: Limits;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Dependencies injected into RoutesNamespace.
|
|
12
|
+
*
|
|
13
|
+
* These are function references from the Router facade,
|
|
14
|
+
* avoiding the need to pass the entire Router object.
|
|
15
|
+
*/
|
|
16
|
+
interface RoutesDependencies<Dependencies extends DefaultDependencies = DefaultDependencies> {
|
|
17
|
+
/** Register canActivate handler for a route */
|
|
18
|
+
addActivateGuard: (name: string, handler: GuardFnFactory<Dependencies>) => void;
|
|
19
|
+
/** Register canDeactivate handler for a route */
|
|
20
|
+
addDeactivateGuard: (name: string, handler: GuardFnFactory<Dependencies>) => void;
|
|
21
|
+
/** Create state object */
|
|
22
|
+
makeState: <P extends Params = Params, MP extends Params = Params>(name: string, params?: P, path?: string, meta?: Record<string, Record<string, "url" | "query">>) => State<P, MP>;
|
|
23
|
+
/** Get current router state */
|
|
24
|
+
getState: () => State | undefined;
|
|
25
|
+
/** Compare two states for equality */
|
|
26
|
+
areStatesEqual: (state1: State | undefined, state2: State | undefined, ignoreQueryParams?: boolean) => boolean;
|
|
27
|
+
/** Get a dependency by name */
|
|
28
|
+
getDependency: <K extends keyof Dependencies>(name: K) => Dependencies[K];
|
|
29
|
+
/** Forward state through facade (allows plugin interception) */
|
|
30
|
+
forwardState: <P extends Params = Params>(name: string, params: P) => SimpleState<P>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Configuration storage for routes.
|
|
34
|
+
* Stores decoders, encoders, default params, and forward mappings.
|
|
35
|
+
*/
|
|
36
|
+
interface RouteConfig {
|
|
37
|
+
/** Custom param decoders per route */
|
|
38
|
+
decoders: Record<string, (params: Params) => Params>;
|
|
39
|
+
/** Custom param encoders per route */
|
|
40
|
+
encoders: Record<string, (params: Params) => Params>;
|
|
41
|
+
/** Default params per route */
|
|
42
|
+
defaultParams: Record<string, Params>;
|
|
43
|
+
/** Forward mappings (source -> target) */
|
|
44
|
+
forwardMap: Record<string, string>;
|
|
45
|
+
/** Dynamic forward callbacks (source -> callback) */
|
|
46
|
+
forwardFnMap: Record<string, ForwardToCallback<any>>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface RouteLifecycleDependencies<Dependencies extends DefaultDependencies = DefaultDependencies> {
|
|
50
|
+
compileFactory: (factory: GuardFnFactory<Dependencies>) => GuardFn;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Independent namespace for managing route lifecycle handlers.
|
|
55
|
+
*
|
|
56
|
+
* Static methods handle input validation (called by facade).
|
|
57
|
+
* Instance methods handle state-dependent validation, storage and business logic.
|
|
58
|
+
*/
|
|
59
|
+
declare class RouteLifecycleNamespace<Dependencies extends DefaultDependencies = DefaultDependencies> {
|
|
60
|
+
#private;
|
|
61
|
+
setDependencies(deps: RouteLifecycleDependencies<Dependencies>): void;
|
|
62
|
+
/**
|
|
63
|
+
* Updates handler registration limits (max lifecycle handlers threshold).
|
|
64
|
+
*
|
|
65
|
+
* @param limits - Limits configuration with maxLifecycleHandlers
|
|
66
|
+
*/
|
|
67
|
+
setLimits(limits: Limits): void;
|
|
68
|
+
setValidatorGetter(getter: () => RouterValidator | null): void;
|
|
69
|
+
getHandlerCount(type: "activate" | "deactivate"): number;
|
|
70
|
+
/**
|
|
71
|
+
* Adds a canActivate guard for a route.
|
|
72
|
+
* Handles overwrite detection and registration.
|
|
73
|
+
*
|
|
74
|
+
* @param name - Route name (input-validated by facade)
|
|
75
|
+
* @param handler - Guard function or boolean (input-validated by facade)
|
|
76
|
+
* @param isFromDefinition - True when guard comes from route definition (tracked for HMR replace)
|
|
77
|
+
*/
|
|
78
|
+
addCanActivate(name: string, handler: GuardFnFactory<Dependencies> | boolean, isFromDefinition?: boolean): void;
|
|
79
|
+
/**
|
|
80
|
+
* Adds a canDeactivate guard for a route.
|
|
81
|
+
* Handles overwrite detection and registration.
|
|
82
|
+
*
|
|
83
|
+
* @param name - Route name (input-validated by facade)
|
|
84
|
+
* @param handler - Guard function or boolean (input-validated by facade)
|
|
85
|
+
* @param isFromDefinition - True when guard comes from route definition (tracked for HMR replace)
|
|
86
|
+
*/
|
|
87
|
+
addCanDeactivate(name: string, handler: GuardFnFactory<Dependencies> | boolean, isFromDefinition?: boolean): void;
|
|
88
|
+
/**
|
|
89
|
+
* Removes a canActivate guard for a route.
|
|
90
|
+
* Input already validated by facade (not registering).
|
|
91
|
+
*
|
|
92
|
+
* @param name - Route name (already validated by facade)
|
|
93
|
+
*/
|
|
94
|
+
clearCanActivate(name: string): void;
|
|
95
|
+
/**
|
|
96
|
+
* Removes a canDeactivate guard for a route.
|
|
97
|
+
* Input already validated by facade (not registering).
|
|
98
|
+
*
|
|
99
|
+
* @param name - Route name (already validated by facade)
|
|
100
|
+
*/
|
|
101
|
+
clearCanDeactivate(name: string): void;
|
|
102
|
+
/**
|
|
103
|
+
* Clears all lifecycle handlers (canActivate and canDeactivate).
|
|
104
|
+
* Used by clearRoutes to reset all lifecycle state.
|
|
105
|
+
*/
|
|
106
|
+
clearAll(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Clears only lifecycle handlers that were registered from route definitions.
|
|
109
|
+
* Used by HMR to remove definition-sourced guards without touching externally-added guards.
|
|
110
|
+
*/
|
|
111
|
+
clearDefinitionGuards(): void;
|
|
112
|
+
/**
|
|
113
|
+
* Returns lifecycle factories as records for cloning.
|
|
114
|
+
*
|
|
115
|
+
* @returns Tuple of [canDeactivateFactories, canActivateFactories]
|
|
116
|
+
*/
|
|
117
|
+
getFactories(): [
|
|
118
|
+
Record<string, GuardFnFactory<Dependencies>>,
|
|
119
|
+
Record<string, GuardFnFactory<Dependencies>>
|
|
120
|
+
];
|
|
121
|
+
/**
|
|
122
|
+
* Returns compiled lifecycle functions for transition execution.
|
|
123
|
+
*
|
|
124
|
+
* @returns Tuple of [canDeactivateFunctions, canActivateFunctions] as Maps
|
|
125
|
+
*/
|
|
126
|
+
getFunctions(): [Map<string, GuardFn>, Map<string, GuardFn>];
|
|
127
|
+
canNavigateTo(toDeactivate: string[], toActivate: string[], toState: State, fromState: State | undefined): boolean;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface RoutesStore<Dependencies extends DefaultDependencies = DefaultDependencies> {
|
|
131
|
+
readonly definitions: RouteDefinition[];
|
|
132
|
+
readonly config: RouteConfig;
|
|
133
|
+
tree: RouteTree;
|
|
134
|
+
matcher: Matcher;
|
|
135
|
+
resolvedForwardMap: Record<string, string>;
|
|
136
|
+
routeCustomFields: Record<string, Record<string, unknown>>;
|
|
137
|
+
rootPath: string;
|
|
138
|
+
readonly matcherOptions: CreateMatcherOptions | undefined;
|
|
139
|
+
depsStore: RoutesDependencies<Dependencies> | undefined;
|
|
140
|
+
lifecycleNamespace: RouteLifecycleNamespace<Dependencies> | undefined;
|
|
141
|
+
readonly pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>;
|
|
142
|
+
readonly pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>;
|
|
143
|
+
readonly treeOperations: {
|
|
144
|
+
readonly commitTreeChanges: (store: RoutesStore<Dependencies>) => void;
|
|
145
|
+
readonly resetStore: (store: RoutesStore<Dependencies>) => void;
|
|
146
|
+
readonly nodeToDefinition: (node: RouteTree) => RouteDefinition;
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
interface RouterInternals<D extends DefaultDependencies = DefaultDependencies> {
|
|
151
|
+
readonly makeState: <P extends Params = Params, MP extends Params = Params>(name: string, params?: P, path?: string, meta?: Record<string, Record<string, "url" | "query">>, forceId?: number) => State<P, MP>;
|
|
152
|
+
readonly forwardState: <P extends Params = Params>(routeName: string, routeParams: P) => SimpleState<P>;
|
|
153
|
+
readonly buildStateResolved: (resolvedName: string, resolvedParams: Params) => RouteTreeState | undefined;
|
|
154
|
+
readonly matchPath: <P extends Params = Params, MP extends Params = Params>(path: string, options?: Options) => State<P, MP> | undefined;
|
|
155
|
+
readonly getOptions: () => Options;
|
|
156
|
+
readonly addEventListener: <E extends EventName>(eventName: E, cb: Plugin[EventMethodMap[E]]) => Unsubscribe;
|
|
157
|
+
readonly buildPath: (route: string, params?: Params) => string;
|
|
158
|
+
readonly start: (path: string) => Promise<State>;
|
|
159
|
+
readonly interceptors: Map<string, ((next: (...args: any[]) => any, ...args: any[]) => any)[]>;
|
|
160
|
+
readonly setRootPath: (rootPath: string) => void;
|
|
161
|
+
readonly getRootPath: () => string;
|
|
162
|
+
readonly getTree: () => RouteTree;
|
|
163
|
+
readonly isDisposed: () => boolean;
|
|
164
|
+
validator: RouterValidator | null;
|
|
165
|
+
readonly dependenciesGetStore: () => DependenciesStore<D>;
|
|
166
|
+
readonly cloneOptions: () => Options;
|
|
167
|
+
readonly cloneDependencies: () => Record<string, unknown>;
|
|
168
|
+
readonly getLifecycleFactories: () => [
|
|
169
|
+
Record<string, GuardFnFactory<D>>,
|
|
170
|
+
Record<string, GuardFnFactory<D>>
|
|
171
|
+
];
|
|
172
|
+
readonly getPluginFactories: () => PluginFactory<D>[];
|
|
173
|
+
readonly routeGetStore: () => RoutesStore<D>;
|
|
174
|
+
readonly getStateName: () => string | undefined;
|
|
175
|
+
readonly isTransitioning: () => boolean;
|
|
176
|
+
readonly clearState: () => void;
|
|
177
|
+
readonly setState: (state: State) => void;
|
|
178
|
+
readonly routerExtensions: {
|
|
179
|
+
keys: string[];
|
|
180
|
+
}[];
|
|
181
|
+
}
|
|
182
|
+
declare function getInternals<D extends DefaultDependencies>(router: Router<D>): RouterInternals<D>;
|
|
183
|
+
|
|
184
|
+
export { type RouterInternals, RouterValidator, getInternals };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{getInternals}from"./chunk-QUUNDESP.mjs";//# sourceMappingURL=validation.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"validation.mjs"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "A simple, powerful, view-agnostic, modular and extensible router",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
],
|
|
14
14
|
"utils": [
|
|
15
15
|
"./dist/cjs/utils.d.ts"
|
|
16
|
+
],
|
|
17
|
+
"validation": [
|
|
18
|
+
"./dist/cjs/validation.d.ts"
|
|
16
19
|
]
|
|
17
20
|
}
|
|
18
21
|
},
|
|
@@ -43,6 +46,15 @@
|
|
|
43
46
|
},
|
|
44
47
|
"import": "./dist/esm/utils.mjs",
|
|
45
48
|
"require": "./dist/cjs/utils.js"
|
|
49
|
+
},
|
|
50
|
+
"./validation": {
|
|
51
|
+
"development": "./src/validation.ts",
|
|
52
|
+
"types": {
|
|
53
|
+
"import": "./dist/esm/validation.d.mts",
|
|
54
|
+
"require": "./dist/cjs/validation.d.ts"
|
|
55
|
+
},
|
|
56
|
+
"import": "./dist/esm/validation.mjs",
|
|
57
|
+
"require": "./dist/cjs/validation.js"
|
|
46
58
|
}
|
|
47
59
|
},
|
|
48
60
|
"files": [
|
|
@@ -73,16 +85,17 @@
|
|
|
73
85
|
"url": "https://github.com/greydragon888/real-router/issues"
|
|
74
86
|
},
|
|
75
87
|
"homepage": "https://github.com/greydragon888/real-router",
|
|
76
|
-
"sideEffects":
|
|
88
|
+
"sideEffects": [
|
|
89
|
+
"./dist/esm/chunk-*.mjs"
|
|
90
|
+
],
|
|
77
91
|
"dependencies": {
|
|
78
92
|
"@real-router/fsm": "^0.2.2",
|
|
79
93
|
"@real-router/logger": "^0.2.1",
|
|
80
|
-
"@real-router/types": "^0.
|
|
94
|
+
"@real-router/types": "^0.26.0"
|
|
81
95
|
},
|
|
82
96
|
"devDependencies": {
|
|
83
97
|
"event-emitter": "^0.1.2",
|
|
84
|
-
"route-tree": "^0.3.4"
|
|
85
|
-
"type-guards": "^0.3.7"
|
|
98
|
+
"route-tree": "^0.3.4"
|
|
86
99
|
},
|
|
87
100
|
"scripts": {
|
|
88
101
|
"build": "tsup",
|
package/src/Router.ts
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
import { logger } from "@real-router/logger";
|
|
10
10
|
import { EventEmitter } from "event-emitter";
|
|
11
|
-
import { validateRouteName } from "type-guards";
|
|
12
11
|
|
|
13
12
|
import { EMPTY_PARAMS, errorCodes } from "./constants";
|
|
14
13
|
import { createRouterFSM } from "./fsm";
|
|
14
|
+
import { guardDependencies, guardRouteStructure } from "./guards";
|
|
15
15
|
import { createLimits } from "./helpers";
|
|
16
16
|
import {
|
|
17
17
|
createInterceptable,
|
|
@@ -30,15 +30,7 @@ import {
|
|
|
30
30
|
StateNamespace,
|
|
31
31
|
createDependenciesStore,
|
|
32
32
|
} from "./namespaces";
|
|
33
|
-
import { validateDependenciesObject } from "./namespaces/DependenciesNamespace/validators";
|
|
34
33
|
import { CACHED_ALREADY_STARTED_ERROR } from "./namespaces/RouterLifecycleNamespace/constants";
|
|
35
|
-
import {
|
|
36
|
-
validateAddRouteArgs,
|
|
37
|
-
validateBuildPathArgs,
|
|
38
|
-
validateIsActiveRouteArgs,
|
|
39
|
-
validateRoutes,
|
|
40
|
-
validateShouldUpdateNodeArgs,
|
|
41
|
-
} from "./namespaces/RoutesNamespace/validators";
|
|
42
34
|
import { RouterError } from "./RouterError";
|
|
43
35
|
import { getTransitionPath } from "./transitionPath";
|
|
44
36
|
import { isLoggerConfig } from "./typeGuards";
|
|
@@ -98,12 +90,6 @@ export class Router<
|
|
|
98
90
|
|
|
99
91
|
readonly #eventBus: EventBusNamespace;
|
|
100
92
|
|
|
101
|
-
/**
|
|
102
|
-
* When true, skips argument validation in public methods for production performance.
|
|
103
|
-
* Constructor options are always validated (needed to validate noValidate itself).
|
|
104
|
-
*/
|
|
105
|
-
readonly #noValidate: boolean;
|
|
106
|
-
|
|
107
93
|
// ============================================================================
|
|
108
94
|
// Constructor
|
|
109
95
|
// ============================================================================
|
|
@@ -128,22 +114,14 @@ export class Router<
|
|
|
128
114
|
// Validate inputs before creating namespaces
|
|
129
115
|
// =========================================================================
|
|
130
116
|
|
|
131
|
-
// Always validate options
|
|
132
|
-
OptionsNamespace.
|
|
117
|
+
// Always validate options
|
|
118
|
+
OptionsNamespace.validateOptionsIsObject(options);
|
|
133
119
|
|
|
134
|
-
//
|
|
135
|
-
|
|
120
|
+
// Unconditional guard-level validation before creating namespaces
|
|
121
|
+
guardDependencies(dependencies);
|
|
136
122
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
validateDependenciesObject(dependencies, "constructor");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Conditional validation for initial routes - structure and batch duplicates
|
|
143
|
-
// Validation happens BEFORE tree is built, so tree is not passed
|
|
144
|
-
if (!noValidate && routes.length > 0) {
|
|
145
|
-
validateAddRouteArgs(routes);
|
|
146
|
-
validateRoutes(routes);
|
|
123
|
+
if (routes.length > 0) {
|
|
124
|
+
guardRouteStructure(routes);
|
|
147
125
|
}
|
|
148
126
|
|
|
149
127
|
// =========================================================================
|
|
@@ -157,14 +135,12 @@ export class Router<
|
|
|
157
135
|
this.#state = new StateNamespace();
|
|
158
136
|
this.#routes = new RoutesNamespace<Dependencies>(
|
|
159
137
|
routes,
|
|
160
|
-
noValidate,
|
|
161
138
|
deriveMatcherOptions(this.#options.get()),
|
|
162
139
|
);
|
|
163
140
|
this.#routeLifecycle = new RouteLifecycleNamespace<Dependencies>();
|
|
164
141
|
this.#plugins = new PluginsNamespace<Dependencies>();
|
|
165
142
|
this.#navigation = new NavigationNamespace();
|
|
166
143
|
this.#lifecycle = new RouterLifecycleNamespace();
|
|
167
|
-
this.#noValidate = noValidate;
|
|
168
144
|
|
|
169
145
|
// =========================================================================
|
|
170
146
|
// Initialize EventBus
|
|
@@ -241,10 +217,6 @@ export class Router<
|
|
|
241
217
|
start: createInterceptable(
|
|
242
218
|
"start",
|
|
243
219
|
(path: string) => {
|
|
244
|
-
if (!noValidate) {
|
|
245
|
-
RouterLifecycleNamespace.validateStartArgs([path]);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
220
|
return this.#lifecycle.start(path);
|
|
249
221
|
},
|
|
250
222
|
interceptorsMap,
|
|
@@ -256,7 +228,7 @@ export class Router<
|
|
|
256
228
|
getRootPath: () => this.#routes.getStore().rootPath,
|
|
257
229
|
getTree: () => this.#routes.getStore().tree,
|
|
258
230
|
isDisposed: () => this.#eventBus.isDisposed(),
|
|
259
|
-
|
|
231
|
+
validator: null,
|
|
260
232
|
// Dependencies (issue #172)
|
|
261
233
|
dependenciesGetStore: () => this.#dependenciesStore,
|
|
262
234
|
// Clone support (issue #173)
|
|
@@ -330,14 +302,17 @@ export class Router<
|
|
|
330
302
|
strictEquality?: boolean,
|
|
331
303
|
ignoreQueryParams?: boolean,
|
|
332
304
|
): boolean {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
305
|
+
getInternals(this).validator?.routes.validateIsActiveRouteArgs(
|
|
306
|
+
name,
|
|
307
|
+
params,
|
|
308
|
+
strictEquality,
|
|
309
|
+
ignoreQueryParams,
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
getInternals(this).validator?.routes.validateRouteName(
|
|
313
|
+
name,
|
|
314
|
+
"isActiveRoute",
|
|
315
|
+
);
|
|
341
316
|
|
|
342
317
|
// Empty string is special case - warn and return false (root node is not a parent)
|
|
343
318
|
if (name === "") {
|
|
@@ -358,11 +333,12 @@ export class Router<
|
|
|
358
333
|
}
|
|
359
334
|
|
|
360
335
|
buildPath(route: string, params?: Params): string {
|
|
361
|
-
|
|
362
|
-
validateBuildPathArgs(route);
|
|
363
|
-
}
|
|
336
|
+
const ctx = getInternals(this);
|
|
364
337
|
|
|
365
|
-
|
|
338
|
+
ctx.validator?.routes.validateBuildPathArgs(route);
|
|
339
|
+
ctx.validator?.navigation.validateParams(params, "buildPath");
|
|
340
|
+
|
|
341
|
+
return ctx.buildPath(route, params);
|
|
366
342
|
}
|
|
367
343
|
|
|
368
344
|
// ============================================================================
|
|
@@ -384,13 +360,11 @@ export class Router<
|
|
|
384
360
|
state2: State | undefined,
|
|
385
361
|
ignoreQueryParams = true,
|
|
386
362
|
): boolean {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
);
|
|
393
|
-
}
|
|
363
|
+
getInternals(this).validator?.state.validateAreStatesEqualArgs(
|
|
364
|
+
state1,
|
|
365
|
+
state2,
|
|
366
|
+
ignoreQueryParams,
|
|
367
|
+
);
|
|
394
368
|
|
|
395
369
|
return this.#state.areStatesEqual(state1, state2, ignoreQueryParams);
|
|
396
370
|
}
|
|
@@ -398,9 +372,7 @@ export class Router<
|
|
|
398
372
|
shouldUpdateNode(
|
|
399
373
|
nodeName: string,
|
|
400
374
|
): (toState: State, fromState?: State) => boolean {
|
|
401
|
-
|
|
402
|
-
validateShouldUpdateNodeArgs(nodeName);
|
|
403
|
-
}
|
|
375
|
+
getInternals(this).validator?.routes.validateShouldUpdateNodeArgs(nodeName);
|
|
404
376
|
|
|
405
377
|
return RoutesNamespace.shouldUpdateNode(nodeName);
|
|
406
378
|
}
|
|
@@ -418,6 +390,8 @@ export class Router<
|
|
|
418
390
|
return Promise.reject(CACHED_ALREADY_STARTED_ERROR);
|
|
419
391
|
}
|
|
420
392
|
|
|
393
|
+
getInternals(this).validator?.navigation.validateStartArgs(startPath);
|
|
394
|
+
|
|
421
395
|
this.#eventBus.sendStart();
|
|
422
396
|
|
|
423
397
|
const promiseState = getInternals(this)
|
|
@@ -494,15 +468,15 @@ export class Router<
|
|
|
494
468
|
// ============================================================================
|
|
495
469
|
|
|
496
470
|
canNavigateTo(name: string, params?: Params): boolean {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
471
|
+
const ctx = getInternals(this);
|
|
472
|
+
|
|
473
|
+
ctx.validator?.routes.validateRouteName(name, "canNavigateTo");
|
|
474
|
+
ctx.validator?.navigation.validateParams(params, "canNavigateTo");
|
|
500
475
|
|
|
501
476
|
if (!this.#routes.hasRoute(name)) {
|
|
502
477
|
return false;
|
|
503
478
|
}
|
|
504
479
|
|
|
505
|
-
const ctx = getInternals(this);
|
|
506
480
|
const { name: resolvedName, params: resolvedParams } = ctx.forwardState(
|
|
507
481
|
name,
|
|
508
482
|
params ?? {},
|
|
@@ -524,27 +498,29 @@ export class Router<
|
|
|
524
498
|
// Plugins
|
|
525
499
|
// ============================================================================
|
|
526
500
|
|
|
527
|
-
usePlugin(
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
501
|
+
usePlugin(
|
|
502
|
+
...plugins: (PluginFactory<Dependencies> | false | null | undefined)[]
|
|
503
|
+
): Unsubscribe {
|
|
504
|
+
const filtered = plugins.filter(Boolean) as PluginFactory<Dependencies>[];
|
|
531
505
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
);
|
|
506
|
+
if (filtered.length === 0) {
|
|
507
|
+
return () => {};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const ctx = getInternals(this);
|
|
538
511
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
512
|
+
ctx.validator?.plugins.validatePluginLimit(
|
|
513
|
+
this.#plugins.count(),
|
|
514
|
+
this.#limits,
|
|
515
|
+
);
|
|
516
|
+
for (const plugin of filtered) {
|
|
517
|
+
ctx.validator?.plugins.validateNoDuplicatePlugins(
|
|
518
|
+
plugin,
|
|
519
|
+
this.#plugins.getAll(),
|
|
543
520
|
);
|
|
544
521
|
}
|
|
545
522
|
|
|
546
|
-
|
|
547
|
-
return this.#plugins.use(...plugins);
|
|
523
|
+
return this.#plugins.use(...filtered);
|
|
548
524
|
}
|
|
549
525
|
|
|
550
526
|
// ============================================================================
|
|
@@ -552,9 +528,7 @@ export class Router<
|
|
|
552
528
|
// ============================================================================
|
|
553
529
|
|
|
554
530
|
subscribe(listener: SubscribeFn): Unsubscribe {
|
|
555
|
-
|
|
556
|
-
EventBusNamespace.validateSubscribeListener(listener);
|
|
557
|
-
}
|
|
531
|
+
EventBusNamespace.validateSubscribeListener(listener);
|
|
558
532
|
|
|
559
533
|
return this.#eventBus.subscribe(listener);
|
|
560
534
|
}
|
|
@@ -568,19 +542,15 @@ export class Router<
|
|
|
568
542
|
routeParams?: Params,
|
|
569
543
|
options?: NavigationOptions,
|
|
570
544
|
): Promise<State> {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
545
|
+
const ctx = getInternals(this);
|
|
546
|
+
|
|
547
|
+
ctx.validator?.navigation.validateNavigateArgs(routeName);
|
|
548
|
+
ctx.validator?.navigation.validateParams(routeParams, "navigate");
|
|
575
549
|
|
|
576
|
-
// 2. Validate parsed options
|
|
577
550
|
const opts = options ?? EMPTY_OPTS;
|
|
578
551
|
|
|
579
|
-
|
|
580
|
-
NavigationNamespace.validateNavigationOptions(opts, "navigate");
|
|
581
|
-
}
|
|
552
|
+
ctx.validator?.navigation.validateNavigationOptions(opts, "navigate");
|
|
582
553
|
|
|
583
|
-
// 3. Execute navigation with parsed arguments
|
|
584
554
|
const promiseState = this.#navigation.navigate(
|
|
585
555
|
routeName,
|
|
586
556
|
routeParams ?? EMPTY_PARAMS,
|
|
@@ -600,19 +570,17 @@ export class Router<
|
|
|
600
570
|
}
|
|
601
571
|
|
|
602
572
|
navigateToDefault(options?: NavigationOptions): Promise<State> {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
}
|
|
573
|
+
const ctx = getInternals(this);
|
|
574
|
+
|
|
575
|
+
ctx.validator?.navigation.validateNavigateToDefaultArgs(options);
|
|
607
576
|
|
|
608
|
-
// 2. Validate parsed options
|
|
609
577
|
const opts = options ?? EMPTY_OPTS;
|
|
610
578
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
579
|
+
ctx.validator?.navigation.validateNavigationOptions(
|
|
580
|
+
opts,
|
|
581
|
+
"navigateToDefault",
|
|
582
|
+
);
|
|
614
583
|
|
|
615
|
-
// 3. Execute navigation with parsed arguments
|
|
616
584
|
const promiseState = this.#navigation.navigateToDefault(opts);
|
|
617
585
|
|
|
618
586
|
if (this.#navigation.lastSyncResolved) {
|
|
@@ -631,6 +599,12 @@ export class Router<
|
|
|
631
599
|
throw new RouterError(errorCodes.ROUTER_NOT_STARTED);
|
|
632
600
|
}
|
|
633
601
|
|
|
602
|
+
if (path !== undefined && typeof path !== "string") {
|
|
603
|
+
throw new TypeError(
|
|
604
|
+
`[router.navigateToNotFound] path must be a string, got ${typeof path}`,
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
634
608
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- isActive() guarantees state exists
|
|
635
609
|
const resolvedPath = path ?? this.#state.get()!.path;
|
|
636
610
|
|
package/src/api/cloneRouter.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { routeTreeToDefinitions } from "route-tree";
|
|
2
|
-
import { getTypeDescription } from "type-guards";
|
|
3
2
|
|
|
4
3
|
import { errorCodes } from "../constants";
|
|
5
4
|
import { getInternals } from "../internals";
|
|
@@ -10,32 +9,6 @@ import { getLifecycleApi } from "./getLifecycleApi";
|
|
|
10
9
|
import type { Route } from "../types";
|
|
11
10
|
import type { DefaultDependencies, Router } from "@real-router/types";
|
|
12
11
|
|
|
13
|
-
function validateCloneArgs(dependencies: unknown): void {
|
|
14
|
-
if (dependencies === undefined) {
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
!(
|
|
20
|
-
dependencies &&
|
|
21
|
-
typeof dependencies === "object" &&
|
|
22
|
-
dependencies.constructor === Object
|
|
23
|
-
)
|
|
24
|
-
) {
|
|
25
|
-
throw new TypeError(
|
|
26
|
-
`[cloneRouter] Invalid dependencies: expected plain object or undefined, received ${getTypeDescription(dependencies)}`,
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
for (const key in dependencies) {
|
|
31
|
-
if (Object.getOwnPropertyDescriptor(dependencies, key)?.get) {
|
|
32
|
-
throw new TypeError(
|
|
33
|
-
`[cloneRouter] Getters not allowed in dependencies: "${key}"`,
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
12
|
export function cloneRouter<
|
|
40
13
|
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
41
14
|
>(
|
|
@@ -48,9 +21,7 @@ export function cloneRouter<
|
|
|
48
21
|
throw new RouterError(errorCodes.ROUTER_DISPOSED);
|
|
49
22
|
}
|
|
50
23
|
|
|
51
|
-
|
|
52
|
-
validateCloneArgs(dependencies);
|
|
53
|
-
}
|
|
24
|
+
ctx.validator?.dependencies.validateCloneArgs(dependencies);
|
|
54
25
|
|
|
55
26
|
// Get source store directly
|
|
56
27
|
const sourceStore = ctx.routeGetStore();
|