@real-router/types 0.14.0 → 0.16.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.
@@ -44,7 +44,7 @@ interface SimpleState<P extends Params = Params> {
44
44
  name: string;
45
45
  params: P;
46
46
  }
47
- type TransitionPhase = "deactivating" | "activating" | "middleware";
47
+ type TransitionPhase = "deactivating" | "activating";
48
48
  type TransitionReason = "success" | "blocked" | "cancelled" | "error";
49
49
  interface TransitionMeta {
50
50
  phase: TransitionPhase;
@@ -103,7 +103,7 @@ interface RouterError extends Error {
103
103
  *
104
104
  * All options are optional and have sensible defaults. Options can be combined to achieve
105
105
  * complex navigation behaviors. The options object is stored in state.meta.options and is
106
- * available to middleware, guards, and event listeners.
106
+ * available to guards and event listeners.
107
107
  *
108
108
  * @see {@link Router.navigate} for navigation method that accepts these options
109
109
  * @see {@link State.meta} for where options are stored after navigation
@@ -137,11 +137,11 @@ interface NavigationOptions {
137
137
  *
138
138
  * Without `reload`:
139
139
  * - Navigation to current route throws SAME_STATES error
140
- * - No lifecycle hooks or middleware execute
140
+ * - No lifecycle hooks execute
141
141
  * - No events are fired
142
142
  *
143
143
  * With `reload`:
144
- * - Full transition executes (deactivate → activate → middleware)
144
+ * - Full transition executes (deactivate → activate)
145
145
  * - All lifecycle hooks run again
146
146
  * - TRANSITION_SUCCESS event fires with same state
147
147
  * - State object is recreated (new reference)
@@ -192,16 +192,16 @@ interface NavigationOptions {
192
192
  *
193
193
  * @description
194
194
  * When `true`, bypasses only the canDeactivate lifecycle hooks for segments being
195
- * deactivated. canActivate guards and middleware still execute normally. This allows
195
+ * deactivated. canActivate guards still execute normally. This allows
196
196
  * forcing navigation away from routes with confirmation dialogs or unsaved changes.
197
197
  *
198
198
  * Skipped vs executed:
199
199
  * ```
200
200
  * // Normal transition
201
- * deactivate(fromSegments) → activate(toSegments) → middleware → success
201
+ * deactivate(fromSegments) → activate(toSegments) → success
202
202
  *
203
203
  * // With forceDeactivate: true
204
- * [skip deactivate] → activate(toSegments) → middleware → success
204
+ * [skip deactivate] → activate(toSegments) → success
205
205
  * ```
206
206
  *
207
207
  * ⚠️ Data loss risk: Bypassing canDeactivate means unsaved changes will be lost
@@ -227,21 +227,12 @@ interface NavigationOptions {
227
227
  *
228
228
  * @description
229
229
  * Automatically set by the router when a navigation is triggered by a redirect from
230
- * middleware or lifecycle hooks. This flag is used internally to track redirect chains
230
+ * guards or lifecycle hooks. This flag is used internally to track redirect chains
231
231
  * and is stored in state.meta.options.redirected.
232
232
  *
233
233
  * @default false (auto-set by router during redirects)
234
234
  *
235
235
  * @example
236
- * // Middleware triggers automatic redirect
237
- * router.useMiddleware((toState, fromState, opts) => {
238
- * if (!isAuthenticated && toState.name !== 'login') {
239
- * // Router will automatically set redirected: true
240
- * return { name: 'login', params: { next: toState.path } };
241
- * }
242
- * });
243
- *
244
- * @example
245
236
  * // Accessing redirect flag in lifecycle
246
237
  * router.addActivateGuard('dashboard', (toState, fromState) => {
247
238
  * if (toState.meta?.options?.redirected) {
@@ -278,13 +269,6 @@ interface LimitsConfig {
278
269
  * @default 50
279
270
  */
280
271
  maxPlugins: number;
281
- /**
282
- * Maximum number of middleware functions in the navigation pipeline.
283
- * Controls middleware chain length to prevent stack overflow and performance issues.
284
- *
285
- * @default 50
286
- */
287
- maxMiddleware: number;
288
272
  /**
289
273
  * Maximum number of event listeners per event type.
290
274
  * Prevents memory leaks from excessive listener registration.
@@ -316,11 +300,11 @@ interface LimitsConfig {
316
300
  }
317
301
 
318
302
  /**
319
- * Base router types without Router class dependency.
303
+ * Router types and interfaces.
320
304
  *
321
- * Router-dependent types (Route, RouteConfigUpdate, ActivationFnFactory,
322
- * MiddlewareFactory, PluginFactory) are defined in @real-router/core
323
- * to avoid circular dependencies.
305
+ * Factory types (PluginFactory, GuardFnFactory) and
306
+ * route config types (Route, RouteConfigUpdate) use self-referencing Router<D>
307
+ * within this single file to resolve circular dependencies.
324
308
  */
325
309
 
326
310
  type LogLevel = "log" | "warn" | "error";
@@ -432,7 +416,6 @@ interface Options {
432
416
  */
433
417
  noValidate?: boolean;
434
418
  }
435
- type ActivationFn = (toState: State, fromState: State | undefined) => boolean | Promise<boolean | State | void> | State | void;
436
419
  type GuardFn = (toState: State, fromState: State | undefined) => boolean | Promise<boolean>;
437
420
  type DefaultDependencies = object;
438
421
  interface Config {
@@ -450,7 +433,6 @@ interface Plugin {
450
433
  onTransitionSuccess?: (toState: State, fromState: State | undefined, opts: NavigationOptions) => void;
451
434
  teardown?: () => void;
452
435
  }
453
- type Middleware = ActivationFn;
454
436
  interface SubscribeState {
455
437
  route: State;
456
438
  previousRoute?: State | undefined;
@@ -482,64 +464,94 @@ interface Navigator {
482
464
  subscribe: (listener: SubscribeFn) => Unsubscribe;
483
465
  }
484
466
  /**
485
- * Router interface - public API for route navigation and lifecycle management.
467
+ * Router interface full public API for route navigation and lifecycle management.
486
468
  *
487
- * Defines the contract for router implementations. The actual Router class in
488
- * @real-router/core implements this interface with full functionality.
489
- *
490
- * This interface uses `GuardFn | boolean` for guard types to avoid circular
491
- * dependencies. The concrete Router class in @real-router/core narrows this to
492
- * `GuardFnFactory | boolean` for more precise type checking.
469
+ * Generic parameter D constrains dependency injection types.
470
+ * Factory types (PluginFactory, GuardFnFactory) self-reference this interface
471
+ * within the same file to avoid circular dependencies.
493
472
  */
494
- interface Router {
495
- /**
496
- * Register an activation guard for a route.
497
- *
498
- * @param name - Route name
499
- * @param guard - Guard function or boolean
500
- * @returns this for method chaining
501
- */
502
- addActivateGuard: (name: string, guard: GuardFn | boolean) => this;
503
- /**
504
- * Register a deactivation guard for a route.
505
- *
506
- * @param name - Route name
507
- * @param guard - Guard function or boolean
508
- * @returns this for method chaining
509
- */
510
- addDeactivateGuard: (name: string, guard: GuardFn | boolean) => this;
511
- /**
512
- * Remove an activation guard from a route.
513
- *
514
- * @param name - Route name
515
- */
516
- removeActivateGuard: (name: string) => void;
517
- /**
518
- * Remove a deactivation guard from a route.
519
- *
520
- * @param name - Route name
521
- */
522
- removeDeactivateGuard: (name: string) => void;
473
+ interface Router<D extends DefaultDependencies = DefaultDependencies> {
474
+ [key: string]: unknown;
475
+ isActiveRoute: (name: string, params?: Params, strictEquality?: boolean, ignoreQueryParams?: boolean) => boolean;
476
+ buildPath: (route: string, params?: Params) => string;
477
+ getState: <P extends Params = Params, MP extends Params = Params>() => State<P, MP> | undefined;
478
+ getPreviousState: () => State | undefined;
479
+ areStatesEqual: (state1: State | undefined, state2: State | undefined, ignoreQueryParams?: boolean) => boolean;
480
+ shouldUpdateNode: (nodeName: string) => (toState: State, fromState?: State) => boolean;
481
+ isActive: () => boolean;
482
+ start: (startPath: string) => Promise<State>;
483
+ stop: () => this;
484
+ dispose: () => void;
485
+ canNavigateTo: (name: string, params?: Params) => boolean;
486
+ usePlugin: (...plugins: PluginFactory<D>[]) => Unsubscribe;
487
+ subscribe: (listener: SubscribeFn) => Unsubscribe;
488
+ navigate: (routeName: string, routeParams?: Params, options?: NavigationOptions) => Promise<State>;
489
+ navigateToDefault: (options?: NavigationOptions) => Promise<State>;
490
+ }
491
+ /**
492
+ * Factory function for creating plugins.
493
+ * Receives the router instance and a dependency getter.
494
+ */
495
+ type PluginFactory<Dependencies extends DefaultDependencies = DefaultDependencies> = (router: Router<Dependencies>, getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K]) => Plugin;
496
+ /**
497
+ * Factory function for creating guards.
498
+ * Receives the router instance and a dependency getter.
499
+ */
500
+ type GuardFnFactory<Dependencies extends DefaultDependencies = DefaultDependencies> = (router: Router<Dependencies>, getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K]) => GuardFn;
501
+ /**
502
+ * Route configuration.
503
+ */
504
+ interface Route<Dependencies extends DefaultDependencies = DefaultDependencies> {
505
+ [key: string]: unknown;
506
+ /** Route name (dot-separated for nested routes). */
507
+ name: string;
508
+ /** URL path pattern for this route. */
509
+ path: string;
510
+ /** Factory function that returns a guard for route activation. */
511
+ canActivate?: GuardFnFactory<Dependencies>;
512
+ /** Factory function that returns a guard for route deactivation. */
513
+ canDeactivate?: GuardFnFactory<Dependencies>;
523
514
  /**
524
- * Check if navigation to a route is allowed without performing actual navigation.
515
+ * Redirects navigation to another route.
525
516
  *
526
- * Synchronously checks all activation and deactivation guards in the transition path.
527
- * Async guards return false with a console warning.
517
+ * IMPORTANT: forwardTo creates a URL alias, not a transition chain.
518
+ * Guards (canActivate) on the source route are NOT executed.
519
+ * Only guards on the final destination are executed.
528
520
  *
529
- * @param name - Route name to check
530
- * @param params - Route parameters (optional)
531
- * @returns true if navigation is allowed, false otherwise
521
+ * This matches Vue Router and Angular Router behavior.
532
522
  */
533
- canNavigateTo: (name: string, params?: Params) => boolean;
523
+ forwardTo?: string | ForwardToCallback<Dependencies>;
524
+ /** Nested child routes. */
525
+ children?: Route<Dependencies>[];
526
+ /** Encodes state params to URL params. */
527
+ encodeParams?: (stateParams: Params) => Params;
528
+ /** Decodes URL params to state params. */
529
+ decodeParams?: (pathParams: Params) => Params;
534
530
  /**
535
- * Dispose the router and release all resources.
531
+ * Default parameters for this route.
536
532
  *
537
- * Stops the router if active, calls plugin teardown, clears all event
538
- * listeners, middleware, routes, and dependencies. After disposal, all
539
- * mutating methods throw a ROUTER_DISPOSED error. Idempotent — safe to
540
- * call multiple times.
533
+ * These values are merged into state.params when creating route states.
534
+ * Missing URL params are filled from defaultParams.
541
535
  */
542
- dispose: () => void;
536
+ defaultParams?: Params;
537
+ }
538
+ /**
539
+ * Configuration update options for updateRoute().
540
+ * All properties are optional. Set to null to remove the configuration.
541
+ */
542
+ interface RouteConfigUpdate<Dependencies extends DefaultDependencies = DefaultDependencies> {
543
+ /** Set to null to remove forwardTo */
544
+ forwardTo?: string | ForwardToCallback<Dependencies> | null;
545
+ /** Set to null to remove defaultParams */
546
+ defaultParams?: Params | null;
547
+ /** Set to null to remove decoder */
548
+ decodeParams?: ((params: Params) => Params) | null;
549
+ /** Set to null to remove encoder */
550
+ encodeParams?: ((params: Params) => Params) | null;
551
+ /** Set to null to remove canActivate */
552
+ canActivate?: GuardFnFactory<Dependencies> | null;
553
+ /** Set to null to remove canDeactivate */
554
+ canDeactivate?: GuardFnFactory<Dependencies> | null;
543
555
  }
544
556
 
545
557
  /**
@@ -584,6 +596,13 @@ interface EventToNameMap {
584
596
  TRANSITION_SUCCESS: "$$success";
585
597
  TRANSITION_ERROR: "$$error";
586
598
  }
599
+ /**
600
+ * Mapping of event names to plugin method names.
601
+ * Type-level computation from EventToNameMap + EventToPluginMap.
602
+ */
603
+ type EventMethodMap = {
604
+ [K in EventsKeys as EventToNameMap[K]]: EventToPluginMap[K];
605
+ };
587
606
  /**
588
607
  * Mapping of error code keys to their values
589
608
  */
@@ -600,4 +619,64 @@ interface ErrorCodeToValueMap {
600
619
  ROUTER_DISPOSED: "DISPOSED";
601
620
  }
602
621
 
603
- export type { ActivationFn, Config, DefaultDependencies, DefaultParamsCallback, DefaultRouteCallback, ErrorCodeKeys, ErrorCodeToValueMap, ErrorCodeValues, EventName, EventToNameMap, EventToPluginMap, EventsKeys, ForwardToCallback, GuardFn, LimitsConfig, Listener, Middleware, NavigationOptions, Navigator, Options, Params, Plugin, PluginMethod, QueryParamsMode, QueryParamsOptions, RouteParams, RouteTreeState, Router, RouterError, SimpleState, State, StateMeta, StateMetaInput, SubscribeFn, SubscribeState, Subscription, TransitionMeta, TransitionPhase, TransitionReason, Unsubscribe };
622
+ /**
623
+ * API interfaces for modular router access.
624
+ * These interfaces are implemented by standalone API functions in @real-router/core.
625
+ */
626
+
627
+ /**
628
+ * Plugin API — for plugins and infrastructure packages.
629
+ * Hides plugin-internal methods from public autocomplete.
630
+ */
631
+ interface PluginApi {
632
+ makeState: <P extends Params = Params, MP extends Params = Params>(name: string, params?: P, path?: string, meta?: StateMetaInput<MP>, forceId?: number) => State<P, MP>;
633
+ buildState: (routeName: string, routeParams: Params) => RouteTreeState | undefined;
634
+ forwardState: <P extends Params = Params>(routeName: string, routeParams: P) => SimpleState<P>;
635
+ matchPath: <P extends Params = Params, MP extends Params = Params>(path: string) => State<P, MP> | undefined;
636
+ setRootPath: (rootPath: string) => void;
637
+ getRootPath: () => string;
638
+ navigateToState: (toState: State, fromState: State | undefined, opts: NavigationOptions) => Promise<State>;
639
+ addEventListener: <E extends EventName>(eventName: E, cb: Plugin[EventMethodMap[E]]) => Unsubscribe;
640
+ buildNavigationState: (name: string, params?: Params) => State | undefined;
641
+ getOptions: () => Options;
642
+ getTree: () => unknown;
643
+ getForwardState: () => <P extends Params = Params>(routeName: string, routeParams: P) => SimpleState<P>;
644
+ setForwardState: (fn: <P extends Params = Params>(routeName: string, routeParams: P) => SimpleState<P>) => void;
645
+ }
646
+ /**
647
+ * Routes API — for dynamic route mutation.
648
+ */
649
+ interface RoutesApi<Dependencies extends DefaultDependencies = DefaultDependencies> {
650
+ add: (routes: Route<Dependencies>[] | Route<Dependencies>, options?: {
651
+ parent?: string;
652
+ }) => void;
653
+ remove: (name: string) => void;
654
+ update: (name: string, updates: RouteConfigUpdate<Dependencies>) => void;
655
+ clear: () => void;
656
+ has: (name: string) => boolean;
657
+ get: (name: string) => Route<Dependencies> | undefined;
658
+ getConfig: (name: string) => Record<string, unknown> | undefined;
659
+ }
660
+ /**
661
+ * Dependencies API — CRUD for dependency injection.
662
+ */
663
+ interface DependenciesApi<Dependencies extends DefaultDependencies = DefaultDependencies> {
664
+ get: <K extends keyof Dependencies>(key: K) => Dependencies[K];
665
+ getAll: () => Partial<Dependencies>;
666
+ set: <K extends keyof Dependencies & string>(name: K, value: Dependencies[K]) => void;
667
+ setAll: (deps: Dependencies) => void;
668
+ remove: (name: keyof Dependencies) => void;
669
+ reset: () => void;
670
+ has: (name: keyof Dependencies) => boolean;
671
+ }
672
+ /**
673
+ * Lifecycle API — guard registration (addActivateGuard, addDeactivateGuard, etc.)
674
+ */
675
+ interface LifecycleApi<Dependencies extends DefaultDependencies = DefaultDependencies> {
676
+ addActivateGuard: (name: string, canActivateHandler: GuardFnFactory<Dependencies> | boolean) => void;
677
+ addDeactivateGuard: (name: string, canDeactivateHandler: GuardFnFactory<Dependencies> | boolean) => void;
678
+ removeActivateGuard: (name: string) => void;
679
+ removeDeactivateGuard: (name: string) => void;
680
+ }
681
+
682
+ export type { Config, DefaultDependencies, DefaultParamsCallback, DefaultRouteCallback, DependenciesApi, ErrorCodeKeys, ErrorCodeToValueMap, ErrorCodeValues, EventMethodMap, EventName, EventToNameMap, EventToPluginMap, EventsKeys, ForwardToCallback, GuardFn, GuardFnFactory, LifecycleApi, LimitsConfig, Listener, NavigationOptions, Navigator, Options, Params, Plugin, PluginApi, PluginFactory, PluginMethod, QueryParamsMode, QueryParamsOptions, Route, RouteConfigUpdate, RouteParams, RouteTreeState, Router, RouterError, RoutesApi, SimpleState, State, StateMeta, StateMetaInput, SubscribeFn, SubscribeState, Subscription, TransitionMeta, TransitionPhase, TransitionReason, Unsubscribe };
@@ -1 +1 @@
1
- {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/index.ts":{"bytes":1309,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js":{"imports":[],"exports":[],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0}},"bytes":0}}}
1
+ {"inputs":{"../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js":{"bytes":569,"imports":[],"format":"esm"},"src/index.ts":{"bytes":1349,"imports":[{"path":"/home/runner/work/real-router/real-router/node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"dist/cjs/index.js":{"imports":[],"exports":[],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0}},"bytes":0}}}
@@ -44,7 +44,7 @@ interface SimpleState<P extends Params = Params> {
44
44
  name: string;
45
45
  params: P;
46
46
  }
47
- type TransitionPhase = "deactivating" | "activating" | "middleware";
47
+ type TransitionPhase = "deactivating" | "activating";
48
48
  type TransitionReason = "success" | "blocked" | "cancelled" | "error";
49
49
  interface TransitionMeta {
50
50
  phase: TransitionPhase;
@@ -103,7 +103,7 @@ interface RouterError extends Error {
103
103
  *
104
104
  * All options are optional and have sensible defaults. Options can be combined to achieve
105
105
  * complex navigation behaviors. The options object is stored in state.meta.options and is
106
- * available to middleware, guards, and event listeners.
106
+ * available to guards and event listeners.
107
107
  *
108
108
  * @see {@link Router.navigate} for navigation method that accepts these options
109
109
  * @see {@link State.meta} for where options are stored after navigation
@@ -137,11 +137,11 @@ interface NavigationOptions {
137
137
  *
138
138
  * Without `reload`:
139
139
  * - Navigation to current route throws SAME_STATES error
140
- * - No lifecycle hooks or middleware execute
140
+ * - No lifecycle hooks execute
141
141
  * - No events are fired
142
142
  *
143
143
  * With `reload`:
144
- * - Full transition executes (deactivate → activate → middleware)
144
+ * - Full transition executes (deactivate → activate)
145
145
  * - All lifecycle hooks run again
146
146
  * - TRANSITION_SUCCESS event fires with same state
147
147
  * - State object is recreated (new reference)
@@ -192,16 +192,16 @@ interface NavigationOptions {
192
192
  *
193
193
  * @description
194
194
  * When `true`, bypasses only the canDeactivate lifecycle hooks for segments being
195
- * deactivated. canActivate guards and middleware still execute normally. This allows
195
+ * deactivated. canActivate guards still execute normally. This allows
196
196
  * forcing navigation away from routes with confirmation dialogs or unsaved changes.
197
197
  *
198
198
  * Skipped vs executed:
199
199
  * ```
200
200
  * // Normal transition
201
- * deactivate(fromSegments) → activate(toSegments) → middleware → success
201
+ * deactivate(fromSegments) → activate(toSegments) → success
202
202
  *
203
203
  * // With forceDeactivate: true
204
- * [skip deactivate] → activate(toSegments) → middleware → success
204
+ * [skip deactivate] → activate(toSegments) → success
205
205
  * ```
206
206
  *
207
207
  * ⚠️ Data loss risk: Bypassing canDeactivate means unsaved changes will be lost
@@ -227,21 +227,12 @@ interface NavigationOptions {
227
227
  *
228
228
  * @description
229
229
  * Automatically set by the router when a navigation is triggered by a redirect from
230
- * middleware or lifecycle hooks. This flag is used internally to track redirect chains
230
+ * guards or lifecycle hooks. This flag is used internally to track redirect chains
231
231
  * and is stored in state.meta.options.redirected.
232
232
  *
233
233
  * @default false (auto-set by router during redirects)
234
234
  *
235
235
  * @example
236
- * // Middleware triggers automatic redirect
237
- * router.useMiddleware((toState, fromState, opts) => {
238
- * if (!isAuthenticated && toState.name !== 'login') {
239
- * // Router will automatically set redirected: true
240
- * return { name: 'login', params: { next: toState.path } };
241
- * }
242
- * });
243
- *
244
- * @example
245
236
  * // Accessing redirect flag in lifecycle
246
237
  * router.addActivateGuard('dashboard', (toState, fromState) => {
247
238
  * if (toState.meta?.options?.redirected) {
@@ -278,13 +269,6 @@ interface LimitsConfig {
278
269
  * @default 50
279
270
  */
280
271
  maxPlugins: number;
281
- /**
282
- * Maximum number of middleware functions in the navigation pipeline.
283
- * Controls middleware chain length to prevent stack overflow and performance issues.
284
- *
285
- * @default 50
286
- */
287
- maxMiddleware: number;
288
272
  /**
289
273
  * Maximum number of event listeners per event type.
290
274
  * Prevents memory leaks from excessive listener registration.
@@ -316,11 +300,11 @@ interface LimitsConfig {
316
300
  }
317
301
 
318
302
  /**
319
- * Base router types without Router class dependency.
303
+ * Router types and interfaces.
320
304
  *
321
- * Router-dependent types (Route, RouteConfigUpdate, ActivationFnFactory,
322
- * MiddlewareFactory, PluginFactory) are defined in @real-router/core
323
- * to avoid circular dependencies.
305
+ * Factory types (PluginFactory, GuardFnFactory) and
306
+ * route config types (Route, RouteConfigUpdate) use self-referencing Router<D>
307
+ * within this single file to resolve circular dependencies.
324
308
  */
325
309
 
326
310
  type LogLevel = "log" | "warn" | "error";
@@ -432,7 +416,6 @@ interface Options {
432
416
  */
433
417
  noValidate?: boolean;
434
418
  }
435
- type ActivationFn = (toState: State, fromState: State | undefined) => boolean | Promise<boolean | State | void> | State | void;
436
419
  type GuardFn = (toState: State, fromState: State | undefined) => boolean | Promise<boolean>;
437
420
  type DefaultDependencies = object;
438
421
  interface Config {
@@ -450,7 +433,6 @@ interface Plugin {
450
433
  onTransitionSuccess?: (toState: State, fromState: State | undefined, opts: NavigationOptions) => void;
451
434
  teardown?: () => void;
452
435
  }
453
- type Middleware = ActivationFn;
454
436
  interface SubscribeState {
455
437
  route: State;
456
438
  previousRoute?: State | undefined;
@@ -482,64 +464,94 @@ interface Navigator {
482
464
  subscribe: (listener: SubscribeFn) => Unsubscribe;
483
465
  }
484
466
  /**
485
- * Router interface - public API for route navigation and lifecycle management.
467
+ * Router interface full public API for route navigation and lifecycle management.
486
468
  *
487
- * Defines the contract for router implementations. The actual Router class in
488
- * @real-router/core implements this interface with full functionality.
489
- *
490
- * This interface uses `GuardFn | boolean` for guard types to avoid circular
491
- * dependencies. The concrete Router class in @real-router/core narrows this to
492
- * `GuardFnFactory | boolean` for more precise type checking.
469
+ * Generic parameter D constrains dependency injection types.
470
+ * Factory types (PluginFactory, GuardFnFactory) self-reference this interface
471
+ * within the same file to avoid circular dependencies.
493
472
  */
494
- interface Router {
495
- /**
496
- * Register an activation guard for a route.
497
- *
498
- * @param name - Route name
499
- * @param guard - Guard function or boolean
500
- * @returns this for method chaining
501
- */
502
- addActivateGuard: (name: string, guard: GuardFn | boolean) => this;
503
- /**
504
- * Register a deactivation guard for a route.
505
- *
506
- * @param name - Route name
507
- * @param guard - Guard function or boolean
508
- * @returns this for method chaining
509
- */
510
- addDeactivateGuard: (name: string, guard: GuardFn | boolean) => this;
511
- /**
512
- * Remove an activation guard from a route.
513
- *
514
- * @param name - Route name
515
- */
516
- removeActivateGuard: (name: string) => void;
517
- /**
518
- * Remove a deactivation guard from a route.
519
- *
520
- * @param name - Route name
521
- */
522
- removeDeactivateGuard: (name: string) => void;
473
+ interface Router<D extends DefaultDependencies = DefaultDependencies> {
474
+ [key: string]: unknown;
475
+ isActiveRoute: (name: string, params?: Params, strictEquality?: boolean, ignoreQueryParams?: boolean) => boolean;
476
+ buildPath: (route: string, params?: Params) => string;
477
+ getState: <P extends Params = Params, MP extends Params = Params>() => State<P, MP> | undefined;
478
+ getPreviousState: () => State | undefined;
479
+ areStatesEqual: (state1: State | undefined, state2: State | undefined, ignoreQueryParams?: boolean) => boolean;
480
+ shouldUpdateNode: (nodeName: string) => (toState: State, fromState?: State) => boolean;
481
+ isActive: () => boolean;
482
+ start: (startPath: string) => Promise<State>;
483
+ stop: () => this;
484
+ dispose: () => void;
485
+ canNavigateTo: (name: string, params?: Params) => boolean;
486
+ usePlugin: (...plugins: PluginFactory<D>[]) => Unsubscribe;
487
+ subscribe: (listener: SubscribeFn) => Unsubscribe;
488
+ navigate: (routeName: string, routeParams?: Params, options?: NavigationOptions) => Promise<State>;
489
+ navigateToDefault: (options?: NavigationOptions) => Promise<State>;
490
+ }
491
+ /**
492
+ * Factory function for creating plugins.
493
+ * Receives the router instance and a dependency getter.
494
+ */
495
+ type PluginFactory<Dependencies extends DefaultDependencies = DefaultDependencies> = (router: Router<Dependencies>, getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K]) => Plugin;
496
+ /**
497
+ * Factory function for creating guards.
498
+ * Receives the router instance and a dependency getter.
499
+ */
500
+ type GuardFnFactory<Dependencies extends DefaultDependencies = DefaultDependencies> = (router: Router<Dependencies>, getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K]) => GuardFn;
501
+ /**
502
+ * Route configuration.
503
+ */
504
+ interface Route<Dependencies extends DefaultDependencies = DefaultDependencies> {
505
+ [key: string]: unknown;
506
+ /** Route name (dot-separated for nested routes). */
507
+ name: string;
508
+ /** URL path pattern for this route. */
509
+ path: string;
510
+ /** Factory function that returns a guard for route activation. */
511
+ canActivate?: GuardFnFactory<Dependencies>;
512
+ /** Factory function that returns a guard for route deactivation. */
513
+ canDeactivate?: GuardFnFactory<Dependencies>;
523
514
  /**
524
- * Check if navigation to a route is allowed without performing actual navigation.
515
+ * Redirects navigation to another route.
525
516
  *
526
- * Synchronously checks all activation and deactivation guards in the transition path.
527
- * Async guards return false with a console warning.
517
+ * IMPORTANT: forwardTo creates a URL alias, not a transition chain.
518
+ * Guards (canActivate) on the source route are NOT executed.
519
+ * Only guards on the final destination are executed.
528
520
  *
529
- * @param name - Route name to check
530
- * @param params - Route parameters (optional)
531
- * @returns true if navigation is allowed, false otherwise
521
+ * This matches Vue Router and Angular Router behavior.
532
522
  */
533
- canNavigateTo: (name: string, params?: Params) => boolean;
523
+ forwardTo?: string | ForwardToCallback<Dependencies>;
524
+ /** Nested child routes. */
525
+ children?: Route<Dependencies>[];
526
+ /** Encodes state params to URL params. */
527
+ encodeParams?: (stateParams: Params) => Params;
528
+ /** Decodes URL params to state params. */
529
+ decodeParams?: (pathParams: Params) => Params;
534
530
  /**
535
- * Dispose the router and release all resources.
531
+ * Default parameters for this route.
536
532
  *
537
- * Stops the router if active, calls plugin teardown, clears all event
538
- * listeners, middleware, routes, and dependencies. After disposal, all
539
- * mutating methods throw a ROUTER_DISPOSED error. Idempotent — safe to
540
- * call multiple times.
533
+ * These values are merged into state.params when creating route states.
534
+ * Missing URL params are filled from defaultParams.
541
535
  */
542
- dispose: () => void;
536
+ defaultParams?: Params;
537
+ }
538
+ /**
539
+ * Configuration update options for updateRoute().
540
+ * All properties are optional. Set to null to remove the configuration.
541
+ */
542
+ interface RouteConfigUpdate<Dependencies extends DefaultDependencies = DefaultDependencies> {
543
+ /** Set to null to remove forwardTo */
544
+ forwardTo?: string | ForwardToCallback<Dependencies> | null;
545
+ /** Set to null to remove defaultParams */
546
+ defaultParams?: Params | null;
547
+ /** Set to null to remove decoder */
548
+ decodeParams?: ((params: Params) => Params) | null;
549
+ /** Set to null to remove encoder */
550
+ encodeParams?: ((params: Params) => Params) | null;
551
+ /** Set to null to remove canActivate */
552
+ canActivate?: GuardFnFactory<Dependencies> | null;
553
+ /** Set to null to remove canDeactivate */
554
+ canDeactivate?: GuardFnFactory<Dependencies> | null;
543
555
  }
544
556
 
545
557
  /**
@@ -584,6 +596,13 @@ interface EventToNameMap {
584
596
  TRANSITION_SUCCESS: "$$success";
585
597
  TRANSITION_ERROR: "$$error";
586
598
  }
599
+ /**
600
+ * Mapping of event names to plugin method names.
601
+ * Type-level computation from EventToNameMap + EventToPluginMap.
602
+ */
603
+ type EventMethodMap = {
604
+ [K in EventsKeys as EventToNameMap[K]]: EventToPluginMap[K];
605
+ };
587
606
  /**
588
607
  * Mapping of error code keys to their values
589
608
  */
@@ -600,4 +619,64 @@ interface ErrorCodeToValueMap {
600
619
  ROUTER_DISPOSED: "DISPOSED";
601
620
  }
602
621
 
603
- export type { ActivationFn, Config, DefaultDependencies, DefaultParamsCallback, DefaultRouteCallback, ErrorCodeKeys, ErrorCodeToValueMap, ErrorCodeValues, EventName, EventToNameMap, EventToPluginMap, EventsKeys, ForwardToCallback, GuardFn, LimitsConfig, Listener, Middleware, NavigationOptions, Navigator, Options, Params, Plugin, PluginMethod, QueryParamsMode, QueryParamsOptions, RouteParams, RouteTreeState, Router, RouterError, SimpleState, State, StateMeta, StateMetaInput, SubscribeFn, SubscribeState, Subscription, TransitionMeta, TransitionPhase, TransitionReason, Unsubscribe };
622
+ /**
623
+ * API interfaces for modular router access.
624
+ * These interfaces are implemented by standalone API functions in @real-router/core.
625
+ */
626
+
627
+ /**
628
+ * Plugin API — for plugins and infrastructure packages.
629
+ * Hides plugin-internal methods from public autocomplete.
630
+ */
631
+ interface PluginApi {
632
+ makeState: <P extends Params = Params, MP extends Params = Params>(name: string, params?: P, path?: string, meta?: StateMetaInput<MP>, forceId?: number) => State<P, MP>;
633
+ buildState: (routeName: string, routeParams: Params) => RouteTreeState | undefined;
634
+ forwardState: <P extends Params = Params>(routeName: string, routeParams: P) => SimpleState<P>;
635
+ matchPath: <P extends Params = Params, MP extends Params = Params>(path: string) => State<P, MP> | undefined;
636
+ setRootPath: (rootPath: string) => void;
637
+ getRootPath: () => string;
638
+ navigateToState: (toState: State, fromState: State | undefined, opts: NavigationOptions) => Promise<State>;
639
+ addEventListener: <E extends EventName>(eventName: E, cb: Plugin[EventMethodMap[E]]) => Unsubscribe;
640
+ buildNavigationState: (name: string, params?: Params) => State | undefined;
641
+ getOptions: () => Options;
642
+ getTree: () => unknown;
643
+ getForwardState: () => <P extends Params = Params>(routeName: string, routeParams: P) => SimpleState<P>;
644
+ setForwardState: (fn: <P extends Params = Params>(routeName: string, routeParams: P) => SimpleState<P>) => void;
645
+ }
646
+ /**
647
+ * Routes API — for dynamic route mutation.
648
+ */
649
+ interface RoutesApi<Dependencies extends DefaultDependencies = DefaultDependencies> {
650
+ add: (routes: Route<Dependencies>[] | Route<Dependencies>, options?: {
651
+ parent?: string;
652
+ }) => void;
653
+ remove: (name: string) => void;
654
+ update: (name: string, updates: RouteConfigUpdate<Dependencies>) => void;
655
+ clear: () => void;
656
+ has: (name: string) => boolean;
657
+ get: (name: string) => Route<Dependencies> | undefined;
658
+ getConfig: (name: string) => Record<string, unknown> | undefined;
659
+ }
660
+ /**
661
+ * Dependencies API — CRUD for dependency injection.
662
+ */
663
+ interface DependenciesApi<Dependencies extends DefaultDependencies = DefaultDependencies> {
664
+ get: <K extends keyof Dependencies>(key: K) => Dependencies[K];
665
+ getAll: () => Partial<Dependencies>;
666
+ set: <K extends keyof Dependencies & string>(name: K, value: Dependencies[K]) => void;
667
+ setAll: (deps: Dependencies) => void;
668
+ remove: (name: keyof Dependencies) => void;
669
+ reset: () => void;
670
+ has: (name: keyof Dependencies) => boolean;
671
+ }
672
+ /**
673
+ * Lifecycle API — guard registration (addActivateGuard, addDeactivateGuard, etc.)
674
+ */
675
+ interface LifecycleApi<Dependencies extends DefaultDependencies = DefaultDependencies> {
676
+ addActivateGuard: (name: string, canActivateHandler: GuardFnFactory<Dependencies> | boolean) => void;
677
+ addDeactivateGuard: (name: string, canDeactivateHandler: GuardFnFactory<Dependencies> | boolean) => void;
678
+ removeActivateGuard: (name: string) => void;
679
+ removeDeactivateGuard: (name: string) => void;
680
+ }
681
+
682
+ export type { Config, DefaultDependencies, DefaultParamsCallback, DefaultRouteCallback, DependenciesApi, ErrorCodeKeys, ErrorCodeToValueMap, ErrorCodeValues, EventMethodMap, EventName, EventToNameMap, EventToPluginMap, EventsKeys, ForwardToCallback, GuardFn, GuardFnFactory, LifecycleApi, LimitsConfig, Listener, NavigationOptions, Navigator, Options, Params, Plugin, PluginApi, PluginFactory, PluginMethod, QueryParamsMode, QueryParamsOptions, Route, RouteConfigUpdate, RouteParams, RouteTreeState, Router, RouterError, RoutesApi, SimpleState, State, StateMeta, StateMetaInput, SubscribeFn, SubscribeState, Subscription, TransitionMeta, TransitionPhase, TransitionReason, Unsubscribe };
@@ -1 +1 @@
1
- {"inputs":{"src/index.ts":{"bytes":1309,"imports":[],"format":"esm"}},"outputs":{"dist/esm/index.mjs":{"imports":[],"exports":[],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0}},"bytes":0}}}
1
+ {"inputs":{"src/index.ts":{"bytes":1349,"imports":[],"format":"esm"}},"outputs":{"dist/esm/index.mjs":{"imports":[],"exports":[],"entryPoint":"src/index.ts","inputs":{"src/index.ts":{"bytesInOutput":0}},"bytes":0}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/types",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "type": "commonjs",
5
5
  "description": "Shared TypeScript types for Real Router ecosystem",
6
6
  "types": "./dist/esm/index.d.mts",
package/src/api.ts ADDED
@@ -0,0 +1,145 @@
1
+ // packages/core-types/modules/api.ts
2
+
3
+ /**
4
+ * API interfaces for modular router access.
5
+ * These interfaces are implemented by standalone API functions in @real-router/core.
6
+ */
7
+
8
+ import type {
9
+ Params,
10
+ State,
11
+ SimpleState,
12
+ StateMetaInput,
13
+ NavigationOptions,
14
+ Unsubscribe,
15
+ } from "./base";
16
+ import type { EventMethodMap, EventName } from "./constants";
17
+ import type { RouteTreeState } from "./route-node-types";
18
+ import type {
19
+ DefaultDependencies,
20
+ GuardFnFactory,
21
+ Options,
22
+ Plugin,
23
+ Route,
24
+ RouteConfigUpdate,
25
+ } from "./router";
26
+
27
+ /**
28
+ * Plugin API — for plugins and infrastructure packages.
29
+ * Hides plugin-internal methods from public autocomplete.
30
+ */
31
+ export interface PluginApi {
32
+ makeState: <P extends Params = Params, MP extends Params = Params>(
33
+ name: string,
34
+ params?: P,
35
+ path?: string,
36
+ meta?: StateMetaInput<MP>,
37
+ forceId?: number,
38
+ ) => State<P, MP>;
39
+
40
+ buildState: (
41
+ routeName: string,
42
+ routeParams: Params,
43
+ ) => RouteTreeState | undefined;
44
+
45
+ forwardState: <P extends Params = Params>(
46
+ routeName: string,
47
+ routeParams: P,
48
+ ) => SimpleState<P>;
49
+
50
+ matchPath: <P extends Params = Params, MP extends Params = Params>(
51
+ path: string,
52
+ ) => State<P, MP> | undefined;
53
+
54
+ setRootPath: (rootPath: string) => void;
55
+ getRootPath: () => string;
56
+
57
+ navigateToState: (
58
+ toState: State,
59
+ fromState: State | undefined,
60
+ opts: NavigationOptions,
61
+ ) => Promise<State>;
62
+
63
+ addEventListener: <E extends EventName>(
64
+ eventName: E,
65
+ cb: Plugin[EventMethodMap[E]],
66
+ ) => Unsubscribe;
67
+
68
+ buildNavigationState: (name: string, params?: Params) => State | undefined;
69
+
70
+ getOptions: () => Options;
71
+
72
+ getTree: () => unknown;
73
+
74
+ getForwardState: () => <P extends Params = Params>(
75
+ routeName: string,
76
+ routeParams: P,
77
+ ) => SimpleState<P>;
78
+
79
+ setForwardState: (
80
+ fn: <P extends Params = Params>(
81
+ routeName: string,
82
+ routeParams: P,
83
+ ) => SimpleState<P>,
84
+ ) => void;
85
+ }
86
+
87
+ /**
88
+ * Routes API — for dynamic route mutation.
89
+ */
90
+ export interface RoutesApi<
91
+ Dependencies extends DefaultDependencies = DefaultDependencies,
92
+ > {
93
+ add: (
94
+ routes: Route<Dependencies>[] | Route<Dependencies>,
95
+ options?: { parent?: string },
96
+ ) => void;
97
+
98
+ remove: (name: string) => void;
99
+
100
+ update: (name: string, updates: RouteConfigUpdate<Dependencies>) => void;
101
+
102
+ clear: () => void;
103
+
104
+ has: (name: string) => boolean;
105
+
106
+ get: (name: string) => Route<Dependencies> | undefined;
107
+
108
+ getConfig: (name: string) => Record<string, unknown> | undefined;
109
+ }
110
+
111
+ /**
112
+ * Dependencies API — CRUD for dependency injection.
113
+ */
114
+ export interface DependenciesApi<
115
+ Dependencies extends DefaultDependencies = DefaultDependencies,
116
+ > {
117
+ get: <K extends keyof Dependencies>(key: K) => Dependencies[K];
118
+ getAll: () => Partial<Dependencies>;
119
+ set: <K extends keyof Dependencies & string>(
120
+ name: K,
121
+ value: Dependencies[K],
122
+ ) => void;
123
+ setAll: (deps: Dependencies) => void;
124
+ remove: (name: keyof Dependencies) => void;
125
+ reset: () => void;
126
+ has: (name: keyof Dependencies) => boolean;
127
+ }
128
+
129
+ /**
130
+ * Lifecycle API — guard registration (addActivateGuard, addDeactivateGuard, etc.)
131
+ */
132
+ export interface LifecycleApi<
133
+ Dependencies extends DefaultDependencies = DefaultDependencies,
134
+ > {
135
+ addActivateGuard: (
136
+ name: string,
137
+ canActivateHandler: GuardFnFactory<Dependencies> | boolean,
138
+ ) => void;
139
+ addDeactivateGuard: (
140
+ name: string,
141
+ canDeactivateHandler: GuardFnFactory<Dependencies> | boolean,
142
+ ) => void;
143
+ removeActivateGuard: (name: string) => void;
144
+ removeDeactivateGuard: (name: string) => void;
145
+ }
package/src/base.ts CHANGED
@@ -11,7 +11,7 @@ export interface SimpleState<P extends Params = Params> {
11
11
  params: P;
12
12
  }
13
13
 
14
- export type TransitionPhase = "deactivating" | "activating" | "middleware";
14
+ export type TransitionPhase = "deactivating" | "activating";
15
15
 
16
16
  export type TransitionReason = "success" | "blocked" | "cancelled" | "error";
17
17
 
@@ -80,7 +80,7 @@ export interface RouterError extends Error {
80
80
  *
81
81
  * All options are optional and have sensible defaults. Options can be combined to achieve
82
82
  * complex navigation behaviors. The options object is stored in state.meta.options and is
83
- * available to middleware, guards, and event listeners.
83
+ * available to guards and event listeners.
84
84
  *
85
85
  * @see {@link Router.navigate} for navigation method that accepts these options
86
86
  * @see {@link State.meta} for where options are stored after navigation
@@ -121,11 +121,11 @@ export interface NavigationOptions {
121
121
  *
122
122
  * Without `reload`:
123
123
  * - Navigation to current route throws SAME_STATES error
124
- * - No lifecycle hooks or middleware execute
124
+ * - No lifecycle hooks execute
125
125
  * - No events are fired
126
126
  *
127
127
  * With `reload`:
128
- * - Full transition executes (deactivate → activate → middleware)
128
+ * - Full transition executes (deactivate → activate)
129
129
  * - All lifecycle hooks run again
130
130
  * - TRANSITION_SUCCESS event fires with same state
131
131
  * - State object is recreated (new reference)
@@ -178,16 +178,16 @@ export interface NavigationOptions {
178
178
  *
179
179
  * @description
180
180
  * When `true`, bypasses only the canDeactivate lifecycle hooks for segments being
181
- * deactivated. canActivate guards and middleware still execute normally. This allows
181
+ * deactivated. canActivate guards still execute normally. This allows
182
182
  * forcing navigation away from routes with confirmation dialogs or unsaved changes.
183
183
  *
184
184
  * Skipped vs executed:
185
185
  * ```
186
186
  * // Normal transition
187
- * deactivate(fromSegments) → activate(toSegments) → middleware → success
187
+ * deactivate(fromSegments) → activate(toSegments) → success
188
188
  *
189
189
  * // With forceDeactivate: true
190
- * [skip deactivate] → activate(toSegments) → middleware → success
190
+ * [skip deactivate] → activate(toSegments) → success
191
191
  * ```
192
192
  *
193
193
  * ⚠️ Data loss risk: Bypassing canDeactivate means unsaved changes will be lost
@@ -214,21 +214,12 @@ export interface NavigationOptions {
214
214
  *
215
215
  * @description
216
216
  * Automatically set by the router when a navigation is triggered by a redirect from
217
- * middleware or lifecycle hooks. This flag is used internally to track redirect chains
217
+ * guards or lifecycle hooks. This flag is used internally to track redirect chains
218
218
  * and is stored in state.meta.options.redirected.
219
219
  *
220
220
  * @default false (auto-set by router during redirects)
221
221
  *
222
222
  * @example
223
- * // Middleware triggers automatic redirect
224
- * router.useMiddleware((toState, fromState, opts) => {
225
- * if (!isAuthenticated && toState.name !== 'login') {
226
- * // Router will automatically set redirected: true
227
- * return { name: 'login', params: { next: toState.path } };
228
- * }
229
- * });
230
- *
231
- * @example
232
223
  * // Accessing redirect flag in lifecycle
233
224
  * router.addActivateGuard('dashboard', (toState, fromState) => {
234
225
  * if (toState.meta?.options?.redirected) {
package/src/constants.ts CHANGED
@@ -87,6 +87,14 @@ export interface EventToNameMap {
87
87
  TRANSITION_ERROR: "$$error";
88
88
  }
89
89
 
90
+ /**
91
+ * Mapping of event names to plugin method names.
92
+ * Type-level computation from EventToNameMap + EventToPluginMap.
93
+ */
94
+ export type EventMethodMap = {
95
+ [K in EventsKeys as EventToNameMap[K]]: EventToPluginMap[K];
96
+ };
97
+
90
98
  /**
91
99
  * Mapping of error code keys to their values
92
100
  */
package/src/index.ts CHANGED
@@ -23,26 +23,26 @@ export type {
23
23
  TransitionMeta,
24
24
  } from "./base";
25
25
 
26
- // Router types (base types without Router dependency)
27
- // Note: Route, RouteConfigUpdate, ActivationFnFactory, MiddlewareFactory,
28
- // PluginFactory, BuildStateResultWithSegments are in @real-router/core
26
+ // Router types, factory types, and route config types
29
27
  export type {
28
+ Router,
29
+ Navigator,
30
+ Route,
31
+ Plugin,
32
+ Listener,
30
33
  Options,
31
34
  DefaultRouteCallback,
32
35
  ForwardToCallback,
33
36
  DefaultParamsCallback,
34
- ActivationFn,
35
37
  GuardFn,
36
38
  DefaultDependencies,
37
39
  Config,
38
- Plugin,
39
- Middleware,
40
40
  SubscribeState,
41
41
  SubscribeFn,
42
- Listener,
43
42
  Subscription,
44
- Navigator,
45
- Router,
43
+ PluginFactory,
44
+ GuardFnFactory,
45
+ RouteConfigUpdate,
46
46
  } from "./router";
47
47
 
48
48
  // Limits configuration
@@ -57,7 +57,16 @@ export type {
57
57
  EventToPluginMap,
58
58
  EventToNameMap,
59
59
  ErrorCodeToValueMap,
60
+ EventMethodMap,
60
61
  } from "./constants";
61
62
 
63
+ // API interfaces (modular router access)
64
+ export type {
65
+ PluginApi,
66
+ RoutesApi,
67
+ DependenciesApi,
68
+ LifecycleApi,
69
+ } from "./api";
70
+
62
71
  // Note: RouterError type is a forward declaration matching the class in real-router package
63
72
  // Use import { RouterError } from "real-router" for the actual class implementation
package/src/limits.ts CHANGED
@@ -19,14 +19,6 @@ export interface LimitsConfig {
19
19
  */
20
20
  maxPlugins: number;
21
21
 
22
- /**
23
- * Maximum number of middleware functions in the navigation pipeline.
24
- * Controls middleware chain length to prevent stack overflow and performance issues.
25
- *
26
- * @default 50
27
- */
28
- maxMiddleware: number;
29
-
30
22
  /**
31
23
  * Maximum number of event listeners per event type.
32
24
  * Prevents memory leaks from excessive listener registration.
package/src/router.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  // packages/core-types/modules/router.ts
2
2
 
3
3
  /**
4
- * Base router types without Router class dependency.
4
+ * Router types and interfaces.
5
5
  *
6
- * Router-dependent types (Route, RouteConfigUpdate, ActivationFnFactory,
7
- * MiddlewareFactory, PluginFactory) are defined in @real-router/core
8
- * to avoid circular dependencies.
6
+ * Factory types (PluginFactory, GuardFnFactory) and
7
+ * route config types (Route, RouteConfigUpdate) use self-referencing Router<D>
8
+ * within this single file to resolve circular dependencies.
9
9
  */
10
10
 
11
11
  import type {
@@ -155,12 +155,6 @@ export interface Options {
155
155
  noValidate?: boolean;
156
156
  }
157
157
 
158
- export type ActivationFn = (
159
- toState: State,
160
- fromState: State | undefined,
161
- // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
162
- ) => boolean | Promise<boolean | State | void> | State | void;
163
-
164
158
  export type GuardFn = (
165
159
  toState: State,
166
160
  fromState: State | undefined,
@@ -193,9 +187,6 @@ export interface Plugin {
193
187
  teardown?: () => void;
194
188
  }
195
189
 
196
- // eslint-disable-next-line sonarjs/redundant-type-aliases
197
- export type Middleware = ActivationFn;
198
-
199
190
  export interface SubscribeState {
200
191
  route: State;
201
192
  previousRoute?: State | undefined;
@@ -241,67 +232,152 @@ export interface Navigator {
241
232
  }
242
233
 
243
234
  /**
244
- * Router interface - public API for route navigation and lifecycle management.
245
- *
246
- * Defines the contract for router implementations. The actual Router class in
247
- * @real-router/core implements this interface with full functionality.
235
+ * Router interface full public API for route navigation and lifecycle management.
248
236
  *
249
- * This interface uses `GuardFn | boolean` for guard types to avoid circular
250
- * dependencies. The concrete Router class in @real-router/core narrows this to
251
- * `GuardFnFactory | boolean` for more precise type checking.
237
+ * Generic parameter D constrains dependency injection types.
238
+ * Factory types (PluginFactory, GuardFnFactory) self-reference this interface
239
+ * within the same file to avoid circular dependencies.
252
240
  */
253
- export interface Router {
254
- /**
255
- * Register an activation guard for a route.
256
- *
257
- * @param name - Route name
258
- * @param guard - Guard function or boolean
259
- * @returns this for method chaining
260
- */
261
- addActivateGuard: (name: string, guard: GuardFn | boolean) => this;
241
+ export interface Router<D extends DefaultDependencies = DefaultDependencies> {
242
+ // Plugins add properties at runtime (e.g. browser-plugin adds buildUrl, matchUrl).
243
+ // Index signature allows property access when module augmentation isn't in scope.
244
+ [key: string]: unknown;
262
245
 
263
- /**
264
- * Register a deactivation guard for a route.
265
- *
266
- * @param name - Route name
267
- * @param guard - Guard function or boolean
268
- * @returns this for method chaining
269
- */
270
- addDeactivateGuard: (name: string, guard: GuardFn | boolean) => this;
246
+ isActiveRoute: (
247
+ name: string,
248
+ params?: Params,
249
+ strictEquality?: boolean,
250
+ ignoreQueryParams?: boolean,
251
+ ) => boolean;
271
252
 
272
- /**
273
- * Remove an activation guard from a route.
274
- *
275
- * @param name - Route name
276
- */
277
- removeActivateGuard: (name: string) => void;
253
+ buildPath: (route: string, params?: Params) => string;
278
254
 
279
- /**
280
- * Remove a deactivation guard from a route.
281
- *
282
- * @param name - Route name
283
- */
284
- removeDeactivateGuard: (name: string) => void;
255
+ getState: <P extends Params = Params, MP extends Params = Params>() =>
256
+ | State<P, MP>
257
+ | undefined;
258
+
259
+ getPreviousState: () => State | undefined;
285
260
 
261
+ areStatesEqual: (
262
+ state1: State | undefined,
263
+ state2: State | undefined,
264
+ ignoreQueryParams?: boolean,
265
+ ) => boolean;
266
+
267
+ shouldUpdateNode: (
268
+ nodeName: string,
269
+ ) => (toState: State, fromState?: State) => boolean;
270
+
271
+ isActive: () => boolean;
272
+
273
+ start: (startPath: string) => Promise<State>;
274
+
275
+ stop: () => this;
276
+
277
+ dispose: () => void;
278
+
279
+ canNavigateTo: (name: string, params?: Params) => boolean;
280
+
281
+ usePlugin: (...plugins: PluginFactory<D>[]) => Unsubscribe;
282
+
283
+ subscribe: (listener: SubscribeFn) => Unsubscribe;
284
+
285
+ navigate: (
286
+ routeName: string,
287
+ routeParams?: Params,
288
+ options?: NavigationOptions,
289
+ ) => Promise<State>;
290
+
291
+ navigateToDefault: (options?: NavigationOptions) => Promise<State>;
292
+ }
293
+
294
+ // =============================================================================
295
+ // Factory Types (self-reference Router<D>)
296
+ // =============================================================================
297
+
298
+ /**
299
+ * Factory function for creating plugins.
300
+ * Receives the router instance and a dependency getter.
301
+ */
302
+ export type PluginFactory<
303
+ Dependencies extends DefaultDependencies = DefaultDependencies,
304
+ > = (
305
+ router: Router<Dependencies>,
306
+ getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K],
307
+ ) => Plugin;
308
+
309
+ /**
310
+ * Factory function for creating guards.
311
+ * Receives the router instance and a dependency getter.
312
+ */
313
+ export type GuardFnFactory<
314
+ Dependencies extends DefaultDependencies = DefaultDependencies,
315
+ > = (
316
+ router: Router<Dependencies>,
317
+ getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K],
318
+ ) => GuardFn;
319
+
320
+ // =============================================================================
321
+ // Route Configuration Types (use GuardFnFactory<D> + ForwardToCallback<D>)
322
+ // =============================================================================
323
+
324
+ /**
325
+ * Route configuration.
326
+ */
327
+ export interface Route<
328
+ Dependencies extends DefaultDependencies = DefaultDependencies,
329
+ > {
330
+ [key: string]: unknown;
331
+ /** Route name (dot-separated for nested routes). */
332
+ name: string;
333
+ /** URL path pattern for this route. */
334
+ path: string;
335
+ /** Factory function that returns a guard for route activation. */
336
+ canActivate?: GuardFnFactory<Dependencies>;
337
+ /** Factory function that returns a guard for route deactivation. */
338
+ canDeactivate?: GuardFnFactory<Dependencies>;
286
339
  /**
287
- * Check if navigation to a route is allowed without performing actual navigation.
340
+ * Redirects navigation to another route.
288
341
  *
289
- * Synchronously checks all activation and deactivation guards in the transition path.
290
- * Async guards return false with a console warning.
342
+ * IMPORTANT: forwardTo creates a URL alias, not a transition chain.
343
+ * Guards (canActivate) on the source route are NOT executed.
344
+ * Only guards on the final destination are executed.
291
345
  *
292
- * @param name - Route name to check
293
- * @param params - Route parameters (optional)
294
- * @returns true if navigation is allowed, false otherwise
346
+ * This matches Vue Router and Angular Router behavior.
295
347
  */
296
- canNavigateTo: (name: string, params?: Params) => boolean;
297
-
348
+ forwardTo?: string | ForwardToCallback<Dependencies>;
349
+ /** Nested child routes. */
350
+ children?: Route<Dependencies>[];
351
+ /** Encodes state params to URL params. */
352
+ encodeParams?: (stateParams: Params) => Params;
353
+ /** Decodes URL params to state params. */
354
+ decodeParams?: (pathParams: Params) => Params;
298
355
  /**
299
- * Dispose the router and release all resources.
356
+ * Default parameters for this route.
300
357
  *
301
- * Stops the router if active, calls plugin teardown, clears all event
302
- * listeners, middleware, routes, and dependencies. After disposal, all
303
- * mutating methods throw a ROUTER_DISPOSED error. Idempotent — safe to
304
- * call multiple times.
358
+ * These values are merged into state.params when creating route states.
359
+ * Missing URL params are filled from defaultParams.
305
360
  */
306
- dispose: () => void;
361
+ defaultParams?: Params;
362
+ }
363
+
364
+ /**
365
+ * Configuration update options for updateRoute().
366
+ * All properties are optional. Set to null to remove the configuration.
367
+ */
368
+ export interface RouteConfigUpdate<
369
+ Dependencies extends DefaultDependencies = DefaultDependencies,
370
+ > {
371
+ /** Set to null to remove forwardTo */
372
+ forwardTo?: string | ForwardToCallback<Dependencies> | null;
373
+ /** Set to null to remove defaultParams */
374
+ defaultParams?: Params | null;
375
+ /** Set to null to remove decoder */
376
+ decodeParams?: ((params: Params) => Params) | null;
377
+ /** Set to null to remove encoder */
378
+ encodeParams?: ((params: Params) => Params) | null;
379
+ /** Set to null to remove canActivate */
380
+ canActivate?: GuardFnFactory<Dependencies> | null;
381
+ /** Set to null to remove canDeactivate */
382
+ canDeactivate?: GuardFnFactory<Dependencies> | null;
307
383
  }