@real-router/core 0.25.3 → 0.26.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 +163 -325
- package/dist/cjs/index.d.ts +47 -178
- 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/esm/index.d.mts +47 -178
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/metafile-esm.json +1 -1
- package/package.json +3 -3
- package/src/Router.ts +84 -574
- package/src/api/cloneRouter.ts +106 -0
- package/src/api/getDependenciesApi.ts +216 -0
- package/src/api/getLifecycleApi.ts +67 -0
- package/src/api/getPluginApi.ts +118 -0
- package/src/api/getRoutesApi.ts +566 -0
- package/src/api/index.ts +16 -0
- package/src/api/types.ts +7 -0
- package/src/getNavigator.ts +5 -2
- package/src/index.ts +17 -3
- package/src/internals.ts +115 -0
- package/src/namespaces/DependenciesNamespace/dependenciesStore.ts +30 -0
- package/src/namespaces/DependenciesNamespace/index.ts +3 -1
- package/src/namespaces/DependenciesNamespace/validators.ts +2 -4
- package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +1 -20
- package/src/namespaces/EventBusNamespace/validators.ts +36 -0
- package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +1 -10
- package/src/namespaces/NavigationNamespace/transition/errorHandling.ts +2 -0
- package/src/namespaces/NavigationNamespace/transition/{executeLifecycleHooks.ts → executeLifecycleGuards.ts} +9 -7
- package/src/namespaces/NavigationNamespace/transition/index.ts +3 -3
- package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +1 -16
- package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +133 -1089
- package/src/namespaces/RoutesNamespace/forwardToValidation.ts +411 -0
- package/src/namespaces/RoutesNamespace/helpers.ts +1 -407
- package/src/namespaces/RoutesNamespace/index.ts +2 -0
- package/src/namespaces/RoutesNamespace/routesStore.ts +388 -0
- package/src/namespaces/RoutesNamespace/validators.ts +209 -3
- package/src/namespaces/StateNamespace/StateNamespace.ts +1 -44
- package/src/namespaces/StateNamespace/validators.ts +46 -0
- package/src/namespaces/index.ts +3 -5
- package/src/types.ts +12 -138
- package/src/wiring/RouterWiringBuilder.ts +30 -36
- package/src/wiring/types.ts +3 -6
- package/src/wiring/wireRouter.ts +0 -1
- package/src/namespaces/CloneNamespace/CloneNamespace.ts +0 -120
- package/src/namespaces/CloneNamespace/index.ts +0 -3
- package/src/namespaces/CloneNamespace/types.ts +0 -42
- package/src/namespaces/DependenciesNamespace/DependenciesNamespace.ts +0 -248
- package/src/namespaces/RoutesNamespace/stateBuilder.ts +0 -70
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/DependenciesNamespace/DependenciesNamespace.ts
|
|
2
|
-
|
|
3
|
-
import { logger } from "@real-router/logger";
|
|
4
|
-
import { getTypeDescription } from "type-guards";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
validateDependencyExists,
|
|
8
|
-
validateDependencyLimit,
|
|
9
|
-
validateDependencyName,
|
|
10
|
-
validateDependenciesObject,
|
|
11
|
-
validateSetDependencyArgs,
|
|
12
|
-
} from "./validators";
|
|
13
|
-
import { DEFAULT_LIMITS } from "../../constants";
|
|
14
|
-
import { computeThresholds } from "../../helpers";
|
|
15
|
-
|
|
16
|
-
import type { Limits } from "../../types";
|
|
17
|
-
import type { DefaultDependencies } from "@real-router/types";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Independent namespace for managing router dependencies.
|
|
21
|
-
*
|
|
22
|
-
* Static methods handle validation (called by facade).
|
|
23
|
-
* Instance methods handle storage and business logic (limits, warnings).
|
|
24
|
-
*/
|
|
25
|
-
export class DependenciesNamespace<
|
|
26
|
-
Dependencies extends DefaultDependencies = DefaultDependencies,
|
|
27
|
-
> {
|
|
28
|
-
#dependencies: Partial<Dependencies> = Object.create(
|
|
29
|
-
null,
|
|
30
|
-
) as Partial<Dependencies>;
|
|
31
|
-
|
|
32
|
-
#limits: Limits = DEFAULT_LIMITS;
|
|
33
|
-
|
|
34
|
-
constructor(initialDependencies: Partial<Dependencies> = {} as Dependencies) {
|
|
35
|
-
this.setMultiple(initialDependencies);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// =========================================================================
|
|
39
|
-
// Static validation methods (called by facade before instance methods)
|
|
40
|
-
// Proxy to functions in validators.ts for separation of concerns
|
|
41
|
-
// =========================================================================
|
|
42
|
-
|
|
43
|
-
static validateName(
|
|
44
|
-
name: unknown,
|
|
45
|
-
methodName: string,
|
|
46
|
-
): asserts name is string {
|
|
47
|
-
validateDependencyName(name, methodName);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
static validateSetDependencyArgs(name: unknown): asserts name is string {
|
|
51
|
-
validateSetDependencyArgs(name);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
static validateDependenciesObject(
|
|
55
|
-
deps: unknown,
|
|
56
|
-
methodName: string,
|
|
57
|
-
): asserts deps is Record<string, unknown> {
|
|
58
|
-
validateDependenciesObject(deps, methodName);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
static validateDependencyExists(
|
|
62
|
-
value: unknown,
|
|
63
|
-
dependencyName: string,
|
|
64
|
-
): asserts value is NonNullable<unknown> {
|
|
65
|
-
validateDependencyExists(value, dependencyName);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
static validateDependencyLimit(
|
|
69
|
-
currentCount: number,
|
|
70
|
-
newCount: number,
|
|
71
|
-
methodName: string,
|
|
72
|
-
maxDependencies?: number,
|
|
73
|
-
): void {
|
|
74
|
-
validateDependencyLimit(
|
|
75
|
-
currentCount,
|
|
76
|
-
newCount,
|
|
77
|
-
methodName,
|
|
78
|
-
maxDependencies,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
setLimits(limits: Limits): void {
|
|
83
|
-
this.#limits = limits;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// =========================================================================
|
|
87
|
-
// Instance methods (trust input - already validated by facade)
|
|
88
|
-
// =========================================================================
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Sets a single dependency.
|
|
92
|
-
* Returns true if set, false if value was undefined (no-op).
|
|
93
|
-
*
|
|
94
|
-
* @param dependencyName - Already validated by facade
|
|
95
|
-
* @param dependencyValue - Value to set
|
|
96
|
-
*/
|
|
97
|
-
set<K extends keyof Dependencies & string>(
|
|
98
|
-
dependencyName: K,
|
|
99
|
-
dependencyValue: Dependencies[K],
|
|
100
|
-
): boolean {
|
|
101
|
-
// undefined = "don't set" (feature for conditional setting)
|
|
102
|
-
if (dependencyValue === undefined) {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const isNewKey = !Object.hasOwn(this.#dependencies, dependencyName);
|
|
107
|
-
|
|
108
|
-
if (isNewKey) {
|
|
109
|
-
// Only check limit when adding new keys (overwrites don't increase count)
|
|
110
|
-
this.#checkDependencyCount("setDependency");
|
|
111
|
-
} else {
|
|
112
|
-
const oldValue = this.#dependencies[dependencyName];
|
|
113
|
-
const isChanging = oldValue !== dependencyValue;
|
|
114
|
-
// Special case for NaN idempotency (NaN !== NaN is always true)
|
|
115
|
-
const bothAreNaN =
|
|
116
|
-
Number.isNaN(oldValue) && Number.isNaN(dependencyValue);
|
|
117
|
-
|
|
118
|
-
if (isChanging && !bothAreNaN) {
|
|
119
|
-
logger.warn(
|
|
120
|
-
"router.setDependency",
|
|
121
|
-
"Router dependency already exists and is being overwritten:",
|
|
122
|
-
dependencyName,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
this.#dependencies[dependencyName] = dependencyValue;
|
|
128
|
-
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Sets multiple dependencies at once.
|
|
134
|
-
* Limit check should be done by facade before calling this method.
|
|
135
|
-
*
|
|
136
|
-
* @param deps - Already validated by facade
|
|
137
|
-
*/
|
|
138
|
-
setMultiple(deps: Partial<Dependencies>): void {
|
|
139
|
-
const overwrittenKeys: string[] = [];
|
|
140
|
-
|
|
141
|
-
for (const key in deps) {
|
|
142
|
-
if (deps[key] !== undefined) {
|
|
143
|
-
if (Object.hasOwn(this.#dependencies, key)) {
|
|
144
|
-
overwrittenKeys.push(key);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
this.#dependencies[key] = deps[key];
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (overwrittenKeys.length > 0) {
|
|
152
|
-
logger.warn(
|
|
153
|
-
"router.setDependencies",
|
|
154
|
-
"Overwritten:",
|
|
155
|
-
overwrittenKeys.join(", "),
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Gets a single dependency.
|
|
162
|
-
* Throws if not found.
|
|
163
|
-
*
|
|
164
|
-
* @param dependencyName - Already validated by facade
|
|
165
|
-
*/
|
|
166
|
-
get<K extends keyof Dependencies>(dependencyName: K): Dependencies[K] {
|
|
167
|
-
return this.#dependencies[dependencyName] as Dependencies[K];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Gets all dependencies as a shallow copy.
|
|
172
|
-
*/
|
|
173
|
-
getAll(): Partial<Dependencies> {
|
|
174
|
-
return { ...this.#dependencies };
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Gets the current number of dependencies.
|
|
179
|
-
*/
|
|
180
|
-
count(): number {
|
|
181
|
-
return Object.keys(this.#dependencies).length;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Removes a dependency by name.
|
|
186
|
-
* Logs warning if dependency doesn't exist.
|
|
187
|
-
*/
|
|
188
|
-
remove(dependencyName: keyof Dependencies): void {
|
|
189
|
-
if (!Object.hasOwn(this.#dependencies, dependencyName)) {
|
|
190
|
-
logger.warn(
|
|
191
|
-
`router.removeDependency`,
|
|
192
|
-
`Attempted to remove non-existent dependency: "${getTypeDescription(dependencyName)}"`,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
delete this.#dependencies[dependencyName];
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Checks if a dependency exists.
|
|
201
|
-
*/
|
|
202
|
-
has(dependencyName: keyof Dependencies): boolean {
|
|
203
|
-
return Object.hasOwn(this.#dependencies, dependencyName);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Removes all dependencies.
|
|
208
|
-
*/
|
|
209
|
-
reset(): void {
|
|
210
|
-
this.#dependencies = Object.create(null) as Partial<Dependencies>;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// =========================================================================
|
|
214
|
-
// Private methods (business logic)
|
|
215
|
-
// =========================================================================
|
|
216
|
-
|
|
217
|
-
#checkDependencyCount(methodName: string): void {
|
|
218
|
-
const maxDependencies = this.#limits.maxDependencies;
|
|
219
|
-
|
|
220
|
-
if (maxDependencies === 0) {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const currentCount = Object.keys(this.#dependencies).length;
|
|
225
|
-
|
|
226
|
-
const { warn, error } = computeThresholds(maxDependencies);
|
|
227
|
-
|
|
228
|
-
if (currentCount === warn) {
|
|
229
|
-
logger.warn(
|
|
230
|
-
`router.${methodName}`,
|
|
231
|
-
`${warn} dependencies registered. ` + `Consider if all are necessary.`,
|
|
232
|
-
);
|
|
233
|
-
} else if (currentCount === error) {
|
|
234
|
-
logger.error(
|
|
235
|
-
`router.${methodName}`,
|
|
236
|
-
`${error} dependencies registered! ` +
|
|
237
|
-
`This indicates architectural problems. ` +
|
|
238
|
-
`Hard limit at ${maxDependencies}.`,
|
|
239
|
-
);
|
|
240
|
-
} else if (currentCount >= maxDependencies) {
|
|
241
|
-
throw new Error(
|
|
242
|
-
`[router.${methodName}] Dependency limit exceeded (${maxDependencies}). ` +
|
|
243
|
-
`Current: ${currentCount}. This is likely a bug in your code. ` +
|
|
244
|
-
`If you genuinely need more dependencies, your architecture needs refactoring.`,
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// packages/core/src/namespaces/RoutesNamespace/stateBuilder.ts
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* State Builder Utilities.
|
|
5
|
-
*
|
|
6
|
-
* Functions for building RouteTreeState from raw route segments.
|
|
7
|
-
* This module handles the conversion from low-level route-node data
|
|
8
|
-
* to the higher-level state representation used by real-router.
|
|
9
|
-
*
|
|
10
|
-
* @module core/stateBuilder
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { RouteParams, RouteTreeState } from "route-tree";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Builds a dot-separated route name from segments.
|
|
17
|
-
*
|
|
18
|
-
* @param segments - Array of route segments with names
|
|
19
|
-
* @returns Dot-separated route name (e.g., "users.profile")
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```typescript
|
|
23
|
-
* const segments = [{ name: "users" }, { name: "profile" }];
|
|
24
|
-
* buildNameFromSegments(segments); // "users.profile"
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
export function buildNameFromSegments(
|
|
28
|
-
segments: readonly { fullName: string }[],
|
|
29
|
-
): string {
|
|
30
|
-
return segments.at(-1)?.fullName ?? "";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Creates a RouteTreeState from a match result.
|
|
35
|
-
*
|
|
36
|
-
* This function is the primary way to build a RouteTreeState when
|
|
37
|
-
* you have a result from matcher.match().
|
|
38
|
-
*
|
|
39
|
-
* @param matchResult - Result from matcher.match() containing segments and params
|
|
40
|
-
* @param matchResult.segments - Matched route segments
|
|
41
|
-
* @param matchResult.params - Matched route params
|
|
42
|
-
* @param matchResult.meta - Matched route meta
|
|
43
|
-
* @param name - Optional explicit name (if not provided, built from segments)
|
|
44
|
-
* @returns RouteTreeState with name, params, and meta
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```typescript
|
|
48
|
-
* const matchResult = matcher.match("/users/123");
|
|
49
|
-
* if (matchResult) {
|
|
50
|
-
* const state = createRouteState(matchResult);
|
|
51
|
-
* // { name: "users.profile", params: { id: "123" }, meta: {...} }
|
|
52
|
-
* }
|
|
53
|
-
* ```
|
|
54
|
-
*/
|
|
55
|
-
export function createRouteState<P extends RouteParams = RouteParams>(
|
|
56
|
-
matchResult: {
|
|
57
|
-
readonly segments: readonly { fullName: string }[];
|
|
58
|
-
readonly params: Readonly<Record<string, unknown>>;
|
|
59
|
-
readonly meta: Readonly<Record<string, Record<string, "url" | "query">>>;
|
|
60
|
-
},
|
|
61
|
-
name?: string,
|
|
62
|
-
): RouteTreeState<P> {
|
|
63
|
-
const resolvedName = name ?? buildNameFromSegments(matchResult.segments);
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
name: resolvedName,
|
|
67
|
-
params: matchResult.params as P,
|
|
68
|
-
meta: matchResult.meta as Record<string, Record<string, "url" | "query">>,
|
|
69
|
-
};
|
|
70
|
-
}
|