@real-router/core 0.25.4 → 0.27.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.
Files changed (49) hide show
  1. package/README.md +175 -323
  2. package/dist/cjs/index.d.ts +47 -178
  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 +47 -178
  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 +3 -3
  11. package/src/Router.ts +86 -574
  12. package/src/api/cloneRouter.ts +106 -0
  13. package/src/api/getDependenciesApi.ts +216 -0
  14. package/src/api/getLifecycleApi.ts +67 -0
  15. package/src/api/getPluginApi.ts +118 -0
  16. package/src/api/getRoutesApi.ts +566 -0
  17. package/src/api/index.ts +16 -0
  18. package/src/api/types.ts +7 -0
  19. package/src/getNavigator.ts +5 -2
  20. package/src/index.ts +17 -3
  21. package/src/internals.ts +115 -0
  22. package/src/namespaces/DependenciesNamespace/dependenciesStore.ts +30 -0
  23. package/src/namespaces/DependenciesNamespace/index.ts +3 -1
  24. package/src/namespaces/DependenciesNamespace/validators.ts +2 -4
  25. package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +1 -20
  26. package/src/namespaces/EventBusNamespace/validators.ts +36 -0
  27. package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +63 -12
  28. package/src/namespaces/NavigationNamespace/transition/errorHandling.ts +2 -0
  29. package/src/namespaces/NavigationNamespace/transition/{executeLifecycleHooks.ts → executeLifecycleGuards.ts} +16 -7
  30. package/src/namespaces/NavigationNamespace/transition/index.ts +7 -4
  31. package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +1 -16
  32. package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +133 -1089
  33. package/src/namespaces/RoutesNamespace/forwardToValidation.ts +411 -0
  34. package/src/namespaces/RoutesNamespace/helpers.ts +1 -407
  35. package/src/namespaces/RoutesNamespace/index.ts +2 -0
  36. package/src/namespaces/RoutesNamespace/routesStore.ts +388 -0
  37. package/src/namespaces/RoutesNamespace/validators.ts +209 -3
  38. package/src/namespaces/StateNamespace/StateNamespace.ts +1 -44
  39. package/src/namespaces/StateNamespace/validators.ts +46 -0
  40. package/src/namespaces/index.ts +3 -5
  41. package/src/types.ts +12 -138
  42. package/src/wiring/RouterWiringBuilder.ts +30 -36
  43. package/src/wiring/types.ts +3 -6
  44. package/src/wiring/wireRouter.ts +0 -1
  45. package/src/namespaces/CloneNamespace/CloneNamespace.ts +0 -120
  46. package/src/namespaces/CloneNamespace/index.ts +0 -3
  47. package/src/namespaces/CloneNamespace/types.ts +0 -42
  48. package/src/namespaces/DependenciesNamespace/DependenciesNamespace.ts +0 -248
  49. package/src/namespaces/RoutesNamespace/stateBuilder.ts +0 -70
package/src/Router.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  // packages/core/src/Router.ts
2
- /* eslint-disable unicorn/prefer-event-target -- custom EventEmitter package, not Node.js EventEmitter */
3
2
 
4
3
  /**
5
4
  * Router class - facade with integrated namespaces.
@@ -14,9 +13,8 @@ import { validateRouteName } from "type-guards";
14
13
  import { errorCodes } from "./constants";
15
14
  import { createRouterFSM } from "./fsm";
16
15
  import { createLimits } from "./helpers";
16
+ import { getInternals, registerInternals } from "./internals";
17
17
  import {
18
- CloneNamespace,
19
- DependenciesNamespace,
20
18
  EventBusNamespace,
21
19
  NavigationNamespace,
22
20
  OptionsNamespace,
@@ -25,33 +23,31 @@ import {
25
23
  RouterLifecycleNamespace,
26
24
  RoutesNamespace,
27
25
  StateNamespace,
26
+ createDependenciesStore,
28
27
  } from "./namespaces";
28
+ import { validateDependenciesObject } from "./namespaces/DependenciesNamespace/validators";
29
29
  import { CACHED_ALREADY_STARTED_ERROR } from "./namespaces/RouterLifecycleNamespace/constants";
30
+ import {
31
+ validateAddRouteArgs,
32
+ validateBuildPathArgs,
33
+ validateIsActiveRouteArgs,
34
+ validateRoutes,
35
+ validateShouldUpdateNodeArgs,
36
+ } from "./namespaces/RoutesNamespace/validators";
30
37
  import { RouterError } from "./RouterError";
31
38
  import { getTransitionPath } from "./transitionPath";
32
39
  import { isLoggerConfig } from "./typeGuards";
33
40
  import { RouterWiringBuilder, wireRouter } from "./wiring";
34
41
 
35
- import type {
36
- EventMethodMap,
37
- GuardFnFactory,
38
- Limits,
39
- PluginFactory,
40
- Route,
41
- RouteConfigUpdate,
42
- RouterEventMap,
43
- } from "./types";
42
+ import type { DependenciesStore } from "./namespaces";
43
+ import type { Limits, PluginFactory, Route, RouterEventMap } from "./types";
44
44
  import type {
45
45
  DefaultDependencies,
46
- EventName,
47
46
  NavigationOptions,
48
47
  Options,
49
48
  Params,
50
- Plugin,
51
- RouteTreeState,
52
- SimpleState,
49
+ Router as RouterInterface,
53
50
  State,
54
- StateMetaInput,
55
51
  SubscribeFn,
56
52
  Unsubscribe,
57
53
  } from "@real-router/types";
@@ -62,8 +58,8 @@ import type { CreateMatcherOptions } from "route-tree";
62
58
  *
63
59
  * All functionality is provided by namespace classes:
64
60
  * - OptionsNamespace: getOptions (immutable)
65
- * - DependenciesNamespace: get/set/remove dependencies
66
- * - EventEmitter: event listeners, subscribe
61
+ * - DependenciesStore: get/set/remove dependencies
62
+ * - EventEmitter: subscribe
67
63
  * - StateNamespace: state storage (getState, setState, getPreviousState)
68
64
  * - RoutesNamespace: route tree operations
69
65
  * - RouteLifecycleNamespace: canActivate/canDeactivate guards
@@ -75,8 +71,7 @@ import type { CreateMatcherOptions } from "route-tree";
75
71
  */
