@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.
- package/dist/cjs/index.d.ts +159 -80
- package/dist/cjs/metafile-cjs.json +1 -1
- package/dist/esm/index.d.mts +159 -80
- package/dist/esm/metafile-esm.json +1 -1
- package/package.json +1 -1
- package/src/api.ts +145 -0
- package/src/base.ts +8 -17
- package/src/constants.ts +8 -0
- package/src/index.ts +18 -9
- package/src/limits.ts +0 -8
- package/src/router.ts +139 -63
package/dist/cjs/index.d.ts
CHANGED
|
@@ -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"
|
|
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
|
|
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
|
|
140
|
+
* - No lifecycle hooks execute
|
|
141
141
|
* - No events are fired
|
|
142
142
|
*
|
|
143
143
|
* With `reload`:
|
|
144
|
-
* - Full transition executes (deactivate → activate
|
|
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
|
|
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) →
|
|
201
|
+
* deactivate(fromSegments) → activate(toSegments) → success
|
|
202
202
|
*
|
|
203
203
|
* // With forceDeactivate: true
|
|
204
|
-
* [skip deactivate] → activate(toSegments) →
|
|
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
|
-
*
|
|
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
|
-
*
|
|
303
|
+
* Router types and interfaces.
|
|
320
304
|
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
* to
|
|
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
|
|
467
|
+
* Router interface — full public API for route navigation and lifecycle management.
|
|
486
468
|
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
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
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
*
|
|
515
|
+
* Redirects navigation to another route.
|
|
525
516
|
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
531
|
+
* Default parameters for this route.
|
|
536
532
|
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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":
|
|
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}}}
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -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"
|
|
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
|
|
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
|
|
140
|
+
* - No lifecycle hooks execute
|
|
141
141
|
* - No events are fired
|
|
142
142
|
*
|
|
143
143
|
* With `reload`:
|
|
144
|
-
* - Full transition executes (deactivate → activate
|
|
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
|
|
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) →
|
|
201
|
+
* deactivate(fromSegments) → activate(toSegments) → success
|
|
202
202
|
*
|
|
203
203
|
* // With forceDeactivate: true
|
|
204
|
-
* [skip deactivate] → activate(toSegments) →
|
|
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
|
-
*
|
|
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
|
-
*
|
|
303
|
+
* Router types and interfaces.
|
|
320
304
|
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
* to
|
|
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
|
|
467
|
+
* Router interface — full public API for route navigation and lifecycle management.
|
|
486
468
|
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
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
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
-
*
|
|
515
|
+
* Redirects navigation to another route.
|
|
525
516
|
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
531
|
+
* Default parameters for this route.
|
|
536
532
|
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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":
|
|
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
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"
|
|
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
|
|
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
|
|
124
|
+
* - No lifecycle hooks execute
|
|
125
125
|
* - No events are fired
|
|
126
126
|
*
|
|
127
127
|
* With `reload`:
|
|
128
|
-
* - Full transition executes (deactivate → activate
|
|
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
|
|
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) →
|
|
187
|
+
* deactivate(fromSegments) → activate(toSegments) → success
|
|
188
188
|
*
|
|
189
189
|
* // With forceDeactivate: true
|
|
190
|
-
* [skip deactivate] → activate(toSegments) →
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
*
|
|
4
|
+
* Router types and interfaces.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* to
|
|
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
|
|
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
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
*
|
|
340
|
+
* Redirects navigation to another route.
|
|
288
341
|
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
356
|
+
* Default parameters for this route.
|
|
300
357
|
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
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
|
-
|
|
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
|
}
|