@real-router/core 0.24.0 → 0.25.1

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.
Files changed (43) hide show
  1. package/README.md +0 -15
  2. package/dist/cjs/index.d.ts +4 -10
  3. package/dist/cjs/index.js +1 -1
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/metafile-cjs.json +1 -1
  6. package/dist/esm/index.d.mts +4 -10
  7. package/dist/esm/index.mjs +1 -1
  8. package/dist/esm/index.mjs.map +1 -1
  9. package/dist/esm/metafile-esm.json +1 -1
  10. package/package.json +4 -4
  11. package/src/Router.ts +11 -54
  12. package/src/RouterError.ts +1 -1
  13. package/src/constants.ts +1 -3
  14. package/src/helpers.ts +1 -1
  15. package/src/index.ts +1 -3
  16. package/src/namespaces/CloneNamespace/CloneNamespace.ts +6 -6
  17. package/src/namespaces/CloneNamespace/types.ts +3 -7
  18. package/src/namespaces/DependenciesNamespace/DependenciesNamespace.ts +2 -4
  19. package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +1 -0
  20. package/src/namespaces/NavigationNamespace/transition/{wrapSyncError.ts → errorHandling.ts} +23 -17
  21. package/src/namespaces/NavigationNamespace/transition/executeLifecycleHooks.ts +6 -6
  22. package/src/namespaces/NavigationNamespace/transition/index.ts +6 -27
  23. package/src/namespaces/NavigationNamespace/types.ts +0 -4
  24. package/src/namespaces/PluginsNamespace/PluginsNamespace.ts +2 -2
  25. package/src/namespaces/RouteLifecycleNamespace/validators.ts +1 -1
  26. package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +50 -31
  27. package/src/namespaces/RoutesNamespace/stateBuilder.ts +1 -1
  28. package/src/namespaces/index.ts +0 -2
  29. package/src/transitionPath.ts +4 -40
  30. package/src/typeGuards.ts +1 -1
  31. package/src/types.ts +0 -12
  32. package/src/wiring/RouterWiringBuilder.ts +1 -17
  33. package/src/wiring/types.ts +0 -3
  34. package/src/wiring/wireRouter.ts +0 -1
  35. package/src/namespaces/MiddlewareNamespace/MiddlewareNamespace.ts +0 -221
  36. package/src/namespaces/MiddlewareNamespace/constants.ts +0 -3
  37. package/src/namespaces/MiddlewareNamespace/index.ts +0 -5
  38. package/src/namespaces/MiddlewareNamespace/types.ts +0 -28
  39. package/src/namespaces/MiddlewareNamespace/validators.ts +0 -95
  40. package/src/namespaces/NavigationNamespace/transition/executeMiddleware.ts +0 -56
  41. package/src/namespaces/NavigationNamespace/transition/makeError.ts +0 -37
  42. package/src/namespaces/NavigationNamespace/transition/mergeStates.ts +0 -54
  43. package/src/namespaces/NavigationNamespace/transition/processLifecycleResult.ts +0 -69
@@ -1,10 +1,9 @@
1
- // packages/real-router/modules/transition/index.ts
1
+ // packages/core/src/namespaces/NavigationNamespace/transition/index.ts
2
2
 
3
3
  import { executeLifecycleHooks } from "./executeLifecycleHooks";
4
- import { executeMiddleware } from "./executeMiddleware";
5
4
  import { constants, errorCodes } from "../../../constants";
6
5
  import { RouterError } from "../../../RouterError";
7
- import { getTransitionPath, nameToIDs } from "../../../transitionPath";
6
+ import { getTransitionPath } from "../../../transitionPath";
8
7
 
9
8
  import type { TransitionDependencies, TransitionOutput } from "../types";
10
9
  import type { NavigationOptions, State } from "@real-router/types";