76
72
  export class Router<
77
73
  Dependencies extends DefaultDependencies = DefaultDependencies,
78
- > {
79
- // Index signatures to satisfy interface
74
+ > implements RouterInterface<Dependencies> {
80
75
  [key: string]: unknown;
81
76
 
82
77
  // ============================================================================
@@ -85,14 +80,13 @@ export class Router<
85
80
 
86
81
  readonly #options: OptionsNamespace;
87
82
  readonly #limits: Limits;
88
- readonly #dependencies: DependenciesNamespace<Dependencies>;
83
+ readonly #dependenciesStore: DependenciesStore<Dependencies>;
89
84
  readonly #state: StateNamespace;
90
85
  readonly #routes: RoutesNamespace<Dependencies>;
91
86
  readonly #routeLifecycle: RouteLifecycleNamespace<Dependencies>;
92
87
  readonly #plugins: PluginsNamespace<Dependencies>;
93
88
  readonly #navigation: NavigationNamespace;
94
89
  readonly #lifecycle: RouterLifecycleNamespace;
95
- readonly #clone: CloneNamespace<Dependencies>;
96
90
 
97
91
  readonly #eventBus: EventBusNamespace;
98
92
 
@@ -134,17 +128,14 @@ export class Router<
134
128
 
135
129
  // Conditional validation for dependencies
136
130
  if (!noValidate) {
137
- DependenciesNamespace.validateDependenciesObject(
138
- dependencies,
139
- "constructor",
140
- );
131
+ validateDependenciesObject(dependencies, "constructor");
141
132
  }
142
133
 
143
134
  // Conditional validation for initial routes - structure and batch duplicates
144
135
  // Validation happens BEFORE tree is built, so tree is not passed
145
136
  if (!noValidate && routes.length > 0) {
146
- RoutesNamespace.validateAddRouteArgs(routes);
147
- RoutesNamespace.validateRoutes(routes);
137
+ validateAddRouteArgs(routes);
138
+ validateRoutes(routes);
148
139
  }
149
140
 
150
141
  // =========================================================================
@@ -153,7 +144,8 @@ export class Router<
153
144
 
154
145
  this.#options = new OptionsNamespace(options);
155
146
  this.#limits = createLimits(options.limits);
156
- this.#dependencies = new DependenciesNamespace<Dependencies>(dependencies);
147
+ this.#dependenciesStore =
148
+ createDependenciesStore<Dependencies>(dependencies);
157
149
  this.#state = new StateNamespace();
158
150
  this.#routes = new RoutesNamespace<Dependencies>(
159
151
  routes,
@@ -164,7 +156,6 @@ export class Router<
164
156
  this.#plugins = new PluginsNamespace<Dependencies>();
165
157
  this.#navigation = new NavigationNamespace();
166
158
  this.#lifecycle = new RouterLifecycleNamespace();
167
- this.#clone = new CloneNamespace<Dependencies>();
168
159
  this.#noValidate = noValidate;
169
160
 
170
161
  // =========================================================================
@@ -172,6 +163,7 @@ export class Router<
172
163
  // =========================================================================
173
164
 
174
165
  const routerFSM = createRouterFSM();
166
+ // eslint-disable-next-line unicorn/prefer-event-target
175
167
  const emitter = new EventEmitter<RouterEventMap>({
176
168
  onListenerError: (eventName, error) => {
177
169
  logger.error("Router", `Error in listener for ${eventName}:`, error);
@@ -195,55 +187,81 @@ export class Router<
195
187
  router: this,
196
188
  options: this.#options,
197
189
  limits: this.#limits,
198
- dependencies: this.#dependencies,
190
+ dependenciesStore: this.#dependenciesStore,
199
191
  state: this.#state,
200
192
  routes: this.#routes,
201
193
  routeLifecycle: this.#routeLifecycle,
202
194
  plugins: this.#plugins,
203
195
  navigation: this.#navigation,
204
196
  lifecycle: this.#lifecycle,
205
- clone: this.#clone,
206
197
  eventBus: this.#eventBus,
207
198
  }),
208
199
  );
209
200
 
201
+ // =========================================================================
202
+ // Register Internals (WeakMap for plugin/infrastructure access)
203
+ // =========================================================================
204
+
205
+ registerInternals(this, {
206
+ makeState: (name, params, path, meta, forceId) =>
207
+ this.#state.makeState(name, params, path, meta, forceId),
208
+ forwardState: (name, params) => this.#routes.forwardState(name, params),
209
+ buildStateResolved: (name, params) =>
210
+ this.#routes.buildStateResolved(name, params),
211
+ matchPath: (path, matchOptions) =>
212
+ this.#routes.matchPath(path, matchOptions),
213
+ getOptions: () => this.#options.get(),
214
+ navigateToState: (toState, fromState, opts) =>
215
+ this.#navigation.navigateToState(toState, fromState, opts),
216
+ addEventListener: (eventName, cb) =>
217
+ this.#eventBus.addEventListener(eventName, cb),
218
+ buildPath: (route, params) =>
219
+ this.#routes.buildPath(route, params, this.#options.get()),
220
+ setRootPath: (rootPath) => {
221
+ this.#routes.setRootPath(rootPath);
222
+ },
223
+ getRootPath: () => this.#routes.getStore().rootPath,
224
+ getTree: () => this.#routes.getStore().tree,
225
+ isDisposed: () => this.#eventBus.isDisposed(),
226
+ noValidate,
227
+ // Dependencies (issue #172)
228
+ dependenciesGetStore: () => this.#dependenciesStore,
229
+ // Clone support (issue #173)
230
+ cloneOptions: () => ({ ...this.#options.get() }),
231
+ cloneDependencies: () =>
232
+ ({ ...this.#dependenciesStore.dependencies }) as Record<
233
+ string,
234
+ unknown
235
+ >,
236
+ getLifecycleFactories: () => this.#routeLifecycle.getFactories(),
237
+ getPluginFactories: () => this.#plugins.getAll(),
238
+ routeGetStore: () => this.#routes.getStore(),
239
+ // Cross-namespace state (issue #174)
240
+ getStateName: () => this.#state.get()?.name,
241
+ isTransitioning: () => this.#eventBus.isTransitioning(),
242
+ clearState: () => {
243
+ this.#state.set(undefined);
244
+ },
245
+ });
246
+
210
247
  // =========================================================================
211
248
  // Bind Public Methods
212
249
  // =========================================================================
213
250
  // All public methods that access private fields must be bound to preserve
214
251
  // `this` context when methods are extracted as references.
215
- // See: https://github.com/nicolo-ribaudo/tc39-proposal-bind-operator
252
+ // See: https://github.com/tc39/proposal-bind-operator
216
253
  // =========================================================================
217
254
 
218
- // Route Management
219
- this.addRoute = this.addRoute.bind(this);
220
- this.removeRoute = this.removeRoute.bind(this);
221
- this.clearRoutes = this.clearRoutes.bind(this);
222
- this.getRoute = this.getRoute.bind(this);
223
- this.getRouteConfig = this.getRouteConfig.bind(this);
224
- this.hasRoute = this.hasRoute.bind(this);
225
- this.updateRoute = this.updateRoute.bind(this);
226
-
227
255
  // Path & State Building
228
256
  this.isActiveRoute = this.isActiveRoute.bind(this);
229
257
  this.buildPath = this.buildPath.bind(this);
230
- this.matchPath = this.matchPath.bind(this);
231
- this.setRootPath = this.setRootPath.bind(this);
232
- this.getRootPath = this.getRootPath.bind(this);
233
258
 
234
259
  // State Management
235
- this.makeState = this.makeState.bind(this);
236
260
  this.getState = this.getState.bind(this);
237
261
  this.getPreviousState = this.getPreviousState.bind(this);
238
262
  this.areStatesEqual = this.areStatesEqual.bind(this);
239
- this.forwardState = this.forwardState.bind(this);
240
- this.buildState = this.buildState.bind(this);
241
- this.buildNavigationState = this.buildNavigationState.bind(this);
242
263
  this.shouldUpdateNode = this.shouldUpdateNode.bind(this);
243
264
 
244
- // Options
245
- this.getOptions = this.getOptions.bind(this);
246
-
247
265
  // Router Lifecycle
248
266
  this.isActive = this.isActive.bind(this);
249
267
  this.start = this.start.bind(this);
@@ -251,216 +269,17 @@ export class Router<
251
269
  this.dispose = this.dispose.bind(this);
252
270
 
253
271
  // Route Lifecycle (Guards)
254
- this.addActivateGuard = this.addActivateGuard.bind(this);
255
- this.addDeactivateGuard = this.addDeactivateGuard.bind(this);
256
- this.removeActivateGuard = this.removeActivateGuard.bind(this);
257
- this.removeDeactivateGuard = this.removeDeactivateGuard.bind(this);
258
272
  this.canNavigateTo = this.canNavigateTo.bind(this);
259
273
 
260
274
  // Plugins
261
275
  this.usePlugin = this.usePlugin.bind(this);
262
276
 
263
- // Dependencies
264
- this.setDependency = this.setDependency.bind(this);
265
- this.setDependencies = this.setDependencies.bind(this);
266
- this.getDependency = this.getDependency.bind(this);
267
- this.getDependencies = this.getDependencies.bind(this);
268
- this.removeDependency = this.removeDependency.bind(this);
269
- this.hasDependency = this.hasDependency.bind(this);
270
- this.resetDependencies = this.resetDependencies.bind(this);
271
-
272
- // Events
273
- this.addEventListener = this.addEventListener.bind(this);
274
-
275
277
  // Navigation
276
278
  this.navigate = this.navigate.bind(this);
277
279
  this.navigateToDefault = this.navigateToDefault.bind(this);
278
- this.navigateToState = this.navigateToState.bind(this);
279
280
 
280
281
  // Subscription
281
282
  this.subscribe = this.subscribe.bind(this);
282
-
283
- // Cloning
284
- this.clone = this.clone.bind(this);
285
- }
286
-
287
- // ============================================================================
288
- // Route Management
289
- // ============================================================================
290
-
291
- addRoute(
292
- routes: Route<Dependencies>[] | Route<Dependencies>,
293
- options?: { parent?: string },
294
- ): this {
295
- const routeArray = Array.isArray(routes) ? routes : [routes];
296
- const parentName = options?.parent;
297
-
298
- if (!this.#noValidate) {
299
- // 1. Validate parent option format
300
- if (parentName !== undefined) {
301
- RoutesNamespace.validateParentOption(parentName);
302
- }
303
-
304
- // 2. Static validation (route structure and properties)
305
- RoutesNamespace.validateAddRouteArgs(routeArray);
306
-
307
- // 3. State-dependent validation (parent exists, duplicates, forwardTo)
308
- RoutesNamespace.validateRoutes(
309
- routeArray,
310
- this.#routes.getTree(),
311
- this.#routes.getForwardRecord(),
312
- parentName,
313
- );
314
- }
315
-
316
- // 4. Execute (add definitions, register handlers, rebuild tree)
317
- this.#routes.addRoutes(routeArray, parentName);
318
-
319
- return this;
320
- }
321
-
322
- removeRoute(name: string): this {
323
- // Static validation
324
- if (!this.#noValidate) {
325
- RoutesNamespace.validateRemoveRouteArgs(name);
326
- }
327
-
328
- // Instance validation (checks active route, navigation state)
329
- const canRemove = this.#routes.validateRemoveRoute(
330
- name,
331
- this.#state.get()?.name,
332
- this.#eventBus.isTransitioning(),
333
- );
334
-
335
- if (!canRemove) {
336
- return this;
337
- }
338
-
339
- // Perform removal
340
- const wasRemoved = this.#routes.removeRoute(name);
341
-
342
- if (!wasRemoved) {
343
- logger.warn(
344
- "router.removeRoute",
345
- `Route "${name}" not found. No changes made.`,
346
- );
347
- }
348
-
349
- return this;
350
- }
351
-
352
- clearRoutes(): this {
353
- const isNavigating = this.#eventBus.isTransitioning();
354
-
355
- // Validate operation can proceed
356
- const canClear = this.#routes.validateClearRoutes(isNavigating);
357
-
358
- if (!canClear) {
359
- return this;
360
- }
361
-
362
- // Clear routes config (definitions, decoders, encoders, defaultParams, forwardMap)
363
- this.#routes.clearRoutes();
364
-
365
- // Clear all lifecycle handlers
366
- this.#routeLifecycle.clearAll();
367
-
368
- // Clear router state since all routes are removed
369
- this.#state.set(undefined);
370
-
371
- return this;
372
- }
373
-
374
- getRoute(name: string): Route<Dependencies> | undefined {
375
- if (!this.#noValidate) {
376
- validateRouteName(name, "getRoute");
377
- }
378
-
379
- return this.#routes.getRoute(name);
380
- }
381
-
382
- getRouteConfig(name: string): Record<string, unknown> | undefined {
383
- return this.#routes.getRouteConfig(name);
384
- }
385
-
386
- hasRoute(name: string): boolean {
387
- if (!this.#noValidate) {
388
- validateRouteName(name, "hasRoute");
389
- }
390
-
391
- return this.#routes.hasRoute(name);
392
- }
393
-
394
- updateRoute(name: string, updates: RouteConfigUpdate<Dependencies>): this {
395
- // Validate name and updates object structure (basic checks only)
396
- if (!this.#noValidate) {
397
- RoutesNamespace.validateUpdateRouteBasicArgs(name, updates);
398
- }
399
-
400
- // Cache all property values upfront to protect against mutating getters.
401
- // This ensures consistent behavior regardless of getter side effects.
402
- // Must happen AFTER basic validation but BEFORE property type validation.
403
- const {
404
- forwardTo,
405
- defaultParams,
406
- decodeParams,
407
- encodeParams,
408
- canActivate,
409
- canDeactivate,
410
- } = updates;
411
-
412
- // Validate cached property values
413
- if (!this.#noValidate) {
414
- RoutesNamespace.validateUpdateRoutePropertyTypes(
415
- forwardTo,
416
- defaultParams,
417
- decodeParams,
418
- encodeParams,
419
- );
420
- }
421
-
422
- // Warn if navigation is in progress
423
- if (this.#eventBus.isTransitioning()) {
424
- logger.error(
425
- "router.updateRoute",
426
- `Updating route "${name}" while navigation is in progress. This may cause unexpected behavior.`,
427
- );
428
- }
429
-
430
- // Instance validation (route existence, forwardTo checks) - use cached values
431
- if (!this.#noValidate) {
432
- this.#routes.validateUpdateRoute(name, forwardTo);
433
- }
434
-
435
- // Update route config
436
- this.#routes.updateRouteConfig(name, {
437
- forwardTo,
438
- defaultParams,
439
- decodeParams,
440
- encodeParams,
441
- });
442
-
443
- // Handle canActivate separately (uses RouteLifecycleNamespace)
444
- // Use facade method for proper validation
445
- if (canActivate !== undefined) {
446
- if (canActivate === null) {
447
- this.#routeLifecycle.clearCanActivate(name);
448
- } else {
449
- this.addActivateGuard(name, canActivate);
450
- }
451
- }
452
-
453
- // Handle canDeactivate separately (uses RouteLifecycleNamespace)
454
- // Use facade method for proper validation
455
- if (canDeactivate !== undefined) {
456
- if (canDeactivate === null) {
457
- this.#routeLifecycle.clearCanDeactivate(name);
458
- } else {
459
- this.addDeactivateGuard(name, canDeactivate);
460
- }
461
- }
462
-
463
- return this;
464
283
  }
465
284
 
466
285
  // ============================================================================
@@ -474,7 +293,7 @@ export class Router<
474
293
  ignoreQueryParams?: boolean,
475
294
  ): boolean {
476
295
  if (!this.#noValidate) {
477
- RoutesNamespace.validateIsActiveRouteArgs(
296
+ validateIsActiveRouteArgs(
478
297
  name,
479
298
  params,
480
299
  strictEquality,
@@ -502,52 +321,16 @@ export class Router<
502
321
 
503
322
  buildPath(route: string, params?: Params): string {
504
323
  if (!this.#noValidate) {
505
- RoutesNamespace.validateBuildPathArgs(route);
324
+ validateBuildPathArgs(route);
506
325
  }
507
326
 
508
327
  return this.#routes.buildPath(route, params, this.#options.get());
509
328
  }
510
329
 
511
- matchPath<P extends Params = Params, MP extends Params = Params>(
512
- path: string,
513
- ): State<P, MP> | undefined {
514
- if (!this.#noValidate) {
515
- RoutesNamespace.validateMatchPathArgs(path);
516
- }
517
-
518
- return this.#routes.matchPath<P, MP>(path, this.#options.get());
519
- }
520
-
521
- setRootPath(rootPath: string): void {
522
- if (!this.#noValidate) {
523
- RoutesNamespace.validateSetRootPathArgs(rootPath);
524
- }
525
-
526
- this.#routes.setRootPath(rootPath);
527
- }
528
-
529
- getRootPath(): string {
530
- return this.#routes.getRootPath();
531
- }
532
-
533
330
  // ============================================================================
534
331
  // State Management (delegated to StateNamespace)
535
332
  // ============================================================================
536
333
 
537
- makeState<P extends Params = Params, MP extends Params = Params>(
538
- name: string,
539
- params?: P,
540
- path?: string,
541
- meta?: StateMetaInput<MP>,
542
- forceId?: number,
543
- ): State<P, MP> {
544
- if (!this.#noValidate) {
545
- StateNamespace.validateMakeStateArgs(name, params, path, forceId);
546
- }
547
-
548
- return this.#state.makeState<P, MP>(name, params, path, meta, forceId);
549
- }
550
-
551
334
  getState<P extends Params = Params, MP extends Params = Params>():
552
335
  | State<P, MP>
553
336
  | undefined {
@@ -574,81 +357,14 @@ export class Router<
574
357
  return this.#state.areStatesEqual(state1, state2, ignoreQueryParams);
575
358
  }
576
359
 
577
- forwardState<P extends Params = Params>(
578
- routeName: string,
579
- routeParams: P,
580
- ): SimpleState<P> {
581
- if (!this.#noValidate) {
582
- RoutesNamespace.validateStateBuilderArgs(
583
- routeName,
584
- routeParams,
585
- "forwardState",
586
- );
587
- }
588
-
589
- return this.#routes.forwardState<P>(routeName, routeParams);
590
- }
591
-
592
- buildState(
593
- routeName: string,
594
- routeParams: Params,
595
- ): RouteTreeState | undefined {
596
- if (!this.#noValidate) {
597
- RoutesNamespace.validateStateBuilderArgs(
598
- routeName,
599
- routeParams,
600
- "buildState",
601
- );
602
- }
603
-
604
- // Call forwardState at facade level to allow plugin interception
605
- const { name, params } = this.forwardState(routeName, routeParams);
606
-
607
- return this.#routes.buildStateResolved(name, params);
608
- }
609
-
610
- buildNavigationState(name: string, params: Params = {}): State | undefined {
611
- if (!this.#noValidate) {
612
- RoutesNamespace.validateStateBuilderArgs(
613
- name,
614
- params,
615
- "buildNavigationState",
616
- );
617
- }
618
-
619
- const routeInfo = this.buildState(name, params);
620
-
621
- if (!routeInfo) {
622
- return undefined;
623
- }
624
-
625
- return this.makeState(
626
- routeInfo.name,
627
- routeInfo.params,
628
- this.buildPath(routeInfo.name, routeInfo.params),
629
- {
630
- params: routeInfo.meta,
631
- options: {},
632
- },
633
- );
634
- }
635
-
636
360
  shouldUpdateNode(
637
361
  nodeName: string,
638
362
  ): (toState: State, fromState?: State) => boolean {
639
363
  if (!this.#noValidate) {
640
- RoutesNamespace.validateShouldUpdateNodeArgs(nodeName);
364
+ validateShouldUpdateNodeArgs(nodeName);
641
365
  }
642
366
 
643
- return this.#routes.shouldUpdateNode(nodeName);
644
- }
645
-
646
- // ============================================================================
647
- // Options (backed by OptionsNamespace)
648
- // ============================================================================
649
-
650
- getOptions(): Options {
651
- return this.#options.get();
367
+ return RoutesNamespace.shouldUpdateNode(nodeName);
652
368
  }
653
369
 
654
370
  // ============================================================================
@@ -684,6 +400,7 @@ export class Router<
684
400
  }
685
401
 
686
402
  stop(): this {
403
+ this.#navigation.abortCurrentNavigation();
687
404
  this.#eventBus.cancelTransitionIfRunning(this.#state.get());
688
405
 
689
406
  if (!this.#eventBus.isReady() && !this.#eventBus.isTransitioning()) {
@@ -701,6 +418,7 @@ export class Router<
701
418
  return;
702
419
  }
703
420
 
421
+ this.#navigation.abortCurrentNavigation();
704
422
  this.#eventBus.cancelTransitionIfRunning(this.#state.get());
705
423
 
706
424
  if (this.#eventBus.isReady() || this.#eventBus.isTransitioning()) {
@@ -715,7 +433,9 @@ export class Router<
715
433
  this.#routes.clearRoutes();
716
434
  this.#routeLifecycle.clearAll();
717
435
  this.#state.reset();
718
- this.#dependencies.reset();
436
+ this.#dependenciesStore.dependencies = Object.create(
437
+ null,
438
+ ) as Partial<Dependencies>;
719
439
 
720
440
  this.#markDisposed();
721
441
  }
@@ -724,79 +444,22 @@ export class Router<
724
444
  // Route Lifecycle (Guards)
725
445
  // ============================================================================
726
446
 
727
- addDeactivateGuard(
728
- name: string,
729
- canDeactivateHandler: GuardFnFactory<Dependencies> | boolean,
730
- ): this {
731
- if (!this.#noValidate) {
732
- validateRouteName(name, "addDeactivateGuard");
733
- RouteLifecycleNamespace.validateHandler(
734
- canDeactivateHandler,
735
- "addDeactivateGuard",
736
- );
737
- }
738
-
739
- this.#routeLifecycle.addCanDeactivate(
740
- name,
741
- canDeactivateHandler,
742
- this.#noValidate,
743
- );
744
-
745
- return this;
746
- }
747
-
748
- addActivateGuard(
749
- name: string,
750
- canActivateHandler: GuardFnFactory<Dependencies> | boolean,
751
- ): this {
752
- if (!this.#noValidate) {
753
- validateRouteName(name, "addActivateGuard");
754
- RouteLifecycleNamespace.validateHandler(
755
- canActivateHandler,
756
- "addActivateGuard",
757
- );
758
- }
759
-
760
- this.#routeLifecycle.addCanActivate(
761
- name,
762
- canActivateHandler,
763
- this.#noValidate,
764
- );
765
-
766
- return this;
767
- }
768
-
769
- removeActivateGuard(name: string): void {
770
- if (!this.#noValidate) {
771
- validateRouteName(name, "removeActivateGuard");
772
- }
773
-
774
- this.#routeLifecycle.clearCanActivate(name);
775
- }
776
-
777
- removeDeactivateGuard(name: string): void {
778
- if (!this.#noValidate) {
779
- validateRouteName(name, "removeDeactivateGuard");
780
- }
781
-
782
- this.#routeLifecycle.clearCanDeactivate(name);
783
- }
784
-
785
447
  canNavigateTo(name: string, params?: Params): boolean {
786
448
  if (!this.#noValidate) {
787
449
  validateRouteName(name, "canNavigateTo");
788
450
  }
789
451
 
790
- if (!this.hasRoute(name)) {
452
+ if (!this.#routes.hasRoute(name)) {
791
453
  return false;
792
454
  }
793
455
 
794
- const { name: resolvedName, params: resolvedParams } = this.forwardState(
456
+ const ctx = getInternals(this);
457
+ const { name: resolvedName, params: resolvedParams } = ctx.forwardState(
795
458
  name,
796
459
  params ?? {},
797
460
  );
798
- const toState = this.makeState(resolvedName, resolvedParams);
799
- const fromState = this.getState();
461
+ const toState = this.#state.makeState(resolvedName, resolvedParams);
462
+ const fromState = this.#state.get();
800
463
 
801
464
  const { toDeactivate, toActivate } = getTransitionPath(toState, fromState);
802
465
 
@@ -854,96 +517,6 @@ export class Router<
854
517
  return this.#plugins.use(...plugins);
855
518
  }
856
519
 
857
- // ============================================================================
858
- // Dependencies (backed by DependenciesNamespace)
859
- // ============================================================================
860
-
861
- setDependency<K extends keyof Dependencies & string>(
862
- dependencyName: K,
863
- dependency: Dependencies[K],
864
- ): this {
865
- if (!this.#noValidate) {
866
- DependenciesNamespace.validateSetDependencyArgs(dependencyName);
867
- }
868
-
869
- this.#dependencies.set(dependencyName, dependency);
870
-
871
- return this;
872
- }
873
-
874
- setDependencies(deps: Dependencies): this {
875
- if (!this.#noValidate) {
876
- DependenciesNamespace.validateDependenciesObject(deps, "setDependencies");
877
- DependenciesNamespace.validateDependencyLimit(
878
- this.#dependencies.count(),
879
- Object.keys(deps).length,
880
- "setDependencies",
881
- this.#limits.maxDependencies,
882
- );
883
- }
884
-
885
- this.#dependencies.setMultiple(deps);
886
-
887
- return this;
888
- }
889
-
890
- getDependency<K extends keyof Dependencies>(key: K): Dependencies[K] {
891
- if (!this.#noValidate) {
892
- DependenciesNamespace.validateName(key, "getDependency");
893
- }
894
-
895
- const value = this.#dependencies.get(key);
896
-
897
- if (!this.#noValidate) {
898
- DependenciesNamespace.validateDependencyExists(value, key as string);
899
- }
900
-
901
- return value;
902
- }
903
-
904
- getDependencies(): Partial<Dependencies> {
905
- return this.#dependencies.getAll();
906
- }
907
-
908
- removeDependency(dependencyName: keyof Dependencies): this {
909
- if (!this.#noValidate) {
910
- DependenciesNamespace.validateName(dependencyName, "removeDependency");
911
- }
912
-
913
- this.#dependencies.remove(dependencyName);
914
-
915
- return this;
916
- }
917
-
918
- hasDependency(dependencyName: keyof Dependencies): boolean {
919
- if (!this.#noValidate) {
920
- DependenciesNamespace.validateName(dependencyName, "hasDependency");
921
- }
922
-
923
- return this.#dependencies.has(dependencyName);
924
- }
925
-
926
- resetDependencies(): this {
927
- this.#dependencies.reset();
928
-
929
- return this;
930
- }
931
-
932
- // ============================================================================
933
- // Events (backed by EventEmitter)
934
- // ============================================================================
935
-
936
- addEventListener<E extends EventName>(
937
- eventName: E,
938
- cb: Plugin[EventMethodMap[E]],
939
- ): Unsubscribe {
940
- if (!this.#noValidate) {
941
- EventBusNamespace.validateListenerArgs(eventName, cb);
942
- }
943
-
944
- return this.#eventBus.addEventListener(eventName, cb);
945
- }
946
-
947
520
  // ============================================================================
948
521
  // Subscription (backed by EventEmitter)
949
522
  // ============================================================================
@@ -960,13 +533,6 @@ export class Router<
960
533
  // Navigation
961
534
  // ============================================================================
962
535
 
963
- navigate(routeName: string): Promise<State>;
964
- navigate(routeName: string, routeParams: Params): Promise<State>;
965
- navigate(
966
- routeName: string,
967
- routeParams: Params,
968
- options: NavigationOptions,
969
- ): Promise<State>;
970
536
  navigate(
971
537
  routeName: string,
972
538
  routeParams?: Params,
@@ -996,8 +562,6 @@ export class Router<
996
562
  return promiseState;
997
563
  }
998
564
 
999
- navigateToDefault(): Promise<State>;
1000
- navigateToDefault(options: NavigationOptions): Promise<State>;
1001
565
  navigateToDefault(options?: NavigationOptions): Promise<State> {
1002
566
  // 1. Validate arguments
1003
567
  if (!this.#noValidate) {
@@ -1019,43 +583,6 @@ export class Router<
1019
583
  return promiseState;
1020
584
  }
1021
585
 
1022
- navigateToState(
1023
- toState: State,
1024
- fromState: State | undefined,
1025
- opts: NavigationOptions,
1026
- ): Promise<State> {
1027
- if (!this.#noValidate) {
1028
- NavigationNamespace.validateNavigateToStateArgs(toState, fromState, opts);
1029
- }
1030
-
1031
- return this.#navigation.navigateToState(toState, fromState, opts);
1032
- }
1033
-
1034
- // ============================================================================
1035
- // Cloning
1036
- // ============================================================================
1037
-
1038
- clone(dependencies?: Dependencies): Router<Dependencies> {
1039
- if (!this.#noValidate) {
1040
- CloneNamespace.validateCloneArgs(dependencies);
1041
- }
1042
-
1043
- return this.#clone.clone(
1044
- dependencies,
1045
- (routes, options, deps) =>
1046
- new Router<Dependencies>(routes, options, deps),
1047
- (newRouter, config, resolvedForwardMap, routeCustomFields) => {
1048
- const typedRouter = newRouter as unknown as Router<Dependencies>;
1049
-
1050
- typedRouter.#routes.applyClonedConfig(
1051
- config,
1052
- resolvedForwardMap,
1053
- routeCustomFields,
1054
- );
1055
- },
1056
- );
1057
- }
1058
-
1059
586
  /**
1060
587
  * Pre-allocated callback for #suppressUnhandledRejection.
1061
588
  * Avoids creating a new closure on every navigate() call.
@@ -1086,26 +613,11 @@ export class Router<
1086
613
  #markDisposed(): void {
1087
614
  this.navigate = throwDisposed as never;
1088
615
  this.navigateToDefault = throwDisposed as never;
1089
- this.navigateToState = throwDisposed as never;
1090
616
  this.start = throwDisposed as never;
1091
617
  this.stop = throwDisposed as never;
1092
- this.addRoute = throwDisposed as never;
1093
- this.removeRoute = throwDisposed as never;
1094
- this.clearRoutes = throwDisposed as never;
1095
- this.updateRoute = throwDisposed as never;
1096
- this.addActivateGuard = throwDisposed as never;
1097
- this.addDeactivateGuard = throwDisposed as never;
1098
- this.removeActivateGuard = throwDisposed as never;
1099
- this.removeDeactivateGuard = throwDisposed as never;
1100
618
  this.usePlugin = throwDisposed as never;
1101
- this.setDependency = throwDisposed as never;
1102
- this.setDependencies = throwDisposed as never;
1103
- this.removeDependency = throwDisposed as never;
1104
- this.resetDependencies = throwDisposed as never;
1105
- this.addEventListener = throwDisposed as never;
619
+
1106
620
  this.subscribe = throwDisposed as never;
1107
- this.setRootPath = throwDisposed as never;
1108
- this.clone = throwDisposed as never;
1109
621
  this.canNavigateTo = throwDisposed as never;
1110
622
  }
1111
623
  }