@@ -18,7 +17,6 @@ export async function transition(
18
17
  // We're caching the necessary data
19
18
  const [canDeactivateFunctions, canActivateFunctions] =
20
19
  deps.getLifecycleFunctions();
21
- const middlewareFunctions = deps.getMiddlewareFunctions();
22
20
  const isUnknownRoute = toState.name === constants.UNKNOWN_ROUTE;
23
21
 
24
22
  // State management functions
@@ -34,9 +32,6 @@ export async function transition(
34
32
  const shouldDeactivate =
35
33
  fromState && !opts.forceDeactivate && toDeactivate.length > 0;
36
34
  const shouldActivate = !isUnknownRoute && toActivate.length > 0;
37
- const shouldRunMiddleware = middlewareFunctions.length > 0;
38
-
39
- let currentState = toState;
40
35
 
41
36
  if (shouldDeactivate) {
42
37
  await executeLifecycleHooks(
@@ -68,35 +63,19 @@ export async function transition(
68
63
  throw new RouterError(errorCodes.TRANSITION_CANCELLED);
69
64
  }
70
65
 
71
- if (shouldRunMiddleware) {
72
- currentState = await executeMiddleware(
73
- middlewareFunctions,
74
- currentState,
75
- fromState,
76
- isCancelled,
77
- );
78
- }
79
-
80
- if (isCancelled()) {
81
- throw new RouterError(errorCodes.TRANSITION_CANCELLED);
82
- }
83
-
84
66
  // Automatic cleaning of inactive segments
85
67
  if (fromState) {
86
- const activeSegments = nameToIDs(toState.name);
87
- const previousActiveSegments = nameToIDs(fromState.name);
88
-
89
- for (const name of previousActiveSegments) {
90
- if (!activeSegments.includes(name) && canDeactivateFunctions.has(name)) {
68
+ for (const name of toDeactivate) {
69
+ if (!toActivate.includes(name) && canDeactivateFunctions.has(name)) {
91
70
  deps.clearCanDeactivate(name);
92
71
  }
93
72
  }
94
73
  }
95
74
 
96
75
  return {
97
- state: currentState,
76
+ state: toState,
98
77
  meta: {
99
- phase: "middleware",
78
+ phase: "activating",
100
79
  segments: {
101
80
  deactivated: toDeactivate,
102
81
  activated: toActivate,
@@ -3,7 +3,6 @@
3
3
  import type { BuildStateResultWithSegments } from "../../types";
4
4
  import type {
5
5
  GuardFn,
6
- Middleware,
7
6
  NavigationOptions,
8
7
  Options,
9
8
  Params,
@@ -112,9 +111,6 @@ export interface TransitionDependencies {
112
111
  /** Get lifecycle functions (canDeactivate, canActivate maps) */
113
112
  getLifecycleFunctions: () => [Map<string, GuardFn>, Map<string, GuardFn>];
114
113
 
115
- /** Get middleware functions array */
116
- getMiddlewareFunctions: () => Middleware[];
117
-
118
114
  /** Check if router is active (for cancellation check on stop()) */
119
115
  isActive: () => boolean;
120
116
 
@@ -222,8 +222,8 @@ export class PluginsNamespace<
222
222
  /**
223
223
  * Disposes all registered plugins by running their teardown callbacks
224
224
  * and removing event listener subscriptions.
225
- * Unlike {@link MiddlewareNamespace.clearAll}, active disposal is required
226
- * plugins have an active lifecycle (event subscriptions, teardown hooks).
225
+ * Active disposal is required because plugins have an active lifecycle
226
+ * (event subscriptions, teardown hooks).
227
227
  * Named "dispose" (not "clear") because there is active cleanup to perform.
228
228
  */
229
229
  disposeAll(): void {
@@ -59,7 +59,7 @@ export function validateHandlerLimit(
59
59
  throw new Error(
60
60
  `[router.${methodName}] Lifecycle handler limit exceeded (${maxLifecycleHandlers}). ` +
61
61
  `This indicates too many routes with individual handlers. ` +
62
- `Consider using middleware for cross-cutting concerns.`,
62
+ `Consider using plugins for cross-cutting concerns.`,
63
63
  );
64
64
  }
65
65
  }
@@ -77,9 +77,14 @@ export class RoutesNamespace<
77
77
 
78
78
  readonly #definitions: RouteDefinition[] = [];
79
79
  readonly #config: RouteConfig = createEmptyConfig();
80
- readonly #resolvedForwardMap: Record<string, string> = Object.create(
80
+ #resolvedForwardMap: Record<string, string> = Object.create(null) as Record<
81
+ string,
82
+ string
83
+ >;
84
+
85
+ #routeCustomFields: Record<string, Record<string, unknown>> = Object.create(
81
86
  null,
82
- ) as Record<string, string>;
87
+ ) as Record<string, Record<string, unknown>>;
83
88
 
84
89
  // Pending canActivate handlers that need to be registered after router is set
85
90
  // Key: route name, Value: canActivate factory
@@ -346,6 +351,18 @@ export class RoutesNamespace<
346
351
  return this.#enrichRoute(definition, name);
347
352
  }
348
353
 
354
+ getRouteConfig(name: string): Record<string, unknown> | undefined {
355
+ if (!this.#matcher.hasRoute(name)) {
356
+ return undefined;
357
+ }
358
+
359
+ return this.#routeCustomFields[name];
360
+ }
361
+
362
+ getRouteCustomFields(): Record<string, Record<string, unknown>> {
363
+ return this.#routeCustomFields;
364
+ }
365
+
349
366
  /**
350
367
  * Adds one or more routes to the router.
351
368
  * Input already validated by facade (properties and state-dependent checks).
@@ -473,31 +490,15 @@ export class RoutesNamespace<
473
490
  clearRoutes(): void {
474
491
  this.#definitions.length = 0;
475
492
 
476
- // Clear all config entries
477
- for (const key in this.#config.decoders) {
478
- delete this.#config.decoders[key];
479
- }
480
-
481
- for (const key in this.#config.encoders) {
482
- delete this.#config.encoders[key];
483
- }
484
-
485
- for (const key in this.#config.defaultParams) {
486
- delete this.#config.defaultParams[key];
487
- }
488
-
489
- for (const key in this.#config.forwardMap) {
490
- delete this.#config.forwardMap[key];
491
- }
492
-
493
- for (const key in this.#config.forwardFnMap) {
494
- delete this.#config.forwardFnMap[key];
495
- }
493
+ // Reset config to empty null-prototype objects
494
+ Object.assign(this.#config, createEmptyConfig());
496
495
 
497
496
  // Clear forward cache
498
- for (const key in this.#resolvedForwardMap) {
499
- delete this.#resolvedForwardMap[key];
500
- }
497
+ this.#resolvedForwardMap = Object.create(null) as Record<string, string>;
498
+ this.#routeCustomFields = Object.create(null) as Record<
499
+ string,
500
+ Record<string, unknown>
501
+ >;
501
502
 
502
503
  // Rebuild empty tree
503
504
  this.#rebuildTree();
@@ -872,6 +873,7 @@ export class RoutesNamespace<
872
873
  applyClonedConfig(
873
874
  config: RouteConfig,
874
875
  resolvedForwardMap: Record<string, string>,
876
+ routeCustomFields: Record<string, Record<string, unknown>>,
875
877
  ): void {
876
878
  Object.assign(this.#config.decoders, config.decoders);
877
879
  Object.assign(this.#config.encoders, config.encoders);
@@ -879,6 +881,7 @@ export class RoutesNamespace<
879
881
  Object.assign(this.#config.forwardMap, config.forwardMap);
880
882
  Object.assign(this.#config.forwardFnMap, config.forwardFnMap);
881
883
  this.setResolvedForwardMap({ ...resolvedForwardMap });
884
+ Object.assign(this.#routeCustomFields, routeCustomFields);
882
885
  }
883
886
 
884
887
  /**
@@ -1219,9 +1222,7 @@ export class RoutesNamespace<
1219
1222
 
1220
1223
  #validateAndCacheForwardMap(): void {
1221
1224
  // Clear existing cache
1222
- for (const key in this.#resolvedForwardMap) {
1223
- delete this.#resolvedForwardMap[key];
1224
- }
1225
+ this.#resolvedForwardMap = Object.create(null) as Record<string, string>;
1225
1226
 
1226
1227
  // Resolve all chains
1227
1228
  for (const fromRoute of Object.keys(this.#config.forwardMap)) {
@@ -1238,9 +1239,7 @@ export class RoutesNamespace<
1238
1239
  */
1239
1240
  #cacheForwardMap(): void {
1240
1241
  // Clear existing cache
1241
- for (const key in this.#resolvedForwardMap) {
1242
- delete this.#resolvedForwardMap[key];
1243
- }
1242
+ this.#resolvedForwardMap = Object.create(null) as Record<string, string>;
1244
1243
 
1245
1244
  // Resolve chains without validation
1246
1245
  for (const fromRoute of Object.keys(this.#config.forwardMap)) {
@@ -1263,6 +1262,7 @@ export class RoutesNamespace<
1263
1262
  clearConfigEntries(this.#config.defaultParams, shouldClear);
1264
1263
  clearConfigEntries(this.#config.forwardMap, shouldClear);
1265
1264
  clearConfigEntries(this.#config.forwardFnMap, shouldClear);
1265
+ clearConfigEntries(this.#routeCustomFields, shouldClear);
1266
1266
 
1267
1267
  // Clear forwardMap entries pointing TO deleted route
1268
1268
  clearConfigEntries(this.#config.forwardMap, (key) =>
@@ -1305,6 +1305,25 @@ export class RoutesNamespace<
1305
1305
  route: Route<Dependencies>,
1306
1306
  fullName: string,
1307
1307
  ): void {
1308
+ const standardKeys = new Set([
1309
+ "name",
1310
+ "path",
1311
+ "children",
1312
+ "canActivate",
1313
+ "canDeactivate",
1314
+ "forwardTo",
1315
+ "encodeParams",
1316
+ "decodeParams",
1317
+ "defaultParams",
1318
+ ]);
1319
+ const customFields = Object.fromEntries(
1320
+ Object.entries(route).filter(([k]) => !standardKeys.has(k)),
1321
+ );
1322
+
1323
+ if (Object.keys(customFields).length > 0) {
1324
+ this.#routeCustomFields[fullName] = customFields;
1325
+ }
1326
+
1308
1327
  // Register canActivate via deps.canActivate (allows tests to spy on router.canActivate)
1309
1328
  if (route.canActivate) {
1310
1329
  // Note: Uses #depsStore directly because this method is called from constructor
@@ -1,4 +1,4 @@
1
- // packages/real-router/modules/core/stateBuilder.ts
1
+ // packages/core/src/namespaces/RoutesNamespace/stateBuilder.ts
2
2
 
3
3
  /**
4
4
  * State Builder Utilities.
@@ -12,8 +12,6 @@ export {
12
12
 
13
13
  export { StateNamespace } from "./StateNamespace";
14
14
 
15
- export { MiddlewareNamespace } from "./MiddlewareNamespace";
16
-
17
15
  export {
18
16
  PluginsNamespace,
19
17
  EVENTS_MAP,
@@ -1,6 +1,6 @@
1
- // packages/real-router/modules/transitionPath.ts
1
+ // packages/core/src/transitionPath.ts
2
2
 
3
- import type { Params, State } from "@real-router/types";
3
+ import type { State } from "@real-router/types";
4
4
 
5
5
  /**
6
6
  * Parameters extracted from a route segment.
@@ -99,19 +99,7 @@ function extractSegmentParams(name: string, state: State): SegmentParams {
99
99
 
100
100
  const result: SegmentParams = {};
101
101
 
102
- for (const key in keys as Params) {
103
- // Skip inherited properties
104
- if (!Object.hasOwn(keys, key)) {
105
- continue;
106
- }
107
-
108
- // Skip undefined values for consistent behavior (treat { key: undefined } same as missing key)
109
- // Edge case: can appear from manual State creation or object merging
110
- // @ts-expect-error Params type doesn't allow undefined, but it can appear at runtime
111
- if (keys[key] === undefined) {
112
- continue;
113
- }
114
-
102
+ for (const key of Object.keys(keys)) {
115
103
  const value = state.params[key];
116
104
 
117
105
  // Skip null/undefined values
@@ -166,15 +154,9 @@ function pointOfDifference(
166
154
  const toParams = extractSegmentParams(toSegment, toState);
167
155
  const fromParams = extractSegmentParams(fromSegment, fromState);
168
156
 
169
- // Fast check: different number of parameters
157
+ // Compare parameter values
170
158
  const toKeys = Object.keys(toParams);
171
- const fromKeys = Object.keys(fromParams);
172
159
 
173
- if (toKeys.length !== fromKeys.length) {
174
- return i;
175
- }
176
-
177
- // Detailed check: compare parameter values
178
160
  for (const key of toKeys) {
179
161
  if (toParams[key] !== fromParams[key]) {
180
162
  return i;
@@ -384,24 +366,6 @@ export function getTransitionPath(
384
366
  };
385
367
  }
386
368
 
387
- // ===== FAST PATH 4: Same routes with empty meta.params =====
388
- // If both have empty meta.params {}, no parameter checking needed
389
- if (toState.name === fromState.name && toHasMeta && fromHasMeta) {
390
- const toParamsEmpty =
391
- toState.meta && Object.keys(toState.meta.params).length === 0;
392
- const fromParamsEmpty =
393
- fromState.meta && Object.keys(fromState.meta.params).length === 0;
394
-
395
- if (toParamsEmpty && fromParamsEmpty) {
396
- // Both have empty params - no transition needed
397
- return {
398
- intersection: toState.name,
399
- toActivate: [],
400
- toDeactivate: [],
401
- };
402
- }
403
- }
404
-
405
369
  // ===== STANDARD PATH: Routes with parameters =====
406
370
  // Use original algorithm for complex cases with parameters
407
371
  const toStateIds = nameToIDs(toState.name);
package/src/typeGuards.ts CHANGED
@@ -1,4 +1,4 @@
1
- // packages/real-router/modules/typeGuards.ts
1
+ // packages/core/src/typeGuards.ts
2
2
 
3
3
  /**
4
4
  * Re-export common type guards from centralized type-guards package
package/src/types.ts CHANGED
@@ -16,7 +16,6 @@ import type {
16
16
  ForwardToCallback,
17
17
  GuardFn,
18
18
  LimitsConfig,
19
- Middleware,
20
19
  NavigationOptions,
21
20
  Params,
22
21
  Plugin,
@@ -183,17 +182,6 @@ export type GuardFnFactory<
183
182
  getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K],
184
183
  ) => GuardFn;
185
184
 
186
- /**
187
- * Factory function for creating middleware.
188
- * Receives the router instance and a dependency getter.
189
- */
190
- export type MiddlewareFactory<
191
- Dependencies extends DefaultDependencies = DefaultDependencies,
192
- > = (
193
- router: Router<Dependencies>,
194
- getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K],
195
- ) => Middleware;
196
-
197
185
  /**
198
186
  * Factory function for creating plugins.
199
187
  * Receives the router instance and a dependency getter.
@@ -2,7 +2,6 @@
2
2
 
3
3
  import type { EventBusNamespace } from "../namespaces";
4
4
  import type { WiringOptions } from "./types";
5
- import type { MiddlewareDependencies } from "../namespaces/MiddlewareNamespace";
6
5
  import type {
7
6
  NavigationDependencies,
8
7
  TransitionDependencies,
@@ -23,7 +22,6 @@ export class RouterWiringBuilder<
23
22
  private readonly state: WiringOptions<Dependencies>["state"];
24
23
  private readonly routes: WiringOptions<Dependencies>["routes"];
25
24
  private readonly routeLifecycle: WiringOptions<Dependencies>["routeLifecycle"];
26
- private readonly middleware: WiringOptions<Dependencies>["middleware"];
27
25
  private readonly plugins: WiringOptions<Dependencies>["plugins"];
28
26
  private readonly navigation: WiringOptions<Dependencies>["navigation"];
29
27
  private readonly lifecycle: WiringOptions<Dependencies>["lifecycle"];
@@ -38,7 +36,6 @@ export class RouterWiringBuilder<
38
36
  this.state = wiringOptions.state;
39
37
  this.routes = wiringOptions.routes;
40
38
  this.routeLifecycle = wiringOptions.routeLifecycle;
41
- this.middleware = wiringOptions.middleware;
42
39
  this.plugins = wiringOptions.plugins;
43
40
  this.navigation = wiringOptions.navigation;
44
41
  this.lifecycle = wiringOptions.lifecycle;
@@ -49,7 +46,6 @@ export class RouterWiringBuilder<
49
46
  wireLimits(): void {
50
47
  this.dependencies.setLimits(this.limits);
51
48
  this.plugins.setLimits(this.limits);
52
- this.middleware.setLimits(this.limits);
53
49
  this.eventBus.setLimits({
54
50
  maxListeners: this.limits.maxListeners,
55
51
  warnListeners: this.limits.warnListeners,
@@ -90,17 +86,6 @@ export class RouterWiringBuilder<
90
86
  this.routes.setLifecycleNamespace(this.routeLifecycle);
91
87
  }
92
88
 
93
- wireMiddlewareDeps(): void {
94
- this.middleware.setRouter(this.router);
95
-
96
- const middlewareDeps: MiddlewareDependencies<Dependencies> = {
97
- getDependency: <K extends keyof Dependencies>(dependencyName: K) =>
98
- this.dependencies.get(dependencyName),
99
- };
100
-
101
- this.middleware.setDependencies(middlewareDeps);
102
- }
103
-
104
89
  wirePluginsDeps(): void {
105
90
  this.plugins.setRouter(this.router);
106
91
 
@@ -166,7 +151,6 @@ export class RouterWiringBuilder<
166
151
 
167
152
  const transitionDeps: TransitionDependencies = {
168
153
  getLifecycleFunctions: () => this.routeLifecycle.getFunctions(),
169
- getMiddlewareFunctions: () => this.middleware.getFunctions(),
170
154
  isActive: () => this.router.isActive(),
171
155
  isTransitioning: () => this.eventBus.isTransitioning(),
172
156
  clearCanDeactivate: (name) => {
@@ -217,10 +201,10 @@ export class RouterWiringBuilder<
217
201
  dependencies: this.dependencies.getAll(),
218
202
  canDeactivateFactories,
219
203
  canActivateFactories,
220
- middlewareFactories: this.middleware.getFactories(),
221
204
  pluginFactories: this.plugins.getAll(),
222
205
  routeConfig: this.routes.getConfig(),
223
206
  resolvedForwardMap: this.routes.getResolvedForwardMap(),
207
+ routeCustomFields: this.routes.getRouteCustomFields(),
224
208
  };
225
209
  });
226
210
  }
@@ -4,7 +4,6 @@ import type {
4
4
  CloneNamespace,
5
5
  DependenciesNamespace,
6
6
  EventBusNamespace,
7
- MiddlewareNamespace,
8
7
  NavigationNamespace,
9
8
  OptionsNamespace,
10
9
  PluginsNamespace,
@@ -38,8 +37,6 @@ export interface WiringOptions<Dependencies extends DefaultDependencies> {
38
37
  routes: RoutesNamespace<Dependencies>;
39
38
  /** Route lifecycle namespace (canActivate/canDeactivate guards) */
40
39
  routeLifecycle: RouteLifecycleNamespace<Dependencies>;
41
- /** Middleware namespace */
42
- middleware: MiddlewareNamespace<Dependencies>;
43
40
  /** Plugins namespace */
44
41
  plugins: PluginsNamespace<Dependencies>;
45
42
  /** Navigation namespace */
@@ -19,7 +19,6 @@ export function wireRouter<Dependencies extends DefaultDependencies>(
19
19
  builder.wireLimits();
20
20
  builder.wireRouteLifecycleDeps();
21
21
  builder.wireRoutesDeps();
22
- builder.wireMiddlewareDeps();
23
22
  builder.wirePluginsDeps();
24
23
  builder.wireNavigationDeps();
25
24
  builder.wireLifecycleDeps();