@real-router/core 0.77.1 → 0.77.2

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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"Router-_5_uB35f.mjs","names":["#r","#i","#a","#e","#n","#t","#o","DEFAULT_QUERY_PARAMS","#options","#frozenState","#previousState","#deps","PLUGINS_CONST","EVENTS_CONST","#plugins","#unsubscribes","#deps","#startPlugin","#deduplicateBatch","#definitionActivateFactories","#externalActivateFactories","#definitionDeactivateFactories","#externalDeactivateFactories","#canDeactivateFunctions","#canActivateFunctions","#functionsTuple","#deps","#getFactoryMaps","#registerHandler","#clearGuard","#recompileSlot","#checkGuardSync","createRouteTree","createMatcher","routeTreeToDefinitions","#store","#deps","#getBuildPathOptions","#mergeDefaultParams","#resolveDynamicForward","#cachedBuildPathOpts","#cachedOptionsSource","#deps","#executeNavigation","#abortPreviousNavigation","#currentController","#navigationId","#handleNoGuardsLeave","#finishAsyncNavigation","#cleanupController","#handleNavigateError","#deps","#fsm","#emitter","#onListenerError","#abortController","#leaveListeners","#currentToState","#setupFSMActions","#dispatchDepth","#pendingToState","#pendingFromState","#pendingError","#pendingCancelReason","#checkListenerThreshold","#getValidator","#emitPendingError","#options","#limits","#dependenciesStore","#state","#routes","#routeLifecycle","#plugins","#navigation","#lifecycle","#eventBus","EventEmitter","#assertNotReentrant","#suppressUnhandledRejection","#unwindFailedStart","#onSuppressedStartError","#markDisposed","#isExpectedRejection","#onSuppressedNavigateError"],"sources":["../../../event-emitter/dist/esm/index.mjs","../../src/constants.ts","../../src/fsm/routerFSM.ts","../../src/guards.ts","../../src/helpers.ts","../../src/namespaces/DependenciesNamespace/dependenciesStore.ts","../../src/namespaces/OptionsNamespace/constants.ts","../../src/namespaces/OptionsNamespace/helpers.ts","../../src/namespaces/OptionsNamespace/validators.ts","../../src/namespaces/OptionsNamespace/OptionsNamespace.ts","../../src/namespaces/StateNamespace/helpers.ts","../../src/stateMetaStore.ts","../../src/namespaces/StateNamespace/StateNamespace.ts","../../src/namespaces/PluginsNamespace/constants.ts","../../src/namespaces/PluginsNamespace/validators.ts","../../src/namespaces/PluginsNamespace/PluginsNamespace.ts","../../src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts","../../src/namespaces/RoutesNamespace/constants.ts","../../src/namespaces/RoutesNamespace/helpers.ts","../../src/namespaces/RoutesNamespace/forwardChain.ts","../../src/namespaces/RoutesNamespace/routesStore.ts","../../src/transitionPath.ts","../../src/namespaces/RoutesNamespace/RoutesNamespace.ts","../../src/RouterError.ts","../../src/namespaces/NavigationNamespace/constants.ts","../../src/namespaces/NavigationNamespace/transition/completeTransition.ts","../../src/namespaces/NavigationNamespace/transition/errorHandling.ts","../../src/namespaces/NavigationNamespace/transition/guardPhase.ts","../../src/namespaces/NavigationNamespace/NavigationNamespace.ts","../../src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts","../../src/namespaces/EventBusNamespace/EventBusNamespace.ts","../../src/namespaces/RouterLifecycleNamespace/constants.ts","../../src/typeGuards.ts","../../src/wiring/wireNamespaces.ts","../../src/Router.ts"],"sourcesContent":["const e={maxListeners:0,warnListeners:0};var t=class{#e=new Map;#t=new Set;#n=null;#r=e;#i;#a;constructor(e){e?.limits&&(this.#r=e.limits),this.#i=e?.onListenerError??null,this.#a=e?.onListenerWarn??null}static validateCallback(e,t){if(typeof e!=`function`)throw TypeError(`Expected callback to be a function for event ${t}`)}setLimits(e){this.#r=e}on(e,t){let n=this.#e.get(e),r=n?.size??0;if(n?.has(t))throw Error(`Duplicate listener for \"${e}\"`);let{maxListeners:i,warnListeners:a}=this.#r;if(i!==0&&r>=i)throw Error(`Listener limit (${i}) reached for \"${e}\"`);a!==0&&r===a&&this.#a!==null&&(this.#n??=new Set,this.#n.has(e)||(this.#a(e,a),this.#n.add(e)));let o=n;return o===void 0&&(o=new Set,this.#e.set(e,o)),o.add(t),()=>{this.off(e,t)}}off(e,t){let n=this.#e.get(e);n&&(n.delete(t),n.size===0&&(this.#e.delete(e),this.#n?.delete(e)))}emit(e,t,n,r,i){let a=this.#e.get(e);if(!a||a.size===0||this.#t.has(e))return;let o=arguments.length-1;this.#t.add(e);try{if(a.size===1){let[s]=a;try{this.#o(s,o,t,n,r,i)}catch(t){this.#i?.(e,t)}}else{let s=[...a];for(let a of s)try{this.#o(a,o,t,n,r,i)}catch(t){this.#i?.(e,t)}}}finally{this.#t.delete(e)}}clearAll(){this.#e.clear(),this.#n=null}listenerCount(e){return this.#e.get(e)?.size??0}isDispatching(e){return this.#t.has(e)}#o(e,t,n,r,i,a){switch(t){case 0:e();break;case 1:e(n);break;case 2:e(n,r);break;case 3:e(n,r,i);break;default:e(n,r,i,a)}}};export{t as EventEmitter};\n//# sourceMappingURL=index.mjs.map","// packages/core/src/constants.ts\n\nimport type {\n EventToNameMap,\n EventToPluginMap,\n ErrorCodeToValueMap,\n ErrorCodeKeys,\n ErrorCodeValues,\n TransitionMeta,\n} from \"@real-router/types\";\n\nexport type ConstantsKeys = \"UNKNOWN_ROUTE\";\n\nexport type Constants = Record<ConstantsKeys, string>;\n\n// =============================================================================\n// Error Codes (migrated from router-error)\n// =============================================================================\n\nexport type ErrorCodes = Record<ErrorCodeKeys, ErrorCodeValues>;\n\n/**\n * Error codes for router operations.\n * Used to identify specific failure scenarios in navigation and lifecycle.\n * Frozen to prevent accidental modifications.\n */\nexport const errorCodes: ErrorCodeToValueMap = Object.freeze({\n ROUTER_NOT_STARTED: \"NOT_STARTED\", // navigate() called before start()\n NO_START_PATH_OR_STATE: \"NO_START_PATH_OR_STATE\", // start() without initial route\n ROUTER_ALREADY_STARTED: \"ALREADY_STARTED\", // start() called twice\n ROUTE_NOT_FOUND: \"ROUTE_NOT_FOUND\", // Navigation to non-existent route\n SAME_STATES: \"SAME_STATES\", // Navigate to current route without reload\n CANNOT_DEACTIVATE: \"CANNOT_DEACTIVATE\", // canDeactivate guard blocked navigation\n CANNOT_ACTIVATE: \"CANNOT_ACTIVATE\", // canActivate guard blocked navigation\n TRANSITION_ERR: \"TRANSITION_ERR\", // Generic transition failure\n TRANSITION_CANCELLED: \"CANCELLED\", // Navigation cancelled by user or new navigation\n ROUTER_DISPOSED: \"DISPOSED\", // Router has been disposed\n PLUGIN_CONFLICT: \"PLUGIN_CONFLICT\", // Plugin tried to extend router with already-existing property\n CONTEXT_NAMESPACE_ALREADY_CLAIMED: \"CONTEXT_NAMESPACE_ALREADY_CLAIMED\", // Plugin tried to claim a context namespace already owned by another plugin\n REENTRANT_NAVIGATION: \"REENTRANT_NAVIGATION\", // navigate() called synchronously from inside a transition-event listener (banned — use await/async listener)\n REENTRANT_TREE_MUTATION: \"REENTRANT_TREE_MUTATION\", // route-CRUD called synchronously from inside a subscribeChanges handler (banned — use await/async/queueMicrotask)\n});\n\n/**\n * General router constants.\n * Special route names and identifiers.\n */\nexport const UNKNOWN_ROUTE = \"@@router/UNKNOWN_ROUTE\";\n\nexport const constants: Constants = {\n UNKNOWN_ROUTE,\n};\n\n/**\n * Plugin method names.\n * Maps to methods that plugins can implement to hook into router lifecycle.\n */\nexport const plugins: EventToPluginMap = {\n ROUTER_START: \"onStart\", // Plugin method called when router starts\n ROUTER_STOP: \"onStop\", // Plugin method called when router stops\n TRANSITION_START: \"onTransitionStart\", // Plugin method called when navigation begins\n TRANSITION_LEAVE_APPROVE: \"onTransitionLeaveApprove\", // Plugin method called when deactivation guards pass\n TRANSITION_CANCEL: \"onTransitionCancel\", // Plugin method called when navigation cancelled\n TRANSITION_SUCCESS: \"onTransitionSuccess\", // Plugin method called when navigation succeeds\n TRANSITION_ERROR: \"onTransitionError\", // Plugin method called when navigation fails\n};\n\n/**\n * Event names for router event system.\n * Used with addEventListener/removeEventListener for reactive subscriptions.\n */\nexport const events: EventToNameMap = {\n ROUTER_START: \"$start\", // Emitted when router.start() succeeds\n ROUTER_STOP: \"$stop\", // Emitted when router.stop() is called\n TRANSITION_START: \"$$start\", // Emitted when navigation begins\n TRANSITION_LEAVE_APPROVE: \"$$leaveApprove\", // Emitted when deactivation guards pass\n TRANSITION_CANCEL: \"$$cancel\", // Emitted when navigation is cancelled\n TRANSITION_SUCCESS: \"$$success\", // Emitted when navigation completes successfully\n TRANSITION_ERROR: \"$$error\", // Emitted when navigation fails\n};\n\nexport const DEFAULT_LIMITS = {\n maxDependencies: 100,\n maxPlugins: 50,\n maxListeners: 10_000,\n warnListeners: 1000,\n maxLifecycleHandlers: 200,\n} as const;\n\nexport const EMPTY_PARAMS: Readonly<Record<string, never>> = Object.freeze({});\n\nconst FROZEN_EMPTY_SEGMENTS = Object.freeze({\n deactivated: Object.freeze([]) as unknown as string[],\n activated: Object.freeze([]) as unknown as string[],\n intersection: \"\",\n});\n\nexport const DEFAULT_TRANSITION = Object.freeze({\n phase: \"activating\",\n reason: \"success\",\n segments: FROZEN_EMPTY_SEGMENTS,\n}) as TransitionMeta;\n","// packages/core/src/fsm/routerFSM.ts\n\nimport { FSM } from \"@real-router/fsm\";\n\nimport type { FSMConfig } from \"@real-router/fsm\";\nimport type { NavigationOptions, State } from \"@real-router/types\";\n\n/**\n * Router FSM states.\n *\n * - IDLE: Router not started or stopped\n * - STARTING: Router is initializing\n * - READY: Router is ready for navigation\n * - TRANSITION_STARTED: Navigation in progress (before deactivation guards)\n * - LEAVE_APPROVED: Deactivation guards passed, activation guards pending\n * - DISPOSED: Router has been disposed (R2+)\n */\nexport const routerStates = {\n IDLE: \"IDLE\",\n STARTING: \"STARTING\",\n READY: \"READY\",\n TRANSITION_STARTED: \"TRANSITION_STARTED\",\n LEAVE_APPROVED: \"LEAVE_APPROVED\",\n DISPOSED: \"DISPOSED\",\n} as const;\n\nexport type RouterState = (typeof routerStates)[keyof typeof routerStates];\n\n/**\n * Router FSM events.\n *\n * - START: Begin router initialization\n * - STARTED: Router initialization complete\n * - NAVIGATE: Begin navigation\n * - COMPLETE: Navigation completed successfully\n * - FAIL: Navigation or initialization failed\n * - CANCEL: Navigation cancelled\n * - STOP: Stop router\n * - DISPOSE: Dispose router (R2+)\n */\nexport const routerEvents = {\n START: \"START\",\n STARTED: \"STARTED\",\n NAVIGATE: \"NAVIGATE\",\n LEAVE_APPROVE: \"LEAVE_APPROVE\",\n COMPLETE: \"COMPLETE\",\n FAIL: \"FAIL\",\n CANCEL: \"CANCEL\",\n STOP: \"STOP\",\n DISPOSE: \"DISPOSE\",\n} as const;\n\nexport type RouterEvent = (typeof routerEvents)[keyof typeof routerEvents];\n\n/**\n * Per-event payloads for the router FSM (#1169 commit-gate). The three hot\n * navigation transitions carry their transition states so the FSM action\n * dispatched by `send()` emits the matching transition event — i.e. events are\n * literal consequences of FSM transitions (no `forceState` + manual emit). See\n * `EventBusNamespace.#setupFSMActions`.\n */\nexport interface RouterPayloads {\n NAVIGATE: { toState: State; fromState?: State | undefined };\n LEAVE_APPROVE: { toState: State; fromState?: State | undefined };\n COMPLETE: {\n toState: State;\n fromState?: State | undefined;\n opts?: NavigationOptions | undefined;\n };\n}\n\n/**\n * Router FSM configuration.\n *\n * Transitions:\n * - IDLE → STARTING (START), DISPOSED (DISPOSE)\n * - STARTING → READY (STARTED), IDLE (FAIL, STOP), DISPOSED (DISPOSE)\n * - READY → TRANSITION_STARTED (NAVIGATE), READY (FAIL, self-loop for early validation errors), IDLE (STOP), DISPOSED (DISPOSE)\n * - TRANSITION_STARTED → LEAVE_APPROVED (LEAVE_APPROVE), TRANSITION_STARTED (NAVIGATE, self-loop), READY (CANCEL, FAIL), DISPOSED (DISPOSE)\n * - LEAVE_APPROVED → READY (COMPLETE, CANCEL, FAIL), TRANSITION_STARTED (NAVIGATE), DISPOSED (DISPOSE)\n * - DISPOSED → (no transitions)\n *\n * DISPOSE is wired from every non-DISPOSED state so `router.dispose()` always\n * settles the FSM at DISPOSED. The facade orchestrates cleanup through IDLE\n * for healthy flows; the direct transitions guarantee the FSM is not left\n * stuck if cleanup is skipped (e.g. dispose mid-STARTING when the start\n * pipeline threw before STARTED/FAIL).\n */\nconst routerFSMConfig: FSMConfig<RouterState, RouterEvent, null> = {\n initial: routerStates.IDLE,\n context: null,\n transitions: {\n [routerStates.IDLE]: {\n [routerEvents.START]: routerStates.STARTING,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.STARTING]: {\n [routerEvents.STARTED]: routerStates.READY,\n [routerEvents.FAIL]: routerStates.IDLE,\n [routerEvents.STOP]: routerStates.IDLE,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.READY]: {\n [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,\n [routerEvents.FAIL]: routerStates.READY,\n [routerEvents.STOP]: routerStates.IDLE,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.TRANSITION_STARTED]: {\n [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,\n [routerEvents.LEAVE_APPROVE]: routerStates.LEAVE_APPROVED,\n [routerEvents.CANCEL]: routerStates.READY,\n [routerEvents.FAIL]: routerStates.READY,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.LEAVE_APPROVED]: {\n [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,\n [routerEvents.COMPLETE]: routerStates.READY,\n [routerEvents.CANCEL]: routerStates.READY,\n [routerEvents.FAIL]: routerStates.READY,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.DISPOSED]: {},\n },\n};\n\n/**\n * Factory function to create a router FSM instance.\n *\n * @returns FSM instance with initial state \"IDLE\"\n */\nexport function createRouterFSM(): FSM<\n RouterState,\n RouterEvent,\n null,\n RouterPayloads\n> {\n return new FSM<RouterState, RouterEvent, null, RouterPayloads>(\n routerFSMConfig,\n );\n}\n","// packages/core/src/guards.ts\n\nimport type { Route } from \"./types\";\nimport type { RouterValidator } from \"./types/RouterValidator\";\n\nexport function guardDependencies(deps: unknown): void {\n if (\n !deps ||\n typeof deps !== \"object\" ||\n (deps as { constructor: unknown }).constructor !== Object\n ) {\n throw new TypeError(\"dependencies must be a plain object\");\n }\n for (const key in deps as Record<string, unknown>) {\n if (Object.getOwnPropertyDescriptor(deps, key)?.get) {\n throw new TypeError(`dependencies cannot contain getters: \"${key}\"`);\n }\n }\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- accepts any Route type */\nexport function guardRouteStructure(\n routes: Route<any>[],\n validator?: RouterValidator | null,\n): void {\n /* eslint-enable @typescript-eslint/no-explicit-any */\n for (const route of routes) {\n const routeValue: unknown = route;\n\n if (\n routeValue === null ||\n typeof routeValue !== \"object\" ||\n Array.isArray(routeValue)\n ) {\n throw new TypeError(\"route must be a non-array object\");\n }\n\n validator?.routes.guardRouteCallbacks(route as Route);\n validator?.routes.guardNoAsyncCallbacks(route as Route);\n const children = (route as Route).children;\n\n if (children) {\n guardRouteStructure(children, validator);\n }\n }\n}\n","// packages/core/src/helpers.ts\n\nimport { DEFAULT_LIMITS, EMPTY_PARAMS } from \"./constants\";\n\nimport type { Limits } from \"./types\";\nimport type { Params, State, LimitsConfig } from \"@real-router/types\";\n\n// =============================================================================\n// State Helpers\n// =============================================================================\n\n/**\n * Shallow-freezes a State object in place.\n *\n * Freezes only the top-level State object (blocks reassignment of `name`,\n * `params`, `path`, `transition`, `context`). Nested objects (`params`,\n * `transition`, `transition.segments`, `transition.segments.{deactivated,activated}`)\n * are expected to be **already frozen at creation time** by their producers:\n *\n * - `params` frozen in `makeState()` / `navigateToNotFound()`\n * - `transition`, `segments`, `deactivated`, `activated` frozen in\n * `buildTransitionMeta()` (or inline in `navigateToNotFound()`)\n *\n * `state.context` is **intentionally not frozen** — plugins write to it via\n * `claim.write(state, value)` after state creation.\n *\n * @internal\n */\nexport function freezeStateInPlace<T extends State>(state: T): T {\n // `Object.freeze` returns non-objects (incl. null/undefined) unchanged, so the\n // former `if (!state) return state` guard was redundant — callers also gate it\n // (`state ? freezeStateInPlace(state) : undefined`) and `T extends State` is\n // typed non-null.\n return Object.freeze(state);\n}\n\n/**\n * Merges user limits with defaults.\n * Returns frozen object for immutability.\n */\nexport function createLimits(userLimits: Partial<LimitsConfig> = {}): Limits {\n return { ...DEFAULT_LIMITS, ...userLimits };\n}\n\n// =============================================================================\n// Params Helpers\n// =============================================================================\n\n/**\n * Strips `undefined` values from a params object before handoff to the query\n * string engine and state storage.\n *\n * **Why this exists:** `router.navigate(name, { x: undefined })` must not put\n * `x` into the resulting URL (publicly documented contract). The underlying\n * query engine (`search-params`) already does this, but the contract belongs\n * to `@real-router/core` — this function guarantees it at the core boundary\n * so that:\n * - Plugin interceptors on `forwardState` that inject `undefined` values are\n * caught before they reach the engine\n * - `state.params` never contains `undefined` values (roundtrip consistent\n * with URL)\n * - The contract is verifiable at core's own test surface (doesn't depend on\n * engine behavior for regression detection)\n *\n * Single pass. When nothing survives (empty input, or every value `undefined`)\n * it returns the shared frozen `EMPTY_PARAMS` singleton, so `makeState`'s\n * `params === EMPTY_PARAMS` reuse branch fires and an empty-params navigation\n * allocates zero transient `{}` (#1027); a non-empty input returns a fresh\n * object. Either way reference identity is not preserved across calls, and the\n * result MUST be treated as read-only — callers must not mutate it (the empty\n * case is a shared frozen singleton).\n */\nexport function normalizeParams(params: Params): Params;\n\nexport function normalizeParams(params: undefined): undefined;\n\nexport function normalizeParams(params: Params | undefined): Params | undefined;\n\nexport function normalizeParams(\n params: Params | undefined,\n): Params | undefined {\n if (params === undefined) {\n return params;\n }\n\n let normalized: Params | undefined;\n\n for (const key in params) {\n if (!Object.hasOwn(params, key)) {\n continue;\n }\n\n const value = params[key];\n\n if (value !== undefined) {\n // Lazy allocation: an all-empty / all-undefined input costs zero objects.\n normalized ??= {};\n normalized[key] = value;\n }\n }\n\n // Reuse the shared singleton when nothing survived so makeState's\n // `params === EMPTY_PARAMS` reuse branch fires (#1027).\n return normalized ?? EMPTY_PARAMS;\n}\n","import { DEFAULT_LIMITS } from \"../../constants\";\n\nimport type { Limits } from \"../../types\";\nimport type { DefaultDependencies } from \"@real-router/types\";\n\nexport interface DependenciesStore<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n> {\n dependencies: Partial<Dependencies>;\n limits: Limits;\n}\n\nexport function createDependenciesStore<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n initialDependencies: Partial<Dependencies> = {},\n): DependenciesStore<Dependencies> {\n const dependencies = Object.create(null) as Partial<Dependencies>;\n\n for (const key in initialDependencies) {\n if (initialDependencies[key] !== undefined) {\n dependencies[key] = initialDependencies[key];\n }\n }\n\n return {\n dependencies,\n limits: DEFAULT_LIMITS,\n };\n}\n","// packages/core/src/namespaces/OptionsNamespace/constants.ts\n\nimport { DEFAULT_QUERY_PARAMS } from \"route-tree\";\n\nimport type { Options } from \"@real-router/types\";\n\n/**\n * Default options for the router.\n */\nexport const defaultOptions: Options = {\n defaultRoute: \"\",\n defaultParams: {},\n trailingSlash: \"preserve\",\n caseSensitive: true,\n queryParamsMode: \"loose\",\n queryParams: DEFAULT_QUERY_PARAMS,\n urlParamsEncoding: \"default\",\n allowNotFound: true,\n rewritePathOnMatch: true,\n} satisfies Options;\n","// packages/core/src/namespaces/OptionsNamespace/helpers.ts\n\nimport type { Options, Params } from \"@real-router/types\";\n\n/**\n * Recursively freezes an object and all nested objects.\n * Only freezes plain objects, not primitives or special objects.\n */\nexport function deepFreeze<T extends object>(obj: T): Readonly<T> {\n Object.freeze(obj);\n\n for (const value of Object.values(obj as Record<string, unknown>)) {\n if (value && typeof value === \"object\" && value.constructor === Object) {\n deepFreeze(value);\n }\n }\n\n return obj;\n}\n\n/**\n * Resolves an option value that can be static or a callback.\n * If the value is a function, calls it with getDependency and returns the result.\n * Otherwise, returns the value as-is.\n */\nexport function resolveOption(\n value: Options[\"defaultRoute\"],\n getDependency: (name: string) => unknown,\n): string;\n\nexport function resolveOption(\n value: Options[\"defaultParams\"],\n getDependency: (name: string) => unknown,\n): Params;\n\nexport function resolveOption(\n value: Options[\"defaultRoute\"] | Options[\"defaultParams\"],\n getDependency: (name: string) => unknown,\n): string | Params {\n if (typeof value === \"function\") {\n // Runtime getDependency is (name: string) => unknown, but DefaultRouteCallback<object>\n // expects <K extends keyof object>(name: K) => object[K] where keyof object = never.\n // Cast needed to bridge generic constraint mismatch.\n return value(getDependency as never);\n }\n\n return value;\n}\n","// packages/core/src/namespaces/OptionsNamespace/validators.ts\n\n/**\n * Minimal crash guard for options.\n * Full DX validation moved to @real-router/validation-plugin (retrospective pattern).\n */\nexport function validateOptionsIsObject(\n options: unknown,\n): asserts options is Record<string, unknown> {\n if (!options || typeof options !== \"object\" || Array.isArray(options)) {\n throw new TypeError(\"[router.constructor] options must be a plain object\");\n }\n}\n","// packages/core/src/namespaces/OptionsNamespace/OptionsNamespace.ts\n\nimport { defaultOptions } from \"./constants\";\nimport { deepFreeze } from \"./helpers\";\nimport { validateOptionsIsObject } from \"./validators\";\n\nimport type { Options } from \"@real-router/types\";\n\nexport class OptionsNamespace {\n readonly #options: Readonly<Options>;\n\n constructor(initialOptions: Partial<Options> = {}) {\n this.#options = deepFreeze({\n ...defaultOptions,\n ...initialOptions,\n });\n }\n\n static validateOptionsIsObject(\n options: unknown,\n ): asserts options is Record<string, unknown> {\n validateOptionsIsObject(options);\n }\n\n get(): Readonly<Options> {\n return this.#options;\n }\n}\n","// packages/core/src/namespaces/StateNamespace/helpers.ts\n\nexport function areParamValuesEqual(val1: unknown, val2: unknown): boolean {\n if (val1 === val2) {\n return true;\n }\n\n if (Array.isArray(val1) && Array.isArray(val2)) {\n if (val1.length !== val2.length) {\n return false;\n }\n\n // eslint-disable-next-line unicorn/no-for-loop -- hot path: for-of entries() allocates iterator per recursive call\n for (let i = 0; i < val1.length; i++) {\n if (!areParamValuesEqual(val1[i], val2[i])) {\n return false;\n }\n }\n\n return true;\n }\n\n return false;\n}\n","// packages/core/src/stateMetaStore.ts\n\nimport type { Params, State } from \"@real-router/types\";\n\nconst store = new WeakMap<State, Params>();\n\n/** @internal */\nexport function getStateMetaParams(state: State): Params | undefined {\n return store.get(state);\n}\n\n/** @internal */\nexport function setStateMetaParams(state: State, params: Params): void {\n store.set(state, params);\n}\n","// packages/core/src/namespaces/StateNamespace/StateNamespace.ts\n\nimport { areParamValuesEqual } from \"./helpers\";\nimport { DEFAULT_TRANSITION, EMPTY_PARAMS } from \"../../constants\";\nimport { freezeStateInPlace } from \"../../helpers\";\nimport { setStateMetaParams } from \"../../stateMetaStore\";\n\nimport type { StateNamespaceDependencies } from \"./types\";\nimport type { Params, State } from \"@real-router/types\";\nimport type { RouteTreeStateMeta } from \"route-tree\";\n\n/**\n * Independent namespace for managing router state storage and creation.\n *\n * Static methods handle validation (called by facade).\n * Instance methods handle state storage, freezing, and creation.\n */\nexport class StateNamespace {\n /**\n * Cached frozen state - avoids structuredClone on every getState() call.\n */\n #frozenState: State | undefined = undefined;\n\n /**\n * Previous state before the last setState call.\n */\n #previousState: State | undefined = undefined;\n\n /**\n * Dependencies injected from Router.\n */\n #deps!: StateNamespaceDependencies;\n\n // =========================================================================\n // Instance methods (trust input - already validated by facade)\n // =========================================================================\n\n /**\n * Returns the current router state.\n *\n * The returned state is deeply frozen (immutable) for safety.\n * Returns `undefined` if the router has not been started or has been stopped.\n */\n get<P extends Params = Params>(): State<P> | undefined {\n return this.#frozenState as State<P> | undefined; // NOSONAR -- generic narrowing needed for public API\n }\n\n /**\n * Sets the current router state.\n *\n * The state is deeply frozen before storage to ensure immutability.\n * The previous state is preserved and accessible via `getPrevious()`.\n *\n * @param state - Already validated by facade, or undefined to clear\n */\n set(state: State | undefined): void {\n // Preserve current state as previous before updating\n this.#previousState = this.#frozenState;\n\n // If state is already frozen (from makeState()), use it directly.\n // For external states, freeze in place without cloning.\n this.#frozenState = state ? freezeStateInPlace(state) : undefined;\n }\n\n /**\n * Returns the previous router state (before the last navigation).\n */\n getPrevious(): State | undefined {\n return this.#previousState;\n }\n\n reset(): void {\n this.#frozenState = undefined;\n this.#previousState = undefined;\n }\n\n // =========================================================================\n // Dependency Injection\n // =========================================================================\n\n /**\n * Sets dependencies for state creation methods.\n * Must be called before using makeState, areStatesEqual, etc.\n */\n setDependencies(deps: StateNamespaceDependencies): void {\n this.#deps = deps;\n }\n\n // =========================================================================\n // State Creation Methods\n // =========================================================================\n\n /**\n * Creates a state object for a route.\n *\n * `params` is frozen at creation so it is always immutable, even when\n * `skipFreeze=true` is passed to defer the outer `Object.freeze(state)` call.\n * This keeps params-freezing invariants independent of transition-pipeline\n * mutation (e.g. `completeTransition` attaching `state.transition`).\n *\n * `context` is initialized as a fresh empty object — intentionally NOT frozen\n * so plugins can publish data via `claim.write(state, value)` after creation.\n */\n makeState<P extends Params = Params>(\n name: string,\n params?: P,\n path?: string,\n meta?: RouteTreeStateMeta,\n skipFreeze?: boolean,\n ): State<P> {\n // Optimization: O(1) lookup instead of O(depth) ancestor iteration\n const defaultParamsConfig = this.#deps.getDefaultParams();\n const hasDefaultParams = Object.hasOwn(defaultParamsConfig, name);\n\n // Conditional allocation: avoid spreading when no defaultParams exist\n let mergedParams: P;\n\n if (hasDefaultParams) {\n mergedParams = Object.freeze({\n ...defaultParamsConfig[name],\n ...params,\n }) as P;\n } else if (!params || params === EMPTY_PARAMS) {\n mergedParams = EMPTY_PARAMS as P;\n } else {\n mergedParams = Object.freeze({ ...params });\n }\n\n const state = {\n name,\n params: mergedParams,\n path: path ?? this.#deps.buildPath(name, params),\n context: {},\n ...(!skipFreeze && { transition: DEFAULT_TRANSITION }),\n } as State<P>;\n\n if (meta) {\n setStateMetaParams(state, meta);\n }\n\n return skipFreeze ? state : freezeStateInPlace(state);\n }\n\n // =========================================================================\n // State Comparison Methods\n // =========================================================================\n\n /**\n * Compares two states for equality.\n * By default, ignores query params (only compares URL params).\n */\n areStatesEqual(\n state1: State | undefined,\n state2: State | undefined,\n ignoreQueryParams = true,\n ): boolean {\n if (!state1 || !state2) {\n return !!state1 === !!state2;\n }\n\n if (state1.name !== state2.name) {\n return false;\n }\n\n if (ignoreQueryParams) {\n // URL (path) param names are cached at the routes layer and invalidated\n // on every tree mutation, so this stays correct after replace() (#723).\n const urlParams = this.#deps.getUrlParams(state1.name);\n\n for (const urlParam of urlParams) {\n if (\n !areParamValuesEqual(state1.params[urlParam], state2.params[urlParam])\n ) {\n return false;\n }\n }\n\n return true;\n }\n\n const state1Keys = Object.keys(state1.params);\n const state2Keys = Object.keys(state2.params);\n\n if (state1Keys.length !== state2Keys.length) {\n return false;\n }\n\n for (const param of state1Keys) {\n if (\n !(param in state2.params) ||\n !areParamValuesEqual(state1.params[param], state2.params[param])\n ) {\n return false;\n }\n }\n\n return true;\n }\n}\n","// packages/core/src/namespaces/PluginsNamespace/constants.ts\n\nimport {\n events as EVENTS_CONST,\n plugins as PLUGINS_CONST,\n} from \"../../constants\";\n\nimport type { EventName } from \"@real-router/types\";\n\n/**\n * Maps plugin method names to router event names.\n */\nexport const EVENTS_MAP = {\n [PLUGINS_CONST.ROUTER_START]: EVENTS_CONST.ROUTER_START,\n [PLUGINS_CONST.ROUTER_STOP]: EVENTS_CONST.ROUTER_STOP,\n [PLUGINS_CONST.TRANSITION_SUCCESS]: EVENTS_CONST.TRANSITION_SUCCESS,\n [PLUGINS_CONST.TRANSITION_START]: EVENTS_CONST.TRANSITION_START,\n [PLUGINS_CONST.TRANSITION_LEAVE_APPROVE]:\n EVENTS_CONST.TRANSITION_LEAVE_APPROVE,\n [PLUGINS_CONST.TRANSITION_ERROR]: EVENTS_CONST.TRANSITION_ERROR,\n [PLUGINS_CONST.TRANSITION_CANCEL]: EVENTS_CONST.TRANSITION_CANCEL,\n} as const satisfies Record<\n (typeof PLUGINS_CONST)[keyof typeof PLUGINS_CONST],\n EventName\n>;\n\n/**\n * Plugin method names that correspond to router events.\n */\nexport const EVENT_METHOD_NAMES = Object.keys(\n EVENTS_MAP,\n) as (keyof typeof EVENTS_MAP)[];\n\nexport const LOGGER_CONTEXT = \"router.usePlugin\";\n","// packages/core/src/namespaces/PluginsNamespace/validators.ts\n\n/**\n * Static validation functions for PluginsNamespace.\n * Called by Router facade before instance methods.\n */\n\nimport type { Plugin } from \"@real-router/types\";\n\n/**\n * Validates that a plugin factory returned a valid plugin object.\n */\nexport function validatePlugin(plugin: Plugin): void {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!(plugin && typeof plugin === \"object\") || Array.isArray(plugin)) {\n throw new TypeError(\n `[router.usePlugin] Plugin factory must return an object, got ${typeof plugin}`,\n );\n }\n\n // Detect async factory (returns Promise)\n if (typeof (plugin as unknown as { then?: unknown }).then === \"function\") {\n throw new TypeError(\n `[router.usePlugin] Async plugin factories are not supported. ` +\n `Factory returned a Promise instead of a plugin object.`,\n );\n }\n}\n","// packages/core/src/namespaces/PluginsNamespace/PluginsNamespace.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport { EVENTS_MAP, EVENT_METHOD_NAMES, LOGGER_CONTEXT } from \"./constants\";\nimport { validatePlugin } from \"./validators\";\n\nimport type { PluginsDependencies } from \"./types\";\nimport type { PluginFactory } from \"../../types\";\nimport type {\n DefaultDependencies,\n Plugin,\n Unsubscribe,\n} from \"@real-router/types\";\n\n/**\n * Independent namespace for managing plugins.\n *\n * Static methods handle validation (called by facade).\n * Instance methods handle storage and business logic.\n */\nexport class PluginsNamespace<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n> {\n readonly #plugins = new Set<PluginFactory<Dependencies>>();\n readonly #unsubscribes = new Set<Unsubscribe>();\n\n #deps!: PluginsDependencies<Dependencies>;\n\n // =========================================================================\n // Static validation methods (called by facade before instance methods)\n // Proxy to functions in validators.ts for separation of concerns\n // =========================================================================\n\n static validatePlugin(plugin: Plugin): void {\n validatePlugin(plugin);\n }\n\n // =========================================================================\n // Dependency injection\n // =========================================================================\n\n setDependencies(deps: PluginsDependencies<Dependencies>): void {\n this.#deps = deps;\n }\n\n // =========================================================================\n // Instance methods (trust input - already validated by facade)\n // =========================================================================\n\n /**\n * Returns the number of registered plugins.\n * Used by facade for limit validation.\n */\n /* v8 ignore next 3 -- @preserve: only called via validator interface (ctx.validator?.plugins.validatePluginLimit), not reachable without validation plugin */\n count(): number {\n return this.#plugins.size;\n }\n\n /**\n * Registers one or more plugin factories.\n * Returns unsubscribe function to remove all added plugins.\n * Input already validated by facade (limit, duplicates).\n *\n * @param factories - Already validated by facade\n */\n use(...factories: PluginFactory<Dependencies>[]): Unsubscribe {\n // Emit warnings for count thresholds (not validation, just warnings)\n this.#deps\n .getValidator()\n ?.plugins.validateCountThresholds(this.#plugins.size + factories.length);\n\n // Fast path for single plugin (common case)\n if (factories.length === 1) {\n const factory = factories[0];\n const cleanup = this.#startPlugin(factory);\n\n this.#plugins.add(factory);\n\n let unsubscribed = false;\n\n const unsubscribe: Unsubscribe = () => {\n if (unsubscribed) {\n return;\n }\n\n unsubscribed = true;\n this.#plugins.delete(factory);\n this.#unsubscribes.delete(unsubscribe);\n try {\n cleanup();\n } catch (error) {\n logger.error(LOGGER_CONTEXT, \"Error during cleanup:\", error);\n }\n };\n\n this.#unsubscribes.add(unsubscribe);\n\n return unsubscribe;\n }\n\n // Deduplicate batch with warning (validation already done by facade)\n const seenInBatch = this.#deduplicateBatch(factories);\n\n // Track successfully initialized plugins for cleanup\n const initializedPlugins: {\n factory: PluginFactory<Dependencies>;\n cleanup: Unsubscribe;\n }[] = [];\n\n // Initialize deduplicated plugins sequentially\n try {\n for (const plugin of seenInBatch) {\n const cleanup = this.#startPlugin(plugin);\n\n initializedPlugins.push({ factory: plugin, cleanup });\n }\n } catch (error) {\n // Rollback on failure - cleanup all initialized plugins\n for (const { cleanup } of initializedPlugins) {\n try {\n cleanup();\n } catch (cleanupError) {\n logger.error(LOGGER_CONTEXT, \"Cleanup error:\", cleanupError);\n }\n }\n\n throw error;\n }\n\n // Commit phase - add to registry\n for (const { factory } of initializedPlugins) {\n this.#plugins.add(factory);\n }\n\n // Return unsubscribe function\n let unsubscribed = false;\n\n const unsubscribe: Unsubscribe = () => {\n if (unsubscribed) {\n return;\n }\n\n unsubscribed = true;\n this.#unsubscribes.delete(unsubscribe);\n\n for (const { factory } of initializedPlugins) {\n this.#plugins.delete(factory);\n }\n\n for (const { cleanup } of initializedPlugins) {\n try {\n cleanup();\n } catch (error) {\n logger.error(LOGGER_CONTEXT, \"Error during cleanup:\", error);\n }\n }\n };\n\n this.#unsubscribes.add(unsubscribe);\n\n return unsubscribe;\n }\n\n /**\n * Returns registered plugin factories.\n */\n getAll(): PluginFactory<Dependencies>[] {\n return [...this.#plugins];\n }\n\n /**\n * Checks if a plugin factory is registered.\n * Used internally by validation to avoid array allocation.\n */\n /* v8 ignore next 3 -- @preserve: only called via validator interface, not reachable without validation plugin */\n has(factory: PluginFactory<Dependencies>): boolean {\n return this.#plugins.has(factory);\n }\n\n /**\n * Disposes all registered plugins by running their teardown callbacks\n * and removing event listener subscriptions.\n * Active disposal is required because plugins have an active lifecycle\n * (event subscriptions, teardown hooks).\n * Named \"dispose\" (not \"clear\") because there is active cleanup to perform.\n */\n disposeAll(): void {\n for (const unsubscribe of this.#unsubscribes) {\n unsubscribe();\n }\n\n this.#plugins.clear();\n this.#unsubscribes.clear();\n }\n\n // =========================================================================\n // Private methods\n // =========================================================================\n\n /**\n * Deduplicates batch with warning for duplicates within batch.\n * Validation (existing duplicates) is done by facade.\n */\n #deduplicateBatch(\n plugins: PluginFactory<Dependencies>[],\n ): Set<PluginFactory<Dependencies>> {\n const seenInBatch = new Set<PluginFactory<Dependencies>>();\n\n for (const plugin of plugins) {\n if (seenInBatch.has(plugin)) {\n this.#deps.getValidator()?.plugins.warnBatchDuplicates(plugins);\n } else {\n seenInBatch.add(plugin);\n }\n }\n\n return seenInBatch;\n }\n\n #startPlugin(pluginFactory: PluginFactory<Dependencies>): Unsubscribe {\n const appliedPlugin = this.#deps.compileFactory(pluginFactory);\n\n PluginsNamespace.validatePlugin(appliedPlugin);\n this.#deps.getValidator()?.plugins.validatePluginKeys(appliedPlugin);\n\n Object.freeze(appliedPlugin);\n\n // Collect all unsubscribe functions\n const removeEventListeners: Unsubscribe[] = [];\n\n // Subscribe plugin methods to corresponding router events\n for (const methodName of EVENT_METHOD_NAMES) {\n if (methodName in appliedPlugin) {\n if (typeof appliedPlugin[methodName] === \"function\") {\n removeEventListeners.push(\n this.#deps.addEventListener(\n EVENTS_MAP[methodName],\n appliedPlugin[methodName],\n ),\n );\n\n if (methodName === \"onStart\" && this.#deps.canNavigate()) {\n this.#deps.getValidator()?.plugins.warnPluginAfterStart(methodName);\n }\n } else {\n this.#deps.getValidator()?.plugins.warnPluginMethodType(methodName);\n }\n }\n }\n\n // Return composite cleanup function\n return () => {\n for (const removeListener of removeEventListeners) {\n removeListener();\n }\n\n if (typeof appliedPlugin.teardown === \"function\") {\n appliedPlugin.teardown();\n }\n };\n }\n}\n","// packages/core/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport type { RouteLifecycleDependencies } from \"./types\";\nimport type { GuardFnFactory } from \"../../types\";\nimport type { DefaultDependencies, GuardFn, State } from \"@real-router/types\";\n\n// Boolean shorthand has only two possible values, so the guard and its factory\n// are module-level singletons — registering `true`/`false` reuses one cached\n// factory instead of allocating a fresh closure per call (#962).\nconst TRUE_GUARD: GuardFn = () => true;\nconst FALSE_GUARD: GuardFn = () => false;\nconst TRUE_FACTORY: GuardFnFactory = () => TRUE_GUARD;\nconst FALSE_FACTORY: GuardFnFactory = () => FALSE_GUARD;\n\n/**\n * Converts a boolean value to a guard function factory.\n * Used for the shorthand syntax where true/false is passed instead of a function.\n * Returns one of two cached factories — no per-call allocation (#962).\n */\nfunction booleanToFactory<Dependencies extends DefaultDependencies>(\n value: boolean,\n): GuardFnFactory<Dependencies> {\n return value ? TRUE_FACTORY : FALSE_FACTORY;\n}\n\n/**\n * Origin lane for a guard clear. Every `clearCanActivate` / `clearCanDeactivate`\n * caller names its lane — there is no origin-blind default — so a new call site\n * cannot silently wipe both the route-config and the external guard (#1171):\n *\n * - `\"definition\"` — clear only the route-config guard (`update(name, {…: null})`, #952).\n * - `\"external\"` — clear only the external, component-managed guard\n * (`removeXGuard()` and post-leave auto-cleanup — the inverse of `addXGuard()`).\n * - `\"both\"` — clear both (route removal / router teardown; the route is gone).\n */\nexport type GuardClearScope = \"definition\" | \"external\" | \"both\";\n\n/**\n * Source of truth for `canActivate` / `canDeactivate` guards.\n *\n * Storage is split by origin into four factory Maps (definition vs external,\n * each ×activate/deactivate); a single compiled-function Map per kind backs\n * navigation (\"external wins\" — when a route holds both a definition and an\n * external guard, the compiled guard is the external one, regardless of\n * registration order; #1174). `getFunctions()` returns a cached\n * `[deactivate, activate]` tuple for the hot path (stable reference, no\n * per-navigate allocation).\n *\n * All input validation is handled upstream by `getLifecycleApi` and\n * `getRoutesApi` — this class has no static methods.\n *\n * **Ordering convention.** Every paired surface lists **deactivate before\n * activate**: `getFunctions()` / `getFactories()` return `[deactivate,\n * activate]`, `getFactoriesByOrigin()` returns that tuple per origin, and\n * `canNavigateTo(toDeactivate, toActivate, …)` takes deactivate first. Keep\n * any new paired surface consistent with this order.\n */\nexport class RouteLifecycleNamespace<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n> {\n // Storage split by origin: definition vs external. Resolution is\n // EXTERNAL-WINS regardless of registration order (#1174): the compiled slot\n // reflects the external factory whenever one exists, else the definition. One\n // policy across every path — `#registerHandler` (keeps external over a later\n // definition), `#recompileSlot`, and `clearDefinitionGuards` (#1192) — so a\n // clone's fixed definition→external replay yields the source's effective guard\n // with no extra tracking. Both semantics are expressed over these primary Maps.\n readonly #definitionActivateFactories = new Map<\n string,\n GuardFnFactory<Dependencies>\n >();\n readonly #externalActivateFactories = new Map<\n string,\n GuardFnFactory<Dependencies>\n >();\n readonly #definitionDeactivateFactories = new Map<\n string,\n GuardFnFactory<Dependencies>\n >();\n readonly #externalDeactivateFactories = new Map<\n string,\n GuardFnFactory<Dependencies>\n >();\n // Compiled-function view. Single Map per kind because navigation does not\n // distinguish origin — it just runs the effective guard. Set on add\n // (external-wins — a definition does not overwrite a live external, #1174)\n // and recompiled on clear from whichever origin Map still holds the slot.\n readonly #canDeactivateFunctions = new Map<string, GuardFn>();\n readonly #canActivateFunctions = new Map<string, GuardFn>();\n // Cached tuple — Maps never change reference, so this is stable\n readonly #functionsTuple: [Map<string, GuardFn>, Map<string, GuardFn>] = [\n this.#canDeactivateFunctions,\n this.#canActivateFunctions,\n ];\n\n #deps!: RouteLifecycleDependencies<Dependencies>;\n\n setDependencies(deps: RouteLifecycleDependencies<Dependencies>): void {\n this.#deps = deps;\n }\n\n getHandlerCount(type: \"activate\" | \"deactivate\"): number {\n const definitionMap =\n type === \"activate\"\n ? this.#definitionActivateFactories\n : this.#definitionDeactivateFactories;\n const externalMap =\n type === \"activate\"\n ? this.#externalActivateFactories\n : this.#externalDeactivateFactories;\n\n if (definitionMap.size === 0) {\n return externalMap.size;\n }\n\n if (externalMap.size === 0) {\n return definitionMap.size;\n }\n\n const names = new Set(definitionMap.keys());\n\n for (const name of externalMap.keys()) {\n names.add(name);\n }\n\n return names.size;\n }\n\n /**\n * Pre-flights the #961 handler-limit `RangeError` into the route-CRUD PREPARE\n * phase (#1046). `#registerHandler`'s per-slot limit check throws AFTER the\n * tree/config swap, so `add`/`replace`/`update` tore post-commit when the\n * validator was installed and the per-type count was at `maxLifecycleHandlers`.\n * Running the same check here — before any store mutation — restores atomicity\n * (#951/#956/#698): a batch that would exceed the limit aborts before a single\n * write.\n *\n * Only NEW slots count (an overwrite leaves the union count unchanged, mirroring\n * `#registerHandler`). For `replace` (`clearsDefinition = true`) the definition\n * guards are about to be cleared, so the projection runs against the surviving\n * EXTERNAL guards only — exactly the post-clear state the install loop sees.\n * Plugin-gated: a no-op without the validator (the limit is opt-in).\n *\n * @param activateNames - route names a `canActivate` would be registered for\n * @param deactivateNames - route names a `canDeactivate` would be registered for\n * @param clearsDefinition - true for `replace` (definition guards cleared first)\n */\n preflightHandlerLimit(\n activateNames: Iterable<string>,\n deactivateNames: Iterable<string>,\n clearsDefinition: boolean,\n ): void {\n const validator = this.#deps.getValidator();\n\n if (!validator) {\n return;\n }\n\n const check = (\n type: \"activate\" | \"deactivate\",\n names: Iterable<string>,\n methodName: string,\n ): void => {\n const { definition, external } = this.#getFactoryMaps(type);\n\n // A name already holding a guard of this type is an overwrite (no new\n // slot, mirroring `#registerHandler`). After a definition-clear (replace)\n // only EXTERNAL guards survive, so the existing-name check — and the base\n // count below — run against `external` alone, matching the post-clear\n // install state the loop sees.\n let newSlots = 0;\n\n for (const name of names) {\n const isExisting = clearsDefinition\n ? external.has(name)\n : definition.has(name) || external.has(name);\n\n if (!isExisting) {\n newSlots++;\n }\n }\n\n if (newSlots === 0) {\n return;\n }\n\n // The install loop throws when a new-slot registration observes\n // `count >= max`. Starting from `base` and adding `newSlots` new names,\n // the highest pre-register count it reaches is `base + newSlots - 1` —\n // replicate that worst case so the pre-flight throws iff the loop would.\n const base = clearsDefinition\n ? external.size\n : this.getHandlerCount(type);\n\n validator.lifecycle.validateHandlerLimit(base + newSlots - 1, methodName);\n };\n\n check(\"activate\", activateNames, \"canActivate\");\n check(\"deactivate\", deactivateNames, \"canDeactivate\");\n }\n\n // =========================================================================\n // Instance methods\n // =========================================================================\n\n /**\n * Adds a canActivate guard for a route.\n *\n * @param name - Route name (input-validated by facade)\n * @param handler - Guard function or boolean (input-validated by facade)\n * @param isFromDefinition - True when guard comes from route definition\n * (lands in the definition Map; subject to `clearDefinitionGuards()`).\n * False (default) when added via `getLifecycleApi().addActivateGuard(...)`\n * (lands in the external Map; survives `replace()`).\n *\n * External wins at runtime (#1174): when a route holds both a definition and\n * an external guard, the compiled function is the external one, regardless of\n * registration order. Within one origin the most recent add overwrites. Origin\n * determines which Map the factory is filed under (relevant for\n * `clearDefinitionGuards()` and `cloneRouter` re-registration).\n */\n addCanActivate(\n name: string,\n handler: GuardFnFactory<Dependencies> | boolean,\n isFromDefinition = false,\n precompiledFn?: GuardFn,\n ): void {\n this.#registerHandler(\n \"activate\",\n name,\n handler,\n isFromDefinition,\n \"canActivate\",\n precompiledFn,\n );\n }\n\n /**\n * Adds a canDeactivate guard for a route.\n *\n * Symmetric counterpart to {@link addCanActivate}.\n */\n addCanDeactivate(\n name: string,\n handler: GuardFnFactory<Dependencies> | boolean,\n isFromDefinition = false,\n precompiledFn?: GuardFn,\n ): void {\n this.#registerHandler(\n \"deactivate\",\n name,\n handler,\n isFromDefinition,\n \"canDeactivate\",\n precompiledFn,\n );\n }\n\n /**\n * Removes a canActivate guard for a route. `scope` names the origin lane\n * (see {@link GuardClearScope}) — there is no origin-blind default, so every\n * caller commits to a lane and a new call site cannot silently clear both.\n * Delegates to {@link #clearGuard} (mirrors the add side's `#registerHandler`).\n *\n * @param name - Route name (already validated by facade)\n * @param scope - Which origin(s) to clear: `\"definition\"` / `\"external\"` / `\"both\"`\n */\n clearCanActivate(name: string, scope: GuardClearScope): void {\n this.#clearGuard(\"activate\", name, scope);\n }\n\n /**\n * Removes a canDeactivate guard for a route. Symmetric counterpart to\n * {@link clearCanActivate}.\n *\n * The `\"external\"` lane is what makes a route-config `canDeactivate` durable:\n * post-leave auto-cleanup (`completeTransition`) and `removeDeactivateGuard()`\n * unregister only the external, component-managed guard (router5 mount/unmount\n * heritage), while a definition guard survives for re-entry — symmetric with\n * definition `canActivate`, which lives as long as the route is in the tree\n * (#1171). Clearing both by default made a config guard one-shot: the first\n * permitted leave erased it, so re-entry was unguarded, `getRoutesApi().get()`\n * lost the field, and a clone taken after the leave never received it\n * (clone invariant #6).\n *\n * @param name - Route name (already validated by facade)\n * @param scope - Which origin(s) to clear: `\"definition\"` / `\"external\"` / `\"both\"`\n */\n clearCanDeactivate(name: string, scope: GuardClearScope): void {\n this.#clearGuard(\"deactivate\", name, scope);\n }\n\n /**\n * Clears all lifecycle handlers (canActivate and canDeactivate).\n * Used by clearRoutes to reset all lifecycle state.\n */\n clearAll(): void {\n this.#definitionActivateFactories.clear();\n this.#externalActivateFactories.clear();\n this.#definitionDeactivateFactories.clear();\n this.#externalDeactivateFactories.clear();\n this.#canActivateFunctions.clear();\n this.#canDeactivateFunctions.clear();\n }\n\n /**\n * Clears only lifecycle handlers that were registered from route definitions.\n * Used by HMR `replace()` to remove definition-sourced guards without\n * touching externally-added guards.\n *\n * For a slot where BOTH a definition and an external guard exist, the external\n * factory survives — and the compiled function is RECOMPILED from it (#1192).\n * Under external-wins (#1174) the compiled slot is already the external guard,\n * so this recompile is idempotent — it re-derives the surviving external factory\n * through the same choke point that keeps clearing correct (and stays robust if\n * the compiled slot were ever out of sync). For a definition-only slot, the\n * compiled function is dropped.\n */\n clearDefinitionGuards(): void {\n for (const name of this.#definitionActivateFactories.keys()) {\n if (this.#externalActivateFactories.has(name)) {\n this.#recompileSlot(\"activate\", name);\n } else {\n this.#canActivateFunctions.delete(name);\n }\n }\n\n for (const name of this.#definitionDeactivateFactories.keys()) {\n if (this.#externalDeactivateFactories.has(name)) {\n this.#recompileSlot(\"deactivate\", name);\n } else {\n this.#canDeactivateFunctions.delete(name);\n }\n }\n\n this.#definitionActivateFactories.clear();\n this.#definitionDeactivateFactories.clear();\n }\n\n /**\n * Returns lifecycle factories as a flat `[deactivate, activate]` tuple of\n * `Record<name, factory>` — the effective view where external wins over\n * definition for the same slot. Used by `getRoutesApi` to enrich route\n * objects with their current canActivate / canDeactivate factories and by\n * the route-removal cleanup path.\n *\n * For cloneRouter (which needs to preserve origin on re-registration), use\n * {@link getFactoriesByOrigin} instead.\n */\n getFactories(): [\n Record<string, GuardFnFactory<Dependencies>>,\n Record<string, GuardFnFactory<Dependencies>>,\n ] {\n const deactivateRecord: Record<string, GuardFnFactory<Dependencies>> = {};\n const activateRecord: Record<string, GuardFnFactory<Dependencies>> = {};\n\n for (const [name, factory] of this.#definitionDeactivateFactories) {\n deactivateRecord[name] = factory;\n }\n for (const [name, factory] of this.#externalDeactivateFactories) {\n deactivateRecord[name] = factory;\n }\n\n for (const [name, factory] of this.#definitionActivateFactories) {\n activateRecord[name] = factory;\n }\n for (const [name, factory] of this.#externalActivateFactories) {\n activateRecord[name] = factory;\n }\n\n return [deactivateRecord, activateRecord];\n }\n\n /**\n * Returns factories tagged by origin — definition and external as separate\n * `[deactivate, activate]` tuples. Used by `cloneRouter` to re-register\n * guards on the clone with their original origin flag preserved.\n */\n getFactoriesByOrigin(): {\n definition: [\n Record<string, GuardFnFactory<Dependencies>>,\n Record<string, GuardFnFactory<Dependencies>>,\n ];\n external: [\n Record<string, GuardFnFactory<Dependencies>>,\n Record<string, GuardFnFactory<Dependencies>>,\n ];\n } {\n const defDeact: Record<string, GuardFnFactory<Dependencies>> = {};\n const defAct: Record<string, GuardFnFactory<Dependencies>> = {};\n const extensionDeact: Record<string, GuardFnFactory<Dependencies>> = {};\n const extensionAct: Record<string, GuardFnFactory<Dependencies>> = {};\n\n for (const [name, factory] of this.#definitionDeactivateFactories) {\n defDeact[name] = factory;\n }\n for (const [name, factory] of this.#definitionActivateFactories) {\n defAct[name] = factory;\n }\n for (const [name, factory] of this.#externalDeactivateFactories) {\n extensionDeact[name] = factory;\n }\n for (const [name, factory] of this.#externalActivateFactories) {\n extensionAct[name] = factory;\n }\n\n return {\n definition: [defDeact, defAct],\n external: [extensionDeact, extensionAct],\n };\n }\n\n /**\n * Returns compiled lifecycle functions for transition execution.\n *\n * @returns Tuple of [canDeactivateFunctions, canActivateFunctions] as Maps\n */\n getFunctions(): [Map<string, GuardFn>, Map<string, GuardFn>] {\n return this.#functionsTuple;\n }\n\n canNavigateTo(\n toDeactivate: string[],\n toActivate: string[],\n toState: State,\n fromState: State | undefined,\n ): boolean {\n for (const segment of toDeactivate) {\n if (\n !this.#checkGuardSync(\n this.#canDeactivateFunctions,\n segment,\n toState,\n fromState,\n \"canNavigateTo\",\n )\n ) {\n return false;\n }\n }\n\n for (const segment of toActivate) {\n if (\n !this.#checkGuardSync(\n this.#canActivateFunctions,\n segment,\n toState,\n fromState,\n \"canNavigateTo\",\n )\n ) {\n return false;\n }\n }\n\n return true;\n }\n\n /**\n * Compiles a guard factory to its `GuardFn` WITHOUT registering it — surfaces\n * a throwing / non-function factory eagerly. The prepare-then-commit\n * add/replace path (`adoptRouteArtifacts`) calls this for every pending guard\n * BEFORE the store swap (#956), so a malformed factory aborts the mutation\n * with the store untouched. The returned function is then installed via the\n * `precompiledFn` argument of {@link addCanActivate} / {@link addCanDeactivate}\n * — no re-compile, so a factory with compile-time side effects runs exactly\n * once. Same boolean-shorthand handling + compile + non-function check as the\n * inline `#registerHandler` path, so a route-config `canActivate: true`\n * (boolean shorthand, runtime-reachable via the public route type) compiles to\n * the cached `TRUE_GUARD`/`FALSE_GUARD` instead of throwing on a non-callable.\n */\n compileGuardFactory(\n handler: GuardFnFactory<Dependencies> | boolean,\n methodName: string,\n ): GuardFn {\n const factory =\n typeof handler === \"boolean\"\n ? booleanToFactory<Dependencies>(handler)\n : handler;\n const fn = this.#deps.compileFactory(factory);\n\n if (typeof fn !== \"function\") {\n throw new TypeError(\n `[router.${methodName}] Factory must return a function, got ${typeof fn}`,\n );\n }\n\n return fn;\n }\n\n // =========================================================================\n // Private methods (business logic)\n // =========================================================================\n\n /**\n * Routes a registration into the origin-specific factory Map and updates the\n * compiled function under EXTERNAL-WINS (#1174): the just-added factory becomes\n * the compiled guard unless it is a definition registered while an external\n * guard is already live (then external stays effective; the definition is still\n * stored for a later `clearDefinitionGuards()`). Within one origin the most\n * recent add overwrites.\n * Emits overwrite / threshold warnings symmetric with the pre-refactor\n * single-Map behaviour: any prior entry for the slot — same origin or\n * cross-origin — counts as an overwrite for the warning surface; only a\n * brand-new slot (no entry in either Map) increments the threshold check.\n */\n #registerHandler(\n type: \"activate\" | \"deactivate\",\n name: string,\n handler: GuardFnFactory<Dependencies> | boolean,\n isFromDefinition: boolean,\n methodName: string,\n precompiledFn?: GuardFn,\n ): void {\n const factoryMaps = this.#getFactoryMaps(type);\n const functions =\n type === \"activate\"\n ? this.#canActivateFunctions\n : this.#canDeactivateFunctions;\n const targetMap = isFromDefinition\n ? factoryMaps.definition\n : factoryMaps.external;\n const otherMap = isFromDefinition\n ? factoryMaps.external\n : factoryMaps.definition;\n\n const isOverwrite = targetMap.has(name) || otherMap.has(name);\n\n if (isOverwrite) {\n this.#deps\n .getValidator()\n ?.lifecycle.warnOverwrite(name, type, methodName);\n } else {\n // Single enforcement choke point for EVERY registration path: programmatic\n // (getLifecycleApi) and route-config (getRoutesApi.add/update, where\n // isFromDefinition=true). The hard limit throws here so route-config guards\n // are bounded exactly like programmatic ones (#961); the approaching-limit\n // warning follows. Only new slots count toward the limit — an overwrite\n // leaves the count unchanged. `getHandlerCount` is read once and only when\n // the validator is installed (opt-in), so the no-plugin path stays free.\n const validator = this.#deps.getValidator();\n\n if (validator) {\n const count = this.getHandlerCount(type);\n\n validator.lifecycle.validateHandlerLimit(count, methodName);\n validator.lifecycle.validateCountThresholds(count + 1, methodName);\n }\n }\n\n const factory =\n typeof handler === \"boolean\"\n ? booleanToFactory<Dependencies>(handler)\n : handler;\n\n // Capture the slot's prior factory (if any) BEFORE the overwrite, so a\n // compile-throw can be rolled back to the previously-valid guard rather\n // than dropping it (#963).\n const previousFactory = targetMap.get(name);\n\n targetMap.set(name, factory);\n\n // External-wins (#1174): the compiled slot reflects the external guard\n // whenever one exists, regardless of registration order. A definition\n // registered while an external guard is live is still stored (so a later\n // replace()-clear can recompile from it via `#recompileSlot`) but does NOT\n // overwrite the compiled function — external stays effective. This makes\n // `#registerHandler` consistent with `#recompileSlot` / `clearDefinitionGuards`\n // (both external-wins, #1192), so the whole namespace has ONE policy, and\n // cloneRouter's fixed definition→external replay yields the same effective\n // guard as the source with no extra origin tracking. (The factory is still\n // compiled below to validate it and to keep the rollback path symmetric.)\n const externalWins = isFromDefinition && otherMap.has(name);\n\n try {\n // A pre-validated function (from the #956 add/replace pre-compile) is\n // installed directly — no re-compile; otherwise compile + non-function\n // check here (`compileGuardFactory` throws on a bad factory).\n const fn = precompiledFn ?? this.compileGuardFactory(factory, methodName);\n\n if (!externalWins) {\n functions.set(name, fn);\n }\n } catch (error) {\n // Roll the slot back to its pre-call state: restore the previous factory\n // on an overwrite (#963), else clear the slot. `#recompileSlot` then\n // resets the compiled function from whichever origin Map still holds an\n // entry — the restored same-origin factory, a surviving cross-origin one,\n // or (empty slot) deletes the compiled function.\n if (previousFactory === undefined) {\n targetMap.delete(name);\n } else {\n targetMap.set(name, previousFactory);\n }\n\n this.#recompileSlot(type, name);\n\n throw error;\n }\n }\n\n /**\n * Shared implementation for {@link clearCanActivate} / {@link clearCanDeactivate}\n * — the clear-side counterpart to {@link #registerHandler}. `scope` selects the\n * origin lane (no origin-blind default, #1171); when one origin is cleared and\n * the other survives, `#recompileSlot` recompiles the compiled function from\n * the survivor (external wins, #1174).\n */\n #clearGuard(\n type: \"activate\" | \"deactivate\",\n name: string,\n scope: GuardClearScope,\n ): void {\n const { definition, external } = this.#getFactoryMaps(type);\n const clearedDefinition =\n scope === \"external\" ? false : definition.delete(name);\n const clearedExternal =\n scope === \"definition\" ? false : external.delete(name);\n\n if (clearedDefinition || clearedExternal) {\n this.#recompileSlot(type, name);\n }\n }\n\n /**\n * Recompiles the compiled-function slot from whichever origin Map still has\n * an entry for `name` after a clear. External wins over definition; if\n * neither has an entry, the compiled function is deleted.\n */\n #recompileSlot(type: \"activate\" | \"deactivate\", name: string): void {\n const factoryMaps = this.#getFactoryMaps(type);\n const functions =\n type === \"activate\"\n ? this.#canActivateFunctions\n : this.#canDeactivateFunctions;\n\n const effective =\n factoryMaps.external.get(name) ?? factoryMaps.definition.get(name);\n\n if (!effective) {\n functions.delete(name);\n\n return;\n }\n\n try {\n const fn = this.#deps.compileFactory(effective);\n\n /* v8 ignore next 4 -- @preserve: stored factories were validated at add time, compileFactory should yield a function on second call too */\n if (typeof fn !== \"function\") {\n functions.delete(name);\n\n return;\n }\n\n functions.set(name, fn);\n } catch {\n /* v8 ignore next 2 -- @preserve: defensive — a user-provided factory could theoretically throw on re-compile (state changed since add time); deleting the function blocks navigation on that slot */\n functions.delete(name);\n }\n }\n\n #getFactoryMaps(type: \"activate\" | \"deactivate\"): {\n definition: Map<string, GuardFnFactory<Dependencies>>;\n external: Map<string, GuardFnFactory<Dependencies>>;\n } {\n return type === \"activate\"\n ? {\n definition: this.#definitionActivateFactories,\n external: this.#externalActivateFactories,\n }\n : {\n definition: this.#definitionDeactivateFactories,\n external: this.#externalDeactivateFactories,\n };\n }\n\n /**\n * Shared implementation for synchronous guard checks.\n * Warns if a guard returns a Promise (async guards are not supported in sync mode).\n * Catches exceptions and treats them as navigation-blocking (`false`).\n *\n * @param functions - Map of compiled guard functions to look up\n * @param name - Route name to check the guard for\n * @param toState - Target navigation state\n * @param fromState - Current state (`undefined` on initial navigation)\n * @param methodName - Public API method name for warning messages\n */\n #checkGuardSync(\n functions: Map<string, GuardFn>,\n name: string,\n toState: State,\n fromState: State | undefined,\n methodName: string,\n ): boolean {\n const guardFn = functions.get(name);\n\n if (!guardFn) {\n return true;\n }\n\n try {\n const result = guardFn(toState, fromState);\n\n if (typeof result === \"boolean\") {\n return result;\n }\n\n this.#deps.getValidator()?.lifecycle.warnAsyncGuardSync(name, methodName);\n\n return false;\n } catch (error) {\n // #959: a throwing sync guard must not vanish silently. `navigate()`\n // surfaces the same throw via `handleGuardError` → TRANSITION_ERROR; the\n // sync predicate (`canNavigateTo`) has no error channel, so core logs it\n // directly. This is an OPERATIONAL signal (the guard crashed — distinct\n // from the opt-in validator DX warnings above for which the validator is\n // the right home): the navigation is still treated as blocked (`false`).\n logger.warn(\n `router.${methodName}`,\n `Guard for \"${name}\" threw — treated as navigation-blocking (returned false)`,\n error,\n );\n\n return false;\n }\n }\n}\n","// packages/core/src/namespaces/RoutesNamespace/constants.ts\n\n/**\n * Default route name for the root node.\n */\nexport const DEFAULT_ROUTE_NAME = \"\";\n\n/**\n * Keys that belong to a route's structural/config surface — everything NOT in\n * this set is a plugin-defined **custom field** (e.g. lifecycle hooks,\n * `preload`, `searchSchema`), stored in `RoutesStore.routeCustomFields`.\n *\n * Single source of truth for the custom-field split, shared by route\n * registration (`add`/`replace`) and `update` so both classify patch keys\n * identically. `name`/`path`/`children` define route identity and are not\n * patchable via `update`; the remaining six are the structural/guard config.\n */\nexport const STANDARD_ROUTE_KEYS: ReadonlySet<string> = new Set([\n \"name\",\n \"path\",\n \"children\",\n \"canActivate\",\n \"canDeactivate\",\n \"forwardTo\",\n \"encodeParams\",\n \"decodeParams\",\n \"defaultParams\",\n]);\n","// packages/core/src/namespaces/RoutesNamespace/helpers.ts\n\nimport type { RouteConfig } from \"./types\";\nimport type { Route } from \"../../types\";\nimport type {\n DefaultDependencies,\n ForwardToCallback,\n Params,\n} from \"@real-router/types\";\nimport type { RouteDefinition } from \"route-tree\";\n\n/**\n * Creates an empty RouteConfig.\n */\nexport function createEmptyConfig(): RouteConfig {\n return {\n decoders: Object.create(null) as Record<string, (params: Params) => Params>,\n encoders: Object.create(null) as Record<string, (params: Params) => Params>,\n defaultParams: Object.create(null) as Record<string, Params>,\n forwardMap: Object.create(null) as Record<string, string>,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n forwardFnMap: Object.create(null) as Record<string, ForwardToCallback<any>>,\n };\n}\n\n/**\n * Copies every {@link RouteConfig} sub-map's entries from `source` into\n * `target` (shallow per map — entries are shared by reference). Driven by\n * `Object.keys(source)` instead of one `Object.assign` per field, so a newly\n * added config sub-field is carried over automatically with nothing to forget\n * at each copy site (#965). Both configs are produced by\n * {@link createEmptyConfig}, so every key in `source` also exists on `target`,\n * and every value is a record object — the invariant this enumeration relies on.\n */\nexport function assignConfigEntries(\n target: RouteConfig,\n source: RouteConfig,\n): void {\n for (const key of Object.keys(source) as (keyof RouteConfig)[]) {\n Object.assign(target[key], source[key]);\n }\n}\n\n// ============================================================================\n// Route Tree Helpers\n// ============================================================================\n\n/**\n * Checks if all params from source exist with same values in target.\n * Small function body allows V8 inlining.\n */\nexport function paramsMatch(source: Params, target: Params): boolean {\n for (const key in source) {\n if (source[key] !== target[key]) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Checks params match, skipping keys present in skipKeys.\n */\nexport function paramsMatchExcluding(\n source: Params,\n target: Params,\n skipKeys: Params,\n): boolean {\n for (const key in source) {\n if (key in skipKeys) {\n continue;\n }\n if (source[key] !== target[key]) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Returns a copy of `defaultParams` with query-typed keys removed, based on\n * `ownMeta` (the per-route paramTypeMap from the matcher). When no query keys\n * are present, returns the input by reference (zero-allocation fast path).\n *\n * `ownMeta` may be `undefined` for non-registered route names — this is the\n * caller's escape hatch (e.g. `getMetaByName(name)?.[name]`); a missing meta\n * means no query type information is available, so defaults pass through\n * unchanged.\n */\nexport function stripQueryDefaults(\n defaultParams: Params,\n ownMeta: Record<string, \"url\" | \"query\"> | undefined,\n): Params {\n if (!ownMeta || !hasQueryDefault(defaultParams, ownMeta)) {\n return defaultParams;\n }\n\n const filtered: Params = {};\n\n for (const key in defaultParams) {\n if (ownMeta[key] !== \"query\") {\n filtered[key] = defaultParams[key];\n }\n }\n\n return filtered;\n}\n\nfunction hasQueryDefault(\n defaultParams: Params,\n ownMeta: Record<string, \"url\" | \"query\">,\n): boolean {\n for (const key in defaultParams) {\n if (ownMeta[key] === \"query\") {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Sanitizes a route by keeping only essential properties.\n */\nexport function sanitizeRoute<Dependencies extends DefaultDependencies>(\n route: Route<Dependencies>,\n): RouteDefinition {\n const sanitized: RouteDefinition = {\n name: route.name,\n path: route.path,\n };\n\n if (route.children) {\n sanitized.children = route.children.map((child) => sanitizeRoute(child));\n }\n\n return sanitized;\n}\n\n/**\n * Recursively removes a route from definitions array.\n */\nexport function removeFromDefinitions(\n definitions: RouteDefinition[],\n routeName: string,\n parentPrefix = \"\",\n): boolean {\n for (let i = 0; i < definitions.length; i++) {\n const route = definitions[i];\n const fullName = parentPrefix\n ? `${parentPrefix}.${route.name}`\n : route.name;\n\n if (fullName === routeName) {\n definitions.splice(i, 1);\n\n return true;\n }\n\n if (\n route.children &&\n routeName.startsWith(`${fullName}.`) &&\n removeFromDefinitions(route.children, routeName, fullName)\n ) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Clears configuration entries that match the predicate.\n */\nexport function clearConfigEntries<T>(\n config: Record<string, T>,\n matcher: (key: string) => boolean,\n): void {\n for (const key of Object.keys(config)) {\n if (matcher(key)) {\n delete config[key];\n }\n }\n}\n\n/**\n * Used by matchPath() when trailingSlash is \"preserve\": the matcher's\n * buildPath() with an unset trailingSlash mode strips trailing slashes,\n * but \"preserve\" means the source path's trailing-slash choice wins.\n * If the source had a trailing slash, re-attach it to the rewritten path.\n * The reverse case (rewritten has trailing, source does not) is not\n * reachable with the current matcher — it never adds a trailing slash\n * with undefined mode.\n */\nexport function matchSourceTrailingSlash(\n sourcePath: string,\n rewrittenPath: string,\n): string {\n const queryIndex = rewrittenPath.search(/[?#]/);\n const pathPart =\n queryIndex === -1 ? rewrittenPath : rewrittenPath.slice(0, queryIndex);\n\n // Stryker disable next-line LogicalOperator: equivalent — buildPath strips trailing slashes, so the rewritten path never ends with \"/\" unless it IS \"/\" (already caught by the `=== \"/\"` operand). `endsWith(\"/\")` is unreachable-true, so `||` ≡ `&&`.\n if (pathPart === \"/\" || pathPart.endsWith(\"/\")) {\n return rewrittenPath;\n }\n\n const sourceQueryIndex = sourcePath.search(/[?#]/);\n const sourcePathPart =\n sourceQueryIndex === -1\n ? sourcePath\n : sourcePath.slice(0, sourceQueryIndex);\n\n if (!(sourcePathPart.length > 1 && sourcePathPart.endsWith(\"/\"))) {\n return rewrittenPath;\n }\n\n const querySuffix = queryIndex === -1 ? \"\" : rewrittenPath.slice(queryIndex);\n\n return `${pathPart}/${querySuffix}`;\n}\n","// packages/core/src/namespaces/RoutesNamespace/forwardChain.ts\n\nexport function resolveForwardChain(\n startRoute: string,\n forwardMap: Record<string, string>,\n maxDepth = 100,\n): string {\n const visited = new Set<string>();\n const chain: string[] = [startRoute];\n let current = startRoute;\n\n while (forwardMap[current]) {\n const next = forwardMap[current];\n\n if (visited.has(next)) {\n const cycleStart = chain.indexOf(next);\n const cycle = [...chain.slice(cycleStart), next];\n\n throw new Error(`Circular forwardTo: ${cycle.join(\" → \")}`);\n }\n\n visited.add(current);\n chain.push(next);\n current = next;\n\n if (chain.length > maxDepth) {\n throw new Error(\n `forwardTo chain exceeds maximum depth (${maxDepth}): ${chain.join(\" → \")}`,\n );\n }\n }\n\n return current;\n}\n","// packages/core/src/namespaces/RoutesNamespace/routesStore.ts\n\nimport { logger } from \"@real-router/logger\";\nimport {\n createMatcher,\n createRouteTree,\n routeTreeToDefinitions,\n} from \"route-tree\";\n\nimport { DEFAULT_ROUTE_NAME, STANDARD_ROUTE_KEYS } from \"./constants\";\nimport { resolveForwardChain } from \"./forwardChain\";\nimport {\n assignConfigEntries,\n createEmptyConfig,\n sanitizeRoute,\n} from \"./helpers\";\n\nimport type { RouteConfig, RoutesDependencies } from \"./types\";\nimport type { GuardFnFactory, Route } from \"../../types\";\nimport type { RouteLifecycleNamespace } from \"../RouteLifecycleNamespace\";\nimport type {\n DefaultDependencies,\n ForwardToCallback,\n GuardFn,\n Params,\n RouteConfigUpdate,\n} from \"@real-router/types\";\nimport type {\n CreateMatcherOptions,\n Matcher,\n RouteDefinition,\n RouteTree,\n} from \"route-tree\";\n\n// =============================================================================\n// Interfaces\n// =============================================================================\n\nexport interface RoutesStore<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n> {\n /**\n * DERIVED VIEW, not stored state: reconstructed from `tree` on every access\n * via `routeTreeToDefinitions` (the lossless inverse cloneRouter already\n * relies on — the `~` absolute marker is restored, child order is the\n * definition order). The tree is the single source of truth, so a third\n * retained copy of the route table (~30 B/route) is not kept. Every reader\n * is a cold CRUD/plugin-registration path; the derive is O(N).\n *\n * The returned array is a FRESH snapshot each time — mutating it never\n * affects the store (pass an explicitly-mutated snapshot to\n * `commitTreeChanges` instead, as `remove` does).\n */\n readonly definitions: RouteDefinition[];\n readonly config: RouteConfig;\n tree: RouteTree;\n matcher: Matcher;\n /**\n * Per-route-name cache of URL (path) param names, derived from `matcher` and\n * read by `RoutesNamespace.getUrlParams` (powers `areStatesEqual` /\n * `isActiveRoute`). Cleared on every `matcher` rebuild so comparisons never\n * stay frozen to a route's pre-mutation param shape (#723).\n */\n readonly urlParamsCache: Map<string, string[]>;\n resolvedForwardMap: Record<string, string>;\n routeCustomFields: Record<string, Record<string, unknown>>;\n rootPath: string;\n readonly matcherOptions: CreateMatcherOptions | undefined;\n depsStore: RoutesDependencies<Dependencies> | undefined;\n lifecycleNamespace: RouteLifecycleNamespace<Dependencies> | undefined;\n readonly pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>;\n readonly pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>;\n}\n\n// =============================================================================\n// Tree operations\n// =============================================================================\n\nfunction rebuildTree(\n definitions: readonly RouteDefinition[],\n rootPath: string,\n matcherOptions: CreateMatcherOptions | undefined,\n): { tree: RouteTree; matcher: Matcher } {\n const tree = createRouteTree(DEFAULT_ROUTE_NAME, rootPath, definitions);\n const matcher = createMatcher(matcherOptions);\n\n matcher.registerTree(tree);\n\n return { tree, matcher };\n}\n\n/**\n * Rebuilds tree+matcher in place from `definitions` (defaults to the current\n * tree's own derived definitions — the same-table case, e.g. a rootPath\n * change).\n */\nexport function rebuildTreeInPlace<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n store: RoutesStore<Dependencies>,\n definitions: readonly RouteDefinition[] = store.definitions,\n): void {\n const result = rebuildTree(definitions, store.rootPath, store.matcherOptions);\n\n store.tree = result.tree;\n store.matcher = result.matcher;\n store.urlParamsCache.clear();\n}\n\nexport function commitTreeChanges<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n store: RoutesStore<Dependencies>,\n definitions: readonly RouteDefinition[],\n): void {\n rebuildTreeInPlace(store, definitions);\n store.resolvedForwardMap = refreshForwardMap(store.config);\n}\n\n// =============================================================================\n// Store reset\n// =============================================================================\n\n/**\n * Clears all routes and resets config.\n * Does NOT clear lifecycle handlers or state — caller handles that.\n */\nexport function resetStore<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(store: RoutesStore<Dependencies>): void {\n clearRouteData(store);\n rebuildTreeInPlace(store, []);\n}\n\n/**\n * Clears route data without rebuilding the tree.\n * Used by replace() to avoid double rebuild (clearRouteData + commitTreeChanges).\n * `definitions` needs no clearing — it is derived from the tree, which the\n * caller rebuilds (resetStore → empty, replace → the new artifacts).\n */\nexport function clearRouteData<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(store: RoutesStore<Dependencies>): void {\n Object.assign(store.config, createEmptyConfig());\n\n store.resolvedForwardMap = Object.create(null) as Record<string, string>;\n store.routeCustomFields = Object.create(null) as Record<\n string,\n Record<string, unknown>\n >;\n}\n\n// =============================================================================\n// Forward map\n// =============================================================================\n\nexport function refreshForwardMap(config: RouteConfig): Record<string, string> {\n const map = Object.create(null) as Record<string, string>;\n\n for (const fromRoute of Object.keys(config.forwardMap)) {\n map[fromRoute] = resolveForwardChain(fromRoute, config.forwardMap);\n }\n\n return map;\n}\n\n// =============================================================================\n// Route handler registration\n// =============================================================================\n\n/**\n * Throws if `forwardTo` is an async function (native or transpiled). Async\n * forwardTo callbacks break the synchronous matchPath/buildPath contract.\n * Runs inside `registerForwardTo` (the add/replace build path, before any store\n * mutation) AND inside `getRoutesApi`'s `updateForwardTo` (the update path), so\n * `update(name, { forwardTo: async })` is rejected at registration with the same\n * actionable error instead of deferring a generic TypeError to navigation (#967).\n */\nexport function assertForwardToNotAsync(\n forwardTo: unknown,\n fullName: string,\n): void {\n if (typeof forwardTo !== \"function\") {\n return;\n }\n\n const isNativeAsync =\n (forwardTo as { constructor: { name: string } }).constructor.name ===\n \"AsyncFunction\";\n const isTranspiledAsync = (forwardTo as { toString: () => string })\n .toString()\n .includes(\"__awaiter\");\n\n if (isNativeAsync || isTranspiledAsync) {\n throw new TypeError(\n `forwardTo callback cannot be async for route \"${fullName}\". ` +\n `Async functions break matchPath/buildPath.`,\n );\n }\n}\n\nfunction registerForwardTo<Dependencies extends DefaultDependencies>(\n route: Route<Dependencies>,\n fullName: string,\n config: RouteConfig,\n): void {\n if (route.canActivate) {\n /* v8 ignore next -- @preserve: edge case, both string and function tested separately */\n const forwardTarget =\n typeof route.forwardTo === \"string\" ? route.forwardTo : \"[dynamic]\";\n\n logger.warn(\n \"real-router\",\n `Route \"${fullName}\" has both forwardTo and canActivate. ` +\n `canActivate will be ignored because forwardTo creates a redirect (industry standard). ` +\n `Move canActivate to the target route \"${forwardTarget}\".`,\n );\n }\n\n if (route.canDeactivate) {\n /* v8 ignore next -- @preserve: edge case, both string and function tested separately */\n const forwardTarget =\n typeof route.forwardTo === \"string\" ? route.forwardTo : \"[dynamic]\";\n\n logger.warn(\n \"real-router\",\n `Route \"${fullName}\" has both forwardTo and canDeactivate. ` +\n `canDeactivate will be ignored because forwardTo creates a redirect (industry standard). ` +\n `Move canDeactivate to the target route \"${forwardTarget}\".`,\n );\n }\n\n assertForwardToNotAsync(route.forwardTo, fullName);\n\n // forwardTo is guaranteed to exist at this point\n if (typeof route.forwardTo === \"string\") {\n config.forwardMap[fullName] = route.forwardTo;\n } else {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n config.forwardFnMap[fullName] = route.forwardTo!;\n }\n}\n\nfunction registerSingleRouteHandlers<Dependencies extends DefaultDependencies>(\n route: Route<Dependencies>,\n fullName: string,\n config: RouteConfig,\n routeCustomFields: Record<string, Record<string, unknown>>,\n pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>,\n pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>,\n): void {\n const customFields = Object.fromEntries(\n Object.entries(route).filter(([key]) => !STANDARD_ROUTE_KEYS.has(key)),\n );\n\n if (Object.keys(customFields).length > 0) {\n routeCustomFields[fullName] = customFields;\n }\n\n // Guards are collected here and registered into the lifecycle later — by\n // `adoptRouteArtifacts` (add/replace) or `RoutesNamespace.flushPendingGuards`\n // (initial routes, the final step of the Router constructor — #1331) — so\n // the build stays a pure, side-effect-free preparation step.\n if (route.canActivate) {\n pendingCanActivate.set(fullName, route.canActivate);\n }\n\n if (route.canDeactivate) {\n pendingCanDeactivate.set(fullName, route.canDeactivate);\n }\n\n if (route.forwardTo) {\n registerForwardTo(route, fullName, config);\n }\n\n if (route.decodeParams) {\n config.decoders[fullName] = (params: Params): Params =>\n route.decodeParams?.(params) ?? params;\n }\n\n if (route.encodeParams) {\n config.encoders[fullName] = (params: Params): Params =>\n route.encodeParams?.(params) ?? params;\n }\n\n if (route.defaultParams) {\n config.defaultParams[fullName] = route.defaultParams;\n }\n}\n\nfunction registerAllRouteHandlers<Dependencies extends DefaultDependencies>(\n routes: readonly Route<Dependencies>[],\n config: RouteConfig,\n routeCustomFields: Record<string, Record<string, unknown>>,\n pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>,\n pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>,\n parentName = \"\",\n): void {\n for (const route of routes) {\n const fullName = parentName ? `${parentName}.${route.name}` : route.name;\n\n registerSingleRouteHandlers(\n route,\n fullName,\n config,\n routeCustomFields,\n pendingCanActivate,\n pendingCanDeactivate,\n );\n\n if (route.children) {\n registerAllRouteHandlers(\n route.children,\n config,\n routeCustomFields,\n pendingCanActivate,\n pendingCanDeactivate,\n fullName,\n );\n }\n }\n}\n\n// =============================================================================\n// Prepare-then-commit (issue #698)\n//\n// add()/replace() build the complete new store state into LOCAL structures, and\n// only swap it into the store once every core-level error has surfaced from the\n// build itself (async/circular forwardTo throw in registerAllRouteHandlers /\n// refreshForwardMap; invalid path constraint throws in rebuildTree). The store\n// is mutated only by `adoptRouteArtifacts`, which compiles every prepared guard\n// factory BEFORE the swap (#956): a factory that throws on compile (or returns a\n// non-function) aborts there, with the store still untouched. So all error\n// classes — core-level build errors AND malformed guard factories — surface\n// before any mutation, leaving the existing routes intact (full atomicity). The\n// silent-corruption cases route-tree never throws on (duplicate name vs an\n// existing route, a name duplicated within the batch, missing parent) are caught\n// up front by `assertAddable`.\n// =============================================================================\n\n/**\n * The fully-built, ready-to-swap result of preparing a route mutation. Holds\n * everything `adoptRouteArtifacts` assigns into the store.\n */\ninterface RouteArtifacts<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n> {\n readonly config: RouteConfig;\n readonly routeCustomFields: Record<string, Record<string, unknown>>;\n readonly pendingCanActivate: Map<string, GuardFnFactory<Dependencies>>;\n readonly pendingCanDeactivate: Map<string, GuardFnFactory<Dependencies>>;\n readonly tree: RouteTree;\n readonly matcher: Matcher;\n readonly resolvedForwardMap: Record<string, string>;\n}\n\n/** Null-proto shallow clone of a RouteConfig (preserves every sub-map's contents). */\nfunction cloneConfig(config: RouteConfig): RouteConfig {\n const clone = createEmptyConfig();\n\n assignConfigEntries(clone, config);\n\n return clone;\n}\n\n/**\n * Returns a new definitions array with `added` inserted, without mutating the\n * input. For a top-level add the existing definitions are shallow-copied and\n * `added` appended. For a parented add the spine down to the parent is cloned\n * (siblings/other branches are shared by reference) and `added` appended to the\n * parent's children. Caller guarantees the parent path exists (see assertAddable).\n */\nfunction insertAddedDefinitions(\n definitions: readonly RouteDefinition[],\n added: RouteDefinition[],\n parentSegments: readonly string[],\n): RouteDefinition[] {\n if (parentSegments.length === 0) {\n return [...definitions, ...added];\n }\n\n const [head, ...rest] = parentSegments;\n\n return definitions.map((def) => {\n if (def.name !== head) {\n return def;\n }\n\n const children = def.children ?? [];\n\n return {\n ...def,\n children:\n rest.length === 0\n ? [...children, ...added]\n : insertAddedDefinitions(children, added, rest),\n };\n });\n}\n\n/** Depth-first walk yielding each route's full dotted name (no side effects). */\nfunction walkRouteNames<Dependencies extends DefaultDependencies>(\n routes: readonly Route<Dependencies>[],\n parentName: string,\n onName: (fullName: string) => void,\n): void {\n for (const route of routes) {\n const fullName = parentName ? `${parentName}.${route.name}` : route.name;\n\n onName(fullName);\n\n if (route.children) {\n walkRouteNames(route.children, fullName, onName);\n }\n }\n}\n\n/**\n * Rejects a route name duplicated WITHIN a single batch — the silent-overwrite\n * case route-tree stays last-wins on (#953 for `add`, #968 for `replace`). Walks\n * the same depth-first dotted names, but tracks them in a local Set: a name seen\n * twice in one array means the caller's second route would silently shadow the\n * first (`matchPath` for the first route's path becomes unreachable). Mirrors\n * validation-plugin's batch-dup message (route-tree `checkBatchNameDuplicate`)\n * so the no-plugin error matches the with-plugin one. `methodName` is \"addRoute\"\n * for both add and replace — the plugin reports \"addRoute\" for replace batches\n * too, so this keeps with/without-plugin parity.\n */\nexport function assertNoDuplicateNamesInBatch<\n Dependencies extends DefaultDependencies,\n>(\n routes: readonly Route<Dependencies>[],\n parentName: string,\n methodName: string,\n): void {\n const seen = new Set<string>();\n\n walkRouteNames(routes, parentName, (fullName) => {\n if (seen.has(fullName)) {\n throw new Error(\n `[router.${methodName}] Duplicate route \"${fullName}\" in batch`,\n );\n }\n\n seen.add(fullName);\n });\n}\n\nconst INTERNAL_ROUTE_PREFIX = \"@@\";\n\n/**\n * Rejects a single (bare) route name that uses the reserved \"@@\" prefix —\n * internal / system names such as UNKNOWN_ROUTE (`\"@@router/UNKNOWN_ROUTE\"`).\n * Mutating such a name would let a real URL `matchPath` to a state with\n * `name === UNKNOWN_ROUTE`, silently conflating a genuine route with \"not\n * found\". This always-on guard protected all four mutators (#238) until the\n * validation-extraction (`d1ebff80`) demoted it to the opt-in\n * validation-plugin; only `add` was restored (#954), so `remove`/`update`\n * regained it via this helper (#1047). Mirrors validation-plugin's\n * `throwIfInternalRoute` message so the no-plugin error matches the with-plugin\n * one.\n */\nexport function assertNoInternalRouteName(\n name: string,\n methodName: string,\n): void {\n if (name.startsWith(INTERNAL_ROUTE_PREFIX)) {\n throw new Error(\n `[router.${methodName}] Route name \"${name}\" uses the reserved \"${INTERNAL_ROUTE_PREFIX}\" prefix. Routes with this prefix are internal and cannot be modified through the public API.`,\n );\n }\n}\n\n/**\n * Batch counterpart to {@link assertNoInternalRouteName}: rejects any route in\n * the batch (recursing children) whose BARE leaf name uses the reserved \"@@\"\n * prefix (the prefix is on the leaf, not the dotted fullName). Used by `add`\n * (#954) and `replace` (#1047).\n */\nexport function assertNoInternalNamesInBatch<\n Dependencies extends DefaultDependencies,\n>(routes: readonly Route<Dependencies>[], methodName: string): void {\n for (const route of routes) {\n assertNoInternalRouteName(route.name, methodName);\n\n if (route.children) {\n assertNoInternalNamesInBatch(route.children, methodName);\n }\n }\n}\n\n/**\n * Rejects two routes that share the same `path` at the same parent level WITHIN\n * a single `add` batch (#955). The matcher resolves a path collision last-wins,\n * so the earlier route stays addressable by name (`has` / `buildPath`) but is\n * unreachable by URL (`matchPath` returns the later route) — a silent shadow.\n * Paths only collide among siblings, so seen paths are tracked per parent\n * fullName. Mirrors validation-plugin's message (route-tree\n * `checkBatchPathDuplicate`) so the no-plugin error matches the with-plugin one.\n * Scoped to the batch (not the existing tree) per #955 — the in-batch case the\n * issue describes.\n */\nexport function assertNoDuplicatePathsInBatch<\n Dependencies extends DefaultDependencies,\n>(\n routes: readonly Route<Dependencies>[],\n parentName: string,\n methodName: string,\n): void {\n const seenByParent = new Map<string, Set<string>>();\n\n const walk = (\n siblings: readonly Route<Dependencies>[],\n parent: string,\n ): void => {\n for (const route of siblings) {\n const paths = seenByParent.get(parent);\n\n if (paths?.has(route.path)) {\n throw new Error(\n `[router.${methodName}] Path \"${route.path}\" is already defined`,\n );\n }\n\n if (paths) {\n paths.add(route.path);\n } else {\n seenByParent.set(parent, new Set([route.path]));\n }\n\n if (route.children) {\n walk(route.children, parent ? `${parent}.${route.name}` : route.name);\n }\n }\n };\n\n walk(routes, parentName);\n}\n\n/**\n * Up-front guard for `add` against the corruptions route-tree stays silent on: a\n * missing `parent`, a name that collides with an EXISTING route, a name\n * duplicated WITHIN the batch, a reserved \"@@\"-prefixed name (which would shadow\n * an internal/system route name), and a path duplicated among siblings WITHIN\n * the batch (any of which would otherwise be silently overwritten/shadowed).\n * Throws before any build.\n */\nexport function assertAddable<Dependencies extends DefaultDependencies>(\n store: RoutesStore<Dependencies>,\n routes: readonly Route<Dependencies>[],\n parentName: string | undefined,\n): void {\n assertNoInternalNamesInBatch(routes, \"addRoute\");\n\n if (parentName !== undefined && !store.matcher.hasRoute(parentName)) {\n throw new Error(\n `[router.addRoute] Parent route \"${parentName}\" does not exist`,\n );\n }\n\n walkRouteNames(routes, parentName ?? \"\", (fullName) => {\n if (store.matcher.hasRoute(fullName)) {\n throw new Error(`[router.addRoute] Route \"${fullName}\" already exists`);\n }\n });\n\n assertNoDuplicateNamesInBatch(routes, parentName ?? \"\", \"addRoute\");\n assertNoDuplicatePathsInBatch(routes, parentName ?? \"\", \"addRoute\");\n}\n\n/**\n * Builds RouteArtifacts from a final definitions array and the routes whose\n * handlers (config + guards) populate `config`/`routeCustomFields`. Guards are\n * collected into the returned pending maps (depsStore is intentionally omitted\n * so nothing compiles or touches the lifecycle here). THROWS on async/circular\n * forwardTo and invalid path constraint — before the caller mutates the store.\n */\nfunction buildArtifacts<Dependencies extends DefaultDependencies>(\n definitions: readonly RouteDefinition[],\n routesForHandlers: readonly Route<Dependencies>[],\n config: RouteConfig,\n routeCustomFields: Record<string, Record<string, unknown>>,\n handlerParentName: string,\n rootPath: string,\n matcherOptions: CreateMatcherOptions | undefined,\n): RouteArtifacts<Dependencies> {\n const pendingCanActivate = new Map<string, GuardFnFactory<Dependencies>>();\n const pendingCanDeactivate = new Map<string, GuardFnFactory<Dependencies>>();\n\n registerAllRouteHandlers(\n routesForHandlers,\n config,\n routeCustomFields,\n pendingCanActivate,\n pendingCanDeactivate,\n handlerParentName,\n );\n\n const resolvedForwardMap = refreshForwardMap(config);\n const { tree, matcher } = rebuildTree(definitions, rootPath, matcherOptions);\n\n return {\n config,\n routeCustomFields,\n pendingCanActivate,\n pendingCanDeactivate,\n tree,\n matcher,\n resolvedForwardMap,\n };\n}\n\n/** Builds the merged artifacts for an incremental `add` (existing ∪ new). */\nexport function buildAddArtifacts<Dependencies extends DefaultDependencies>(\n store: RoutesStore<Dependencies>,\n routes: readonly Route<Dependencies>[],\n parentName: string | undefined,\n): RouteArtifacts<Dependencies> {\n const definitions = insertAddedDefinitions(\n store.definitions,\n routes.map((route) => sanitizeRoute(route)),\n parentName === undefined ? [] : parentName.split(\".\"),\n );\n\n return buildArtifacts(\n definitions,\n routes,\n cloneConfig(store.config),\n Object.assign(\n Object.create(null) as Record<string, Record<string, unknown>>,\n store.routeCustomFields,\n ),\n parentName ?? \"\",\n store.rootPath,\n store.matcherOptions,\n );\n}\n\n/** Builds the fresh artifacts for a full `replace` (standalone new set). */\nexport function buildReplaceArtifacts<Dependencies extends DefaultDependencies>(\n routes: readonly Route<Dependencies>[],\n rootPath: string,\n matcherOptions: CreateMatcherOptions | undefined,\n): RouteArtifacts<Dependencies> {\n return buildArtifacts(\n routes.map((route) => sanitizeRoute(route)),\n routes,\n createEmptyConfig(),\n Object.create(null) as Record<string, Record<string, unknown>>,\n \"\",\n rootPath,\n matcherOptions,\n );\n}\n\n/**\n * Compiles every pending guard factory up front, returning\n * `[name, factory, compiledFn]` triples for installation. THROWS from `compile`\n * on the first factory that throws on compile or returns a non-function — the\n * pre-swap validation that makes `adoptRouteArtifacts` atomic for malformed\n * guards (#956). Compiling here (not at install) means a factory with\n * compile-time side effects runs exactly once.\n */\nfunction compilePendingGuards<Dependencies extends DefaultDependencies>(\n pending: Map<string, GuardFnFactory<Dependencies>>,\n compile: (\n handler: GuardFnFactory<Dependencies>,\n methodName: string,\n ) => GuardFn,\n methodName: string,\n): [string, GuardFnFactory<Dependencies>, GuardFn][] {\n const compiled: [string, GuardFnFactory<Dependencies>, GuardFn][] = [];\n\n for (const [name, factory] of pending) {\n compiled.push([name, factory, compile(factory, methodName)]);\n }\n\n return compiled;\n}\n\n/** Pre-compiled guard triples for {@link adoptRouteArtifacts} install. */\nexport interface CompiledArtifactGuards<\n Dependencies extends DefaultDependencies,\n> {\n activate: [string, GuardFnFactory<Dependencies>, GuardFn][];\n deactivate: [string, GuardFnFactory<Dependencies>, GuardFn][];\n}\n\n/**\n * Compiles an artifacts' pending guard factories up front (#956), THROWING on\n * the first factory that throws on compile or returns a non-function.\n *\n * `replaceRoutes` calls this in its PREPARE phase — **before**\n * `clearDefinitionGuards()` — and hands the result to `adoptRouteArtifacts`, so\n * a compile-throw aborts with BOTH the tree AND the old definition guards intact\n * (#1193, mirroring #1046's handler-limit hoist). `add` has no clear step, so\n * `adoptRouteArtifacts` compiles inline for it.\n */\nexport function compileArtifactGuards<Dependencies extends DefaultDependencies>(\n artifacts: RouteArtifacts<Dependencies>,\n deps: RoutesDependencies<Dependencies>,\n): CompiledArtifactGuards<Dependencies> {\n return {\n activate: compilePendingGuards(\n artifacts.pendingCanActivate,\n deps.compileGuard,\n \"canActivate\",\n ),\n deactivate: compilePendingGuards(\n artifacts.pendingCanDeactivate,\n deps.compileGuard,\n \"canDeactivate\",\n ),\n };\n}\n\n/**\n * Commits prepared artifacts into the store in place. Every pending guard\n * factory is compiled BEFORE the tree/config swap (#956): a factory that throws\n * on compile (or returns a non-function) aborts here with the store untouched,\n * so `add`/`replace` are atomic for malformed guards too — not just core build\n * errors. The tree/config assignments are pure and cannot throw; the\n * pre-compiled guards are then installed without re-compiling (the factory ran\n * once, at the pre-compile above). `depsStore` is always set on a wired router,\n * which is the only path that reaches `add`/`replace`.\n */\nexport function adoptRouteArtifacts<Dependencies extends DefaultDependencies>(\n store: RoutesStore<Dependencies>,\n artifacts: RouteArtifacts<Dependencies>,\n precompiled?: CompiledArtifactGuards<Dependencies>,\n): void {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- depsStore is set once the router is wired; add/replace only run on a wired router (constructor-time registration uses createRoutesStore)\n const deps = store.depsStore!;\n\n // Pre-swap compile: surfaces a malformed guard factory before any mutation.\n // `replace()` pre-compiles in its PREPARE phase (BEFORE clearDefinitionGuards)\n // and passes the result here, so a compile-throw never erases the old\n // definition guards (#1193); `add` has no clear step and compiles inline.\n const { activate: compiledActivate, deactivate: compiledDeactivate } =\n precompiled ?? compileArtifactGuards(artifacts, deps);\n\n // Atomic swap — pure assignments, cannot throw. (`definitions` is derived\n // from `tree`, so swapping the tree IS the definitions swap.)\n Object.assign(store.config, artifacts.config);\n store.routeCustomFields = artifacts.routeCustomFields;\n store.tree = artifacts.tree;\n store.matcher = artifacts.matcher;\n store.urlParamsCache.clear();\n store.resolvedForwardMap = artifacts.resolvedForwardMap;\n\n // Install pre-compiled guards — no re-compile, no throw.\n for (const [name, factory, fn] of compiledActivate) {\n deps.addActivateGuard(name, factory, fn);\n }\n\n for (const [name, factory, fn] of compiledDeactivate) {\n deps.addDeactivateGuard(name, factory, fn);\n }\n}\n\n/**\n * COMMIT core for `update()` — the field-patch counterpart to\n * {@link adoptRouteArtifacts} (add/replace) / {@link commitTreeChanges} (remove)\n * / {@link resetStore} (clear), co-located here so all four route-CRUD commit\n * cores live in one file. Stays **NO_TREE_REBUILD**: an O(1) per-field patch\n * that never rebuilds the tree/matcher (so it must NOT funnel through\n * `adoptRouteArtifacts`).\n *\n * Prepare-then-commit (#951): every throwing step runs in PREPARE, before any\n * store write, so a rejected update leaves the route's prior config fully intact\n * — an async/cyclic `forwardTo` (#967), a guard factory that throws on compile\n * (#956 seam), a throwing custom-field getter, and the #961 handler-limit\n * pre-flight (#1046). Returns the structural fields for the caller's conditional\n * TREE_CHANGED emit, computed from the single destructure here so core invokes\n * each user getter once (#797 / #952 `null`-clears-definition-only preserved).\n */\nexport function commitRouteUpdate<Dependencies extends DefaultDependencies>(\n store: RoutesStore<Dependencies>,\n lifecycle: RouteLifecycleNamespace<Dependencies>,\n name: string,\n updates: RouteConfigUpdate<Dependencies>,\n): {\n forwardTo?: string | ForwardToCallback<Dependencies> | null | undefined;\n defaultParams?: Params | null | undefined;\n decodeParams?: ((params: Params) => Params) | null | undefined;\n encodeParams?: ((params: Params) => Params) | null | undefined;\n} {\n const {\n forwardTo,\n defaultParams,\n decodeParams,\n encodeParams,\n canActivate,\n canDeactivate,\n } = updates;\n\n // ===== PREPARE — compute every change into LOCALS. Any throw here aborts\n // before a single store write, so the whole field set is applied\n // all-or-nothing (#951).\n const forwardToPlan =\n forwardTo === undefined\n ? undefined\n : prepareForwardTo(name, forwardTo, store.config);\n\n const nextCustomFields = prepareCustomFields(store, name, updates);\n\n // Guard factories are compiled NOW (a throwing factory surfaces in PREPARE);\n // the precompiled function is installed in COMMIT without re-invoking the\n // factory, so a factory side effect runs exactly once (reuses the #956\n // compile-then-install seam). Compiled after the other prepares so a throw\n // upstream skips invoking the factory at all.\n const activateFn =\n canActivate === undefined || canActivate === null\n ? undefined\n : lifecycle.compileGuardFactory(canActivate, \"canActivate\");\n const deactivateFn =\n canDeactivate === undefined || canDeactivate === null\n ? undefined\n : lifecycle.compileGuardFactory(canDeactivate, \"canDeactivate\");\n\n // Pre-flight the #961 handler-limit before the COMMIT writes, so an at-limit\n // update that adds a NEW guard slot aborts before forwardTo / scalar config\n // land (#1046, #951). A slot is new only when `name` does not already hold a\n // guard of that type — an overwrite does not count.\n lifecycle.preflightHandlerLimit(\n activateFn === undefined ? [] : [name],\n deactivateFn === undefined ? [] : [name],\n false,\n );\n\n // ===== COMMIT — pure writes from here; nothing below throws.\n // Custom (plugin-defined) fields. Consumers read these lazily via\n // getRouteConfig (lifecycle hooks, preload, searchSchema), so no TREE_CHANGED\n // is needed — the next read sees the new value; the caller's emit stays\n // structural-only by design (О-7).\n if (nextCustomFields !== undefined) {\n if (Object.keys(nextCustomFields).length > 0) {\n store.routeCustomFields[name] = nextCustomFields;\n } else {\n delete store.routeCustomFields[name];\n }\n }\n\n if (forwardToPlan !== undefined) {\n store.config.forwardMap = forwardToPlan.forwardMap;\n store.config.forwardFnMap = forwardToPlan.forwardFnMap;\n store.resolvedForwardMap = forwardToPlan.resolved;\n }\n\n commitScalarConfig(store, name, {\n defaultParams,\n decodeParams,\n encodeParams,\n });\n\n // Install the guards from their PREPARE-phase precompiled functions; a `null`\n // clears the definition-origin guard only (#952). See commitGuardUpdate.\n commitGuardUpdate(lifecycle, \"activate\", name, canActivate, activateFn);\n commitGuardUpdate(lifecycle, \"deactivate\", name, canDeactivate, deactivateFn);\n\n return { forwardTo, defaultParams, decodeParams, encodeParams };\n}\n\n/**\n * PREPARE step for a `forwardTo` update (#951 atomicity): computes the new\n * forward maps and the resolved forward chain into LOCALS and returns them\n * WITHOUT touching the store. A throw here — an async `forwardTo` (#967) or a\n * cycle surfaced by `refreshForwardMap` — aborts `update()` before any field is\n * committed. The caller writes the returned bundle into the store in its COMMIT\n * phase. (Mirrors the build-then-swap shape of #698, but the swap is deferred to\n * the caller so it can be sequenced with the other prepared fields.)\n */\nfunction prepareForwardTo<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n name: string,\n forwardTo: string | ForwardToCallback<Dependencies> | null,\n config: RouteConfig,\n): {\n forwardMap: RouteConfig[\"forwardMap\"];\n forwardFnMap: RouteConfig[\"forwardFnMap\"];\n resolved: Record<string, string>;\n} {\n // #967: reject an async forwardTo at update time — parity with add/replace\n // (registerForwardTo runs the same check on the build path). A no-op for\n // string/null. Without this the async callback is stored silently and\n // surfaces later as a generic \"must return a string, got object\" TypeError\n // from #resolveDynamicForward at navigation. Runs first, before any clone.\n assertForwardToNotAsync(forwardTo, name);\n\n const forwardMap = Object.assign(\n Object.create(null) as RouteConfig[\"forwardMap\"],\n config.forwardMap,\n );\n const forwardFnMap = Object.assign(\n Object.create(null) as RouteConfig[\"forwardFnMap\"],\n config.forwardFnMap,\n );\n\n if (forwardTo === null) {\n delete forwardMap[name];\n delete forwardFnMap[name];\n } else if (typeof forwardTo === \"string\") {\n delete forwardFnMap[name];\n forwardMap[name] = forwardTo;\n } else {\n delete forwardMap[name];\n forwardFnMap[name] = forwardTo;\n }\n\n const resolved = refreshForwardMap({ ...config, forwardMap });\n\n return { forwardMap, forwardFnMap, resolved };\n}\n\n/**\n * PREPARE step for a route's plugin-defined **custom fields** (#951) — the\n * `update` counterpart to how `add`/`replace` register them\n * (`registerSingleRouteHandlers`). A custom field is any patch key not in\n * {@link STANDARD_ROUTE_KEYS}.\n *\n * Computes the merged record and RETURNS it for the caller to commit;\n * `undefined` means no custom-field key was present, so the caller leaves the\n * store untouched. Semantics mirror the scalar fields in\n * {@link commitScalarConfig}: shallow-merge by patch key, `null` removes a\n * single field, `undefined` is a no-op (leaves the field untouched). When the\n * merge empties the record, the caller drops the whole entry so `getRouteConfig`\n * returns `undefined` — symmetric with `add`, which only stores a record when at\n * least one custom field exists.\n *\n * Reading the custom-field getters HERE (in PREPARE, not at commit) is what lets\n * a throwing getter abort the whole update before any field is written. The\n * merged record is a **fresh object**, never mutated in place: `cloneRouter`\n * shares per-route custom-field records by reference (`Object.assign`), so\n * replacing the reference keeps a clone isolated from post-clone updates on the\n * source.\n */\nfunction prepareCustomFields<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n store: RoutesStore<Dependencies>,\n name: string,\n updates: RouteConfigUpdate<Dependencies>,\n): Record<string, unknown> | undefined {\n let next: Record<string, unknown> | undefined;\n\n // `Object.keys` (not `Object.entries`): a value is read only AFTER the\n // standard-key guard, so structural-field getters — already read once by\n // `update`'s destructuring — are not re-invoked. `Object.entries` would read\n // every value eagerly, double-invoking a `defaultParams`/`forwardTo` getter\n // and breaking the \"user getter called once\" invariant.\n // eslint-disable-next-line unicorn/prefer-object-iterable-methods -- see above\n for (const key of Object.keys(updates)) {\n if (STANDARD_ROUTE_KEYS.has(key)) {\n continue;\n }\n\n const value = (updates as Record<string, unknown>)[key];\n\n // `undefined` mirrors the structural path: leave the field untouched.\n if (value === undefined) {\n continue;\n }\n\n // Clone-on-first-write — keeps clones (which alias this record) isolated.\n next ??= { ...store.routeCustomFields[name] };\n\n if (value === null) {\n delete next[key];\n } else {\n next[key] = value;\n }\n }\n\n return next;\n}\n\n/**\n * COMMIT step for the scalar config fields of an update (#951): writes\n * `defaultParams` / `decodeParams` / `encodeParams` in place. These assignments\n * are pure and never throw, so they run in the COMMIT phase after every throwing\n * field has been validated in PREPARE. `forwardTo` is handled separately — it\n * has its own throwing prepare step ({@link prepareForwardTo}).\n */\nfunction commitScalarConfig<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n store: RoutesStore<Dependencies>,\n name: string,\n updates: {\n defaultParams?: Params | null | undefined;\n decodeParams?: ((params: Params) => Params) | null | undefined;\n encodeParams?: ((params: Params) => Params) | null | undefined;\n },\n): void {\n if (updates.defaultParams !== undefined) {\n if (updates.defaultParams === null) {\n delete store.config.defaultParams[name];\n } else {\n store.config.defaultParams[name] = updates.defaultParams;\n }\n }\n\n if (updates.decodeParams !== undefined) {\n if (updates.decodeParams === null) {\n delete store.config.decoders[name];\n } else {\n const decoder = updates.decodeParams;\n\n store.config.decoders[name] = (params: Params): Params =>\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime fallback if user-provided decoder violates its return type\n decoder(params) ?? params;\n }\n }\n\n if (updates.encodeParams !== undefined) {\n if (updates.encodeParams === null) {\n delete store.config.encoders[name];\n } else {\n const encoder = updates.encodeParams;\n\n store.config.encoders[name] = (params: Params): Params =>\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime fallback if user-provided encoder violates its return type\n encoder(params) ?? params;\n }\n }\n}\n\n/**\n * COMMIT step for one guard field of an update (#951). `undefined` is a no-op;\n * `null` clears the DEFINITION-origin guard only, preserving an external guard\n * (#952); a factory installs together with its PREPARE-phase `precompiledFn`\n * (no re-compile — #956 seam). Extracted from `update()` so its prepare/commit\n * orchestration stays within the cognitive-complexity budget.\n */\nfunction commitGuardUpdate<Dependencies extends DefaultDependencies>(\n lifecycle: RouteLifecycleNamespace<Dependencies>,\n kind: \"activate\" | \"deactivate\",\n name: string,\n value: GuardFnFactory<Dependencies> | null | undefined,\n precompiledFn: GuardFn | undefined,\n): void {\n if (value === undefined) {\n return;\n }\n\n if (kind === \"activate\") {\n if (value === null) {\n lifecycle.clearCanActivate(name, \"definition\");\n } else {\n lifecycle.addCanActivate(name, value, true, precompiledFn);\n }\n } else if (value === null) {\n lifecycle.clearCanDeactivate(name, \"definition\");\n } else {\n lifecycle.addCanDeactivate(name, value, true, precompiledFn);\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createRoutesStore<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n routes: Route<Dependencies>[],\n matcherOptions?: CreateMatcherOptions,\n): RoutesStore<Dependencies> {\n // Initial routes are a standalone set at rootPath \"\" — same build the\n // prepare-then-commit `replace` path uses. Guards land in the pending maps,\n // flushed by `flushPendingGuards()` at the end of the Router constructor\n // (#1331); `setDependencies` itself is a pure assignment.\n //\n // Reject the silent-corruption cases BEFORE building, giving the constructor\n // parity with `add()` / `replace()` — the third and last route-population\n // entry point (#1351): within-batch duplicate names (#953/#968) and reserved\n // \"@@\" names (#954). Without these the constructor silently last-wins a\n // duplicate-name sibling (the first route is dropped → its deep-link 404s)\n // while add/replace throw. `methodName` is \"addRoute\" so all three entry\n // points surface the identical bare-core error. (Duplicate PATHS are already\n // rejected downstream by the path-matcher backstop #1153, so they are not\n // re-checked here.)\n assertNoInternalNamesInBatch(routes, \"addRoute\");\n assertNoDuplicateNamesInBatch(routes, \"\", \"addRoute\");\n\n const artifacts = buildReplaceArtifacts(routes, \"\", matcherOptions);\n\n const store: RoutesStore<Dependencies> = {\n // Deferred access: the getter runs only after `store` is initialized.\n get definitions() {\n return routeTreeToDefinitions(store.tree);\n },\n config: artifacts.config,\n tree: artifacts.tree,\n matcher: artifacts.matcher,\n urlParamsCache: new Map(),\n resolvedForwardMap: artifacts.resolvedForwardMap,\n routeCustomFields: artifacts.routeCustomFields,\n rootPath: \"\",\n matcherOptions,\n depsStore: undefined,\n lifecycleNamespace: undefined,\n pendingCanActivate: artifacts.pendingCanActivate,\n pendingCanDeactivate: artifacts.pendingCanDeactivate,\n };\n\n return store;\n}\n","// packages/core/src/transitionPath.ts\n\nimport { getStateMetaParams } from \"./stateMetaStore\";\n\nimport type { State } from \"@real-router/types\";\n\n/**\n * Parameters extracted from a route segment.\n * Maps parameter names to their string values.\n */\ntype PrimitiveParam = string | number | boolean;\n\n/**\n * Represents a transition path between two router states.\n * Contains information about which route segments need to be activated/deactivated.\n */\nexport interface TransitionPath {\n /** The common ancestor route segment where paths diverge */\n intersection: string;\n /** Route segments that need to be deactivated (in reverse order) */\n toDeactivate: string[];\n /** Route segments that need to be activated (in order) */\n toActivate: string[];\n}\n\n// Constants for better maintainability\nconst ROUTE_SEGMENT_SEPARATOR = \".\";\nconst EMPTY_INTERSECTION = \"\";\nconst DEFAULT_ROUTE_NAME = \"\";\nconst FROZEN_EMPTY_ARRAY: string[] = [];\n\nObject.freeze(FROZEN_EMPTY_ARRAY);\n\n/**\n * Handles conversion of route names with many segments (5+).\n * Internal helper for nameToIDs function.\n *\n * Uses optimized hybrid approach: split to get segments, then slice original\n * string to build cumulative paths. This approach is 65-81% faster than\n * string concatenation for typical cases (5-10 segments).\n *\n * @param name - Route name with 5 or more segments\n * @returns Array of cumulative segment IDs\n * @throws {Error} If route depth exceeds maximum allowed\n * @internal\n */\nfunction nameToIDsGeneral(name: string): string[] {\n // We know there are at least 5 segments at this point (after fast paths)\n const segments = name.split(ROUTE_SEGMENT_SEPARATOR);\n const segmentCount = segments.length;\n\n // First segment is always just itself\n const ids: string[] = [segments[0]];\n\n // Calculate cumulative lengths and slice from original string\n // This avoids repeated string concatenation (O(k²) → O(k))\n let cumulativeLength = segments[0].length;\n\n for (let i = 1; i < segmentCount - 1; i++) {\n cumulativeLength += 1 + segments[i].length; // +1 for dot separator\n ids.push(name.slice(0, cumulativeLength));\n }\n\n // Last segment is always the full route name\n ids.push(name);\n\n return ids;\n}\n\nconst PRIMITIVE_TYPES: ReadonlySet<string> = new Set([\n \"string\",\n \"number\",\n \"boolean\",\n]);\n\nfunction isPrimitive(value: unknown): value is PrimitiveParam {\n return PRIMITIVE_TYPES.has(typeof value);\n}\n\n/**\n * Compares segment parameters between two states without creating intermediate objects.\n * Returns true if all primitive params for the given segment are equal in both states.\n */\nfunction segmentParamsEqual(\n name: string,\n toMetaParams: Record<string, unknown>,\n toState: State,\n fromState: State,\n): boolean {\n const keys = toMetaParams[name];\n\n if (!keys || typeof keys !== \"object\") {\n return true;\n }\n\n for (const key of Object.keys(keys)) {\n const toVal = toState.params[key];\n const fromVal = fromState.params[key];\n\n if (\n isPrimitive(toVal) &&\n isPrimitive(fromVal) &&\n String(toVal) !== String(fromVal)\n ) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * Finds the point where two state paths diverge based on segments and parameters.\n * Compares both segment names and their parameters to find the first difference.\n *\n * @param toMetaParams - Cached meta.params from toState (avoids per-segment WeakMap lookup)\n * @param toState - Target state\n * @param fromState - Source state\n * @param toStateIds - Segment IDs for target state\n * @param fromStateIds - Segment IDs for source state\n * @param maxI - Maximum index to check (minimum of both arrays)\n * @returns Index of first difference, or maxI if all checked segments match\n */\nfunction pointOfDifference(\n toMetaParams: Record<string, unknown>,\n toState: State,\n fromState: State,\n toStateIds: string[],\n fromStateIds: string[],\n maxI: number,\n): number {\n for (let i = 0; i < maxI; i++) {\n const toSegment = toStateIds[i];\n const fromSegment = fromStateIds[i];\n\n // Different segment names - immediate difference\n if (toSegment !== fromSegment) {\n return i;\n }\n\n if (!segmentParamsEqual(toSegment, toMetaParams, toState, fromState)) {\n return i;\n }\n }\n\n return maxI;\n}\n\n/**\n * Converts a route name to an array of hierarchical segment identifiers.\n * Each segment ID includes all parent segments in the path.\n *\n * @param name - Route name in dot notation (e.g., 'users.profile.edit')\n * @returns Array of cumulative segment IDs\n * @throws {Error} If route depth exceeds maximum allowed depth\n *\n * @example\n * // Simple route\n * nameToIDs('users');\n * // Returns: ['users']\n *\n * @example\n * // Nested route\n * nameToIDs('users.profile.edit');\n * // Returns: ['users', 'users.profile', 'users.profile.edit']\n *\n * @example\n * // Empty string (root route)\n * nameToIDs('');\n * // Returns: ['']\n *\n * @remarks\n * Input parameter is NOT validated in this function for performance reasons.\n * Validation significantly slows down nameToIDs execution.\n * The input should be validated by the function/method that calls nameToIDs.\n */\n// Module-global cache (shared across all router instances): bounded in practice by\n// the app's route-name vocabulary, which is stable across cloneRouter() requests, so\n// it does not grow per request. Intentionally NOT cleared on dispose() — it is not\n// per-router, so one router's teardown must not evict entries other routers rely on.\nconst nameToIDsCache = new Map<string, string[]>();\n\nexport function nameToIDs(name: string): string[] {\n const cached = nameToIDsCache.get(name);\n\n // Stryker disable next-line BlockStatement: equivalent — dropping the cache-hit early return recomputes the identical frozen id chain (the cache is a perf optimization, not a correctness gate).\n if (cached) {\n return cached;\n }\n\n const result = computeNameToIDs(name);\n\n Object.freeze(result);\n nameToIDsCache.set(name, result);\n\n return result;\n}\n\nfunction computeNameToIDs(name: string): string[] {\n if (!name) {\n return [DEFAULT_ROUTE_NAME];\n }\n\n const firstDot = name.indexOf(ROUTE_SEGMENT_SEPARATOR);\n\n if (firstDot === -1) {\n return [name];\n }\n\n const secondDot = name.indexOf(ROUTE_SEGMENT_SEPARATOR, firstDot + 1);\n\n if (secondDot === -1) {\n return [name.slice(0, firstDot), name];\n }\n\n const thirdDot = name.indexOf(ROUTE_SEGMENT_SEPARATOR, secondDot + 1);\n\n // Stryker disable next-line UnaryOperator,BlockStatement: equivalent — inverting/emptying the 3-segment fast path routes the name through nameToIDsGeneral (below), which yields the identical id chain (same rationale as the L242 ArithmeticOperator disable). The ConditionalExpression/EqualityOperator siblings stay live (→true and !== are killed).\n if (thirdDot === -1) {\n return [name.slice(0, firstDot), name.slice(0, secondDot), name];\n }\n\n // Stryker disable next-line ArithmeticOperator: equivalent — `thirdDot - 1` makes fourthDot non-(-1), routing 5+ segment names through nameToIDsGeneral, which yields the identical id chain.\n const fourthDot = name.indexOf(ROUTE_SEGMENT_SEPARATOR, thirdDot + 1);\n\n // Stryker disable next-line UnaryOperator,BlockStatement: equivalent — inverting/emptying the 4-segment fast path routes the name through nameToIDsGeneral (below), which yields the identical id chain (same rationale as the L242 ArithmeticOperator disable). The ConditionalExpression/EqualityOperator siblings stay live (→true and !== are killed).\n if (fourthDot === -1) {\n return [\n name.slice(0, firstDot),\n name.slice(0, secondDot),\n name.slice(0, thirdDot),\n name,\n ];\n }\n\n return nameToIDsGeneral(name);\n}\n\n/**\n * Calculates the transition path between two router states.\n * Determines which route segments need to be deactivated and activated\n * to transition from one state to another.\n *\n * @param toState - Target state to transition to\n * @param fromState - Current state to transition from (optional)\n * @returns Transition path with intersection and segments to activate/deactivate\n *\n * @throws {TypeError} When toState is null or undefined\n * @throws {TypeError} When toState is not an object\n * @throws {TypeError} When toState.name is missing or not a string\n * @throws {TypeError} When toState.params is missing or not an object\n * @throws {TypeError} When toState.path is missing or not a string\n * @throws {TypeError} When toState.name contains invalid route format:\n * - Contains only whitespace (e.g., \" \")\n * - Has consecutive dots (e.g., \"users..profile\")\n * - Has leading/trailing dots (e.g., \".users\" or \"users.\")\n * - Segments don't match pattern [a-zA-Z_][a-zA-Z0-9_-]* (e.g., \"users.123\")\n * - Contains spaces or special characters (e.g., \"users profile\")\n * - Exceeds maximum length (8192 characters)\n * @throws {TypeError} When fromState is provided and has any of the validation errors listed above for toState\n *\n * @example\n * // ✅ Valid calls\n * getTransitionPath({ name: 'users.profile', params: {}, path: '/users/profile' });\n * getTransitionPath(toState, fromState);\n * getTransitionPath({ name: '', params: {}, path: '/' }); // root route\n *\n * @example\n * // ❌ Invalid calls that throw TypeError\n * getTransitionPath(null); // toState is null\n * getTransitionPath(undefined); // toState is undefined\n * getTransitionPath({}); // missing required fields\n * getTransitionPath({ name: 123, params: {}, path: '/' }); // name not a string\n * getTransitionPath({ name: 'home', path: '/' }); // missing params\n * getTransitionPath({ name: 'users..profile', params: {}, path: '/' }); // consecutive dots\n * getTransitionPath({ name: '.users', params: {}, path: '/' }); // leading dot\n * getTransitionPath({ name: 'users.', params: {}, path: '/' }); // trailing dot\n * getTransitionPath({ name: 'users profile', params: {}, path: '/' }); // contains space\n * getTransitionPath({ name: 'users.123', params: {}, path: '/' }); // segment starts with number\n * getTransitionPath(validToState, { name: 'invalid..route', params: {}, path: '/' }); // fromState invalid\n *\n * @example\n * // Full activation (no fromState)\n * getTransitionPath(makeState('users.profile'));\n * // Returns: {\n * // intersection: '',\n * // toActivate: ['users', 'users.profile'],\n * // toDeactivate: []\n * // }\n *\n * @example\n * // Partial transition with common ancestor\n * getTransitionPath(\n * makeState('users.profile'),\n * makeState('users.list')\n * );\n * // Returns: {\n * // intersection: 'users',\n * // toActivate: ['users.profile'],\n * // toDeactivate: ['users.list']\n * // }\n *\n * @example\n * // Complete route change\n * getTransitionPath(\n * makeState('admin.dashboard'),\n * makeState('users.profile')\n * );\n * // Returns: {\n * // intersection: '',\n * // toActivate: ['admin', 'admin.dashboard'],\n * // toDeactivate: ['users.profile', 'users']\n * // }\n */\n// Single-entry cache: shouldUpdateNode calls getTransitionPath N times per\n// navigation with the same state objects (once per subscribed node).\n// Cache by reference eliminates N-1 redundant computations.\n// Module-global (≤2 State refs); not cleared on dispose — negligible, not per-router.\nlet cached1To: State | undefined;\nlet cached1From: State | undefined;\nlet cached1Result: TransitionPath | null = null;\n\nlet cached2To: State | undefined;\nlet cached2From: State | undefined;\nlet cached2Result: TransitionPath | null = null;\n\nfunction computeTransitionPath(\n toState: State,\n fromState?: State,\n): TransitionPath {\n // ===== FAST PATH 1: Initial navigation (no fromState) =====\n // This is the best performing case in benchmarks (5M ops/sec)\n if (!fromState) {\n return {\n intersection: EMPTY_INTERSECTION,\n toActivate: nameToIDs(toState.name),\n toDeactivate: FROZEN_EMPTY_ARRAY,\n };\n }\n\n // ===== FAST PATH 3: Missing meta requires full reload =====\n // Single WeakMap lookup per state, reused in pointOfDifference/segmentParamsEqual\n const toMetaParams = getStateMetaParams(toState);\n const fromMetaParams = getStateMetaParams(fromState);\n\n if (!toMetaParams && !fromMetaParams) {\n // FAST PATH 3 (both states meta-less). Consumers that land here read the\n // result order-INSENSITIVELY, so the from-chain is returned as-is\n // (root→leaf, no reverse needed):\n // • `shouldUpdateNode` reads `toDeactivate` by MEMBERSHIP (`.includes`).\n // • Externally-supplied meta-less states (e.g. a plugin passing a raw\n // `{name, params, path}` to `navigateToState`) land here. Since #1170,\n // `navigateToState` carries the source's WeakMap meta across its writable\n // shell, so start()/popstate states are NOT meta-less. A `replace()`\n // survivor stays meta-less but is benign: the next transition's `toState`\n // always carries meta (buildNavigateState), so this both-meta-less path\n // is not reached from it.\n // (`canNavigateTo` no longer reaches this path — since #970 it builds its\n // toState WITH meta, mirroring buildNavigateState.)\n // The navigate pipeline always carries meta (buildNavigateState) → STANDARD\n // PATH below, which trims the shared ancestor and reverses correctly.\n return {\n intersection: EMPTY_INTERSECTION,\n toActivate: nameToIDs(toState.name),\n toDeactivate: nameToIDs(fromState.name),\n };\n }\n\n // ===== STANDARD PATH: Routes with parameters =====\n const toStateIds = nameToIDs(toState.name);\n const fromStateIds = nameToIDs(fromState.name);\n // Stryker disable next-line MethodExpression: equivalent — Math.max reads one index past the shorter id array; that slot is undefined, so the `toSegment !== fromSegment` check in pointOfDifference returns the same divergence index Math.min would stop at.\n const maxI = Math.min(fromStateIds.length, toStateIds.length);\n\n const i = pointOfDifference(\n (toMetaParams ?? fromMetaParams) as Record<string, unknown>,\n toState,\n fromState,\n toStateIds,\n fromStateIds,\n maxI,\n );\n\n // Optimization: Build deactivation list in reverse order directly\n // instead of slice(i).toReversed() which creates 2 arrays\n let toDeactivate: string[];\n\n if (i >= fromStateIds.length) {\n toDeactivate = FROZEN_EMPTY_ARRAY;\n } else if (i === 0 && fromStateIds.length === 1) {\n // Single-segment route: reversed = original, reuse cached frozen array\n toDeactivate = fromStateIds;\n } else {\n toDeactivate = [];\n\n for (let j = fromStateIds.length - 1; j >= i; j--) {\n toDeactivate.push(fromStateIds[j]);\n }\n }\n\n // Build activation list — reuse cached frozen array when using full list\n const toActivate = i === 0 ? toStateIds : toStateIds.slice(i);\n\n // Determine intersection point (common ancestor)\n const intersection = i > 0 ? fromStateIds[i - 1] : EMPTY_INTERSECTION;\n\n return {\n intersection,\n toDeactivate,\n toActivate,\n };\n}\n\nexport function getTransitionPath(\n toState: State,\n fromState?: State,\n): TransitionPath {\n // Stryker disable BlockStatement: equivalent — both cache short-circuits below; emptying either early-return recomputes the identical TransitionPath (computeTransitionPath is deterministic for the same to/from states) and re-caches it. Restored right after.\n if (\n cached1Result !== null &&\n toState === cached1To &&\n fromState === cached1From\n ) {\n return cached1Result;\n }\n\n if (\n cached2Result !== null &&\n toState === cached2To &&\n fromState === cached2From\n ) {\n return cached2Result;\n }\n // Stryker restore BlockStatement\n\n const result = computeTransitionPath(toState, fromState);\n\n cached2To = cached1To;\n cached2From = cached1From;\n cached2Result = cached1Result;\n\n cached1To = toState;\n cached1From = fromState;\n cached1Result = result;\n\n return result;\n}\n","// packages/core/src/namespaces/RoutesNamespace/RoutesNamespace.ts\n\nimport { logger } from \"@real-router/logger\";\n\nimport { DEFAULT_ROUTE_NAME } from \"./constants\";\nimport {\n matchSourceTrailingSlash,\n paramsMatch,\n paramsMatchExcluding,\n stripQueryDefaults,\n} from \"./helpers\";\nimport {\n createRoutesStore,\n rebuildTreeInPlace,\n resetStore,\n} from \"./routesStore\";\nimport { constants, DEFAULT_TRANSITION } from \"../../constants\";\nimport { getTransitionPath } from \"../../transitionPath\";\n\nimport type { RoutesStore } from \"./routesStore\";\nimport type { RoutesDependencies } from \"./types\";\nimport type { Route } from \"../../types\";\nimport type { RouteLifecycleNamespace } from \"../RouteLifecycleNamespace\";\nimport type {\n DefaultDependencies,\n ForwardToCallback,\n Options,\n Params,\n State,\n} from \"@real-router/types\";\nimport type {\n CreateMatcherOptions,\n RouteParams,\n RouteTree,\n RouteTreeState,\n} from \"route-tree\";\n\nfunction collectUrlParamsArray(segments: readonly RouteTree[]): string[] {\n const params: string[] = [];\n\n for (const segment of segments) {\n for (const param of segment.paramMeta.urlParams) {\n params.push(param);\n }\n }\n\n return params;\n}\n\nfunction createRouteState<P extends RouteParams = RouteParams>(\n matchResult: {\n readonly segments: readonly { fullName: string }[];\n readonly params: Readonly<Record<string, unknown>>;\n readonly meta: Readonly<Record<string, Record<string, \"url\" | \"query\">>>;\n },\n name?: string,\n): RouteTreeState<P> {\n // The matcher yields ≥1 segment for every successful match, each carrying the\n // cumulative route name as `fullName`, so the last element is always present.\n // (Formerly `buildNameFromSegments` with a `?? \"\"` fallback — that branch was\n // unreachable defensive cruft propped up by a white-box test; inlined here.)\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- matcher invariant: a successful match is never empty\n const resolvedName = name ?? matchResult.segments.at(-1)!.fullName;\n\n return {\n name: resolvedName,\n params: matchResult.params as P,\n meta: matchResult.meta,\n };\n}\n\ninterface CachedBuildPathOpts {\n readonly trailingSlash?: \"always\" | \"never\" | undefined;\n readonly queryParamsMode?: \"default\" | \"strict\" | \"loose\" | undefined;\n}\n\n/**\n * Independent namespace for managing routes.\n *\n * Static methods handle validation (called by facade).\n * Instance methods handle storage and business logic.\n */\nexport class RoutesNamespace<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n> {\n readonly #store: RoutesStore<Dependencies>;\n #cachedBuildPathOpts: CachedBuildPathOpts | undefined;\n // Source `options` reference captured on the first #getBuildPathOptions call;\n // used only by the dev-build immutability assertion below (#957).\n #cachedOptionsSource: Options | undefined;\n\n get #deps(): RoutesDependencies<Dependencies> {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.#store.depsStore!;\n }\n\n constructor(\n routes: Route<Dependencies>[] = [],\n matcherOptions?: CreateMatcherOptions,\n ) {\n this.#store = createRoutesStore(routes, matcherOptions);\n }\n\n /**\n * Creates a predicate function to check if a route node should be updated.\n * Note: Argument validation is done by facade (Router.ts) via validateShouldUpdateNodeArgs.\n */\n static shouldUpdateNode(\n nodeName: string,\n ): (toState: State, fromState?: State) => boolean {\n return (toState: State, fromState?: State): boolean => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!(toState && typeof toState === \"object\" && \"name\" in toState)) {\n throw new TypeError(\n \"[router.shouldUpdateNode] toState must be valid State object\",\n );\n }\n\n if (toState.transition.reload) {\n return true;\n }\n\n // Root node (DEFAULT_ROUTE_NAME === \"\") has no route-level identity — it\n // represents \"any route\". It must update on every transition so that\n // consumers subscribed via useRouteNode(\"\") (including RouteView at\n // the top of the tree) see every change. This matches the documented\n // contract in adapter docs: `useRouteNode(\"\")` — Root — ALL route\n // changes. See #519 for the missed transitions it was suffering from\n // (users → users.user had intersection=\"users\", leaving the root node\n // un-updated under a flat <Match segment=\"users.user\" exact> pattern).\n if (nodeName === DEFAULT_ROUTE_NAME) {\n return true;\n }\n\n const { intersection, toActivate, toDeactivate } = getTransitionPath(\n toState,\n fromState,\n );\n\n if (nodeName === intersection) {\n return true;\n }\n\n if (toActivate.includes(nodeName)) {\n return true;\n }\n\n return toDeactivate.includes(nodeName);\n };\n }\n\n // =========================================================================\n // Dependency injection\n // =========================================================================\n\n /**\n * Sets dependencies. Pure assignment — no side effects (#1331).\n *\n * The pending canActivate/canDeactivate factories from initial routes are\n * flushed separately by {@link flushPendingGuards}, called once wiring is\n * complete, so the order of the wire-* calls is unconstrained.\n */\n setDependencies(deps: RoutesDependencies<Dependencies>): void {\n this.#store.depsStore = deps;\n }\n\n /**\n * Registers the pending guard factories collected from initial route\n * definitions. Deferred out of {@link setDependencies} (#1331) so it runs on\n * a fully-built, fully-bound router: a guard factory that calls any\n * `router.*` method sees a ready instance instead of a half-assembled one.\n *\n * Invoked as the last step of the Router constructor. Idempotent after the\n * first call (the pending maps are cleared). Runtime `add()`/`replace()`\n * compile guards in their own PREPARE phase and never populate these maps.\n */\n flushPendingGuards(): void {\n const deps = this.#deps;\n\n for (const [routeName, handler] of this.#store.pendingCanActivate) {\n deps.addActivateGuard(routeName, handler);\n }\n\n this.#store.pendingCanActivate.clear();\n\n for (const [routeName, handler] of this.#store.pendingCanDeactivate) {\n deps.addDeactivateGuard(routeName, handler);\n }\n\n this.#store.pendingCanDeactivate.clear();\n }\n\n /**\n * Sets the lifecycle namespace reference.\n */\n setLifecycleNamespace(\n namespace: RouteLifecycleNamespace<Dependencies> | undefined,\n ): void {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.#store.lifecycleNamespace = namespace!;\n }\n\n // =========================================================================\n // Route tree operations\n // =========================================================================\n\n setRootPath(newRootPath: string): void {\n this.#store.rootPath = newRootPath;\n rebuildTreeInPlace(this.#store);\n }\n\n hasRoute(name: string): boolean {\n return this.#store.matcher.hasRoute(name);\n }\n\n clearRoutes(): void {\n resetStore(this.#store);\n }\n\n // =========================================================================\n // Path operations\n // =========================================================================\n\n /**\n * Builds a URL path for a route.\n * Note: Argument validation is done by facade (Router.ts) via validateBuildPathArgs.\n *\n * @param route - Route name\n * @param params - Route parameters\n * @param options - Router options\n */\n buildPath(route: string, params?: Params, options?: Options): string {\n if (route === constants.UNKNOWN_ROUTE) {\n return typeof params?.path === \"string\" ? params.path : \"\";\n }\n\n const paramsWithDefault = Object.hasOwn(\n this.#store.config.defaultParams,\n route,\n )\n ? { ...this.#store.config.defaultParams[route], ...params }\n : /* v8 ignore next -- @preserve: V8 can't track ?? branch in ternary; covered by buildPath tests without params */ (params ??\n {});\n\n const encodedParams =\n typeof this.#store.config.encoders[route] === \"function\"\n ? this.#store.config.encoders[route]({ ...paramsWithDefault })\n : paramsWithDefault;\n\n return this.#store.matcher.buildPath(\n route,\n encodedParams,\n this.#getBuildPathOptions(options),\n );\n }\n\n /**\n * Matches a URL path to a route in the tree.\n * Note: Argument validation is done by facade (Router.ts) via validateMatchPathArgs.\n */\n matchPath<P extends Params = Params>(\n path: string,\n options?: Options,\n ): State<P> | undefined {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- Router.ts always passes options\n const opts = options!;\n\n const matchResult = this.#store.matcher.match(path);\n\n if (!matchResult) {\n return undefined;\n }\n\n const routeState = createRouteState(matchResult);\n const { name, params, meta } = routeState;\n\n const decodedParams =\n typeof this.#store.config.decoders[name] === \"function\"\n ? this.#store.config.decoders[name](params)\n : params;\n\n const { name: routeName, params: routeParams } = this.#deps.forwardState<P>(\n name,\n decodedParams as P,\n );\n\n let builtPath = path;\n\n if (opts.rewritePathOnMatch) {\n const buildParams =\n typeof this.#store.config.encoders[routeName] === \"function\"\n ? this.#store.config.encoders[routeName]({\n ...(routeParams as Params),\n })\n : (routeParams as Record<string, unknown>);\n\n const ts = opts.trailingSlash;\n\n try {\n builtPath = this.#store.matcher.buildPath(routeName, buildParams, {\n trailingSlash: ts === \"never\" || ts === \"always\" ? ts : undefined,\n queryParamsMode: opts.queryParamsMode,\n });\n\n if (ts === \"preserve\") {\n builtPath = matchSourceTrailingSlash(path, builtPath);\n }\n } catch {\n // The match already succeeded (route found, params decoded); only the\n // post-match path rewrite threw — e.g. a custom encoder handed buildPath\n // a query value its codec cannot serialise. Keep the source path\n // un-rewritten rather than discard a valid match (#1157). Opposite of the\n // parse side (#737): there a throw means \"URL not understood\" → unmatched;\n // here the URL WAS matched and only re-canonicalisation failed.\n builtPath = path;\n }\n }\n\n return this.#deps.makeState<P>(routeName, routeParams, builtPath, meta);\n }\n\n /**\n * Applies forwardTo and returns resolved state with merged defaultParams.\n *\n * Merges params in order:\n * 1. Source route defaultParams\n * 2. Provided params\n * 3. Target route defaultParams (after resolving forwardTo)\n */\n forwardState<P extends Params = Params>(\n name: string,\n params: P,\n ): { name: string; params: P } {\n if (Object.hasOwn(this.#store.config.forwardFnMap, name)) {\n const paramsWithSourceDefaults = this.#mergeDefaultParams(name, params);\n const dynamicForward = this.#store.config.forwardFnMap[name];\n const resolved = this.#resolveDynamicForward(\n name,\n dynamicForward,\n params,\n );\n\n return {\n name: resolved,\n params: this.#mergeDefaultParams(resolved, paramsWithSourceDefaults),\n };\n }\n\n const staticForward = this.#store.resolvedForwardMap[name] ?? name;\n\n if (\n staticForward !== name &&\n Object.hasOwn(this.#store.config.forwardFnMap, staticForward)\n ) {\n const paramsWithSourceDefaults = this.#mergeDefaultParams(name, params);\n const targetDynamicForward =\n this.#store.config.forwardFnMap[staticForward];\n const resolved = this.#resolveDynamicForward(\n staticForward,\n targetDynamicForward,\n params,\n );\n\n return {\n name: resolved,\n params: this.#mergeDefaultParams(resolved, paramsWithSourceDefaults),\n };\n }\n\n if (staticForward !== name) {\n const paramsWithSourceDefaults = this.#mergeDefaultParams(name, params);\n\n return {\n name: staticForward,\n params: this.#mergeDefaultParams(\n staticForward,\n paramsWithSourceDefaults,\n ),\n };\n }\n\n return { name, params: this.#mergeDefaultParams(name, params) };\n }\n\n /**\n * Builds a RouteTreeState from already-resolved route name and params.\n * Called by Router.buildState after forwardState is applied at facade level.\n * This allows plugins to intercept forwardState.\n */\n buildStateResolved(\n resolvedName: string,\n resolvedParams: Params,\n ): RouteTreeState | undefined {\n const segments = this.#store.matcher.getSegmentsByName(resolvedName);\n\n if (!segments) {\n return undefined;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const meta = this.#store.matcher.getMetaByName(resolvedName)!;\n\n return createRouteState(\n { segments, params: resolvedParams, meta },\n resolvedName,\n );\n }\n\n // =========================================================================\n // Query operations\n // =========================================================================\n\n /**\n * Checks if a route is currently active.\n */\n isActiveRoute(\n name: string,\n params: Params = {},\n strictEquality = false,\n ignoreQueryParams = true,\n ): boolean {\n // Note: empty string check is handled by Router.ts facade\n const activeState = this.#deps.getState();\n\n if (!activeState) {\n return false;\n }\n\n const activeName = activeState.name;\n\n // Fast path: check if routes are related before expensive operations\n if (\n activeName !== name &&\n !activeName.startsWith(`${name}.`) &&\n !name.startsWith(`${activeName}.`)\n ) {\n return false;\n }\n\n const defaultParams = this.#store.config.defaultParams[name] as\n Params | undefined;\n\n // Exact match case\n if (strictEquality || activeName === name) {\n const effectiveParams = defaultParams\n ? { ...defaultParams, ...params }\n : params;\n\n const targetState: State = {\n name,\n params: effectiveParams,\n path: \"\",\n transition: DEFAULT_TRANSITION,\n context: {},\n };\n\n return this.#deps.areStatesEqual(\n targetState,\n activeState,\n ignoreQueryParams,\n );\n }\n\n // The fast path above lets through three relations: exact (handled in\n // the previous block), `activeName` descendant of `name`, and `name`\n // descendant of `activeName`. Only the first two count as \"active\" —\n // a link pointing DEEPER than the current state is a navigation option,\n // not an active state. Reject the descendant-of-active case explicitly.\n if (!activeName.startsWith(`${name}.`)) {\n return false;\n }\n\n // Hierarchical check: activeState is a descendant of target (name)\n const activeParams = activeState.params;\n\n if (!paramsMatch(params, activeParams)) {\n return false;\n }\n\n if (!defaultParams) {\n return true;\n }\n\n // Honor `ignoreQueryParams` symmetrically with the exact-match branch\n // above: query-only param differences (e.g. parent has\n // `defaultParams: { sort: \"asc\" }` while the active descendant is\n // `products.detail` with `params: { id: \"6\" }` and no sort) must not\n // disqualify an ancestor link from being active. Strip query-typed\n // keys of `name` from the defaults before comparison; URL-typed keys\n // (`:id`, `:role`, etc.) are still enforced.\n // `name` reaches this point only after the fast-path established a valid\n // hierarchical relation AND `defaultParams` is non-null — both imply the\n // matcher has registered the route. Since the #1414 skip-empty meta, a\n // fully-static route resolves to the shared EMPTY_ROUTE_META with no own\n // entry, so `getMetaByName(name)?.[name]` is undefined there — exactly the\n // \"nothing to strip\" signal stripQueryDefaults short-circuits on (a static\n // route cannot declare query params, hence cannot carry query defaults).\n const defaultsToCheck = ignoreQueryParams\n ? stripQueryDefaults(\n defaultParams,\n this.#store.matcher.getMetaByName(name)?.[name],\n )\n : defaultParams;\n\n return paramsMatchExcluding(defaultsToCheck, activeParams, params);\n }\n\n getMetaForState(\n name: string,\n ): Record<string, Record<string, \"url\" | \"query\">> | undefined {\n return this.#store.matcher.hasRoute(name)\n ? this.#store.matcher.getMetaByName(name)\n : undefined;\n }\n\n getUrlParams(name: string): string[] {\n const cached = this.#store.urlParamsCache.get(name);\n\n // Stryker disable next-line BlockStatement: equivalent — cache short-circuit; emptying the early-return recomputes the identical value (getUrlParams is deterministic per route name) and re-caches it. (ConditionalExpression stays live: `→true` returns undefined on a cache miss = killed.)\n if (cached !== undefined) {\n return cached;\n }\n\n const segments = this.#store.matcher.getSegmentsByName(name);\n const result = segments\n ? collectUrlParamsArray(segments as readonly RouteTree[])\n : [];\n\n this.#store.urlParamsCache.set(name, result);\n\n return result;\n }\n\n getStore(): RoutesStore<Dependencies> {\n return this.#store;\n }\n\n #mergeDefaultParams<P extends Params = Params>(\n routeName: string,\n params: P,\n ): P {\n if (Object.hasOwn(this.#store.config.defaultParams, routeName)) {\n return {\n ...this.#store.config.defaultParams[routeName],\n ...params,\n };\n }\n\n return params;\n }\n\n #getBuildPathOptions(options?: Options): CachedBuildPathOpts {\n // Stryker disable next-line BlockStatement: equivalent — cache short-circuit; emptying the early-return rebuilds the identical buildPath options (deterministic) and re-caches them. (ConditionalExpression stays live: `→false` always rebuilds but a real consumer test pins the cached identity.)\n if (this.#cachedBuildPathOpts) {\n /* v8 ignore next 5 -- @preserve: dev assertion guarding a future caller that passes per-call varying options; the sole caller (Router.buildPath, always via this.#options.get()) passes the same immutable, deep-frozen per-instance options, so this branch is unreachable through the public API by construction (#957) */\n if (options !== this.#cachedOptionsSource) {\n logger.warn(\n \"router.buildPath\",\n \"`options` differs from the cached source reference; router options are immutable per router instance, so the first-cached buildPath options are reused (#957).\",\n );\n }\n\n return this.#cachedBuildPathOpts;\n }\n\n this.#cachedOptionsSource = options;\n\n const ts = options?.trailingSlash;\n\n this.#cachedBuildPathOpts = Object.freeze({\n trailingSlash: ts === \"never\" || ts === \"always\" ? ts : undefined,\n queryParamsMode: options?.queryParamsMode,\n });\n\n return this.#cachedBuildPathOpts;\n }\n\n #resolveDynamicForward(\n startName: string,\n startFn: ForwardToCallback<Dependencies>,\n params: Params,\n ): string {\n const visited = new Set<string>([startName]);\n\n let current = startFn(this.#deps.getDependency, params);\n let depth = 0;\n const MAX_DEPTH = 100;\n\n if (typeof current !== \"string\") {\n throw new TypeError(\n `forwardTo callback must return a string, got ${typeof current}`,\n );\n }\n\n while (depth < MAX_DEPTH) {\n if (this.#store.matcher.getSegmentsByName(current) === undefined) {\n throw new Error(`Route \"${current}\" does not exist`);\n }\n\n if (visited.has(current)) {\n const chain = [...visited, current].join(\" → \");\n\n throw new Error(`Circular forwardTo: ${chain}`);\n }\n\n visited.add(current);\n\n if (Object.hasOwn(this.#store.config.forwardFnMap, current)) {\n const fn = this.#store.config.forwardFnMap[\n current\n ] as ForwardToCallback<Dependencies>;\n\n current = fn(this.#deps.getDependency, params);\n\n depth++;\n continue;\n }\n\n const staticForward = this.#store.config.forwardMap[current];\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (staticForward !== undefined) {\n current = staticForward;\n depth++;\n continue;\n }\n\n return current;\n }\n\n throw new Error(`forwardTo exceeds maximum depth of ${MAX_DEPTH}`);\n }\n}\n","// packages/core/src/RouterError.ts\n\nimport { errorCodes } from \"./constants\";\n\n// Pre-compute Set of error code values for O(1) lookup in setCode()\n// This avoids creating array and doing linear search on every setCode() call\nconst errorCodeValues = new Set(Object.values(errorCodes));\n\n// Reserved built-in properties - throw error if user tries to set these\nconst reservedProperties = new Set([\"code\", \"segment\", \"path\"]);\n\n// Reserved method names - silently ignore attempts to overwrite these\nconst reservedMethods = new Set([\n \"setCode\",\n \"setErrorInstance\",\n \"setAdditionalFields\",\n \"hasField\",\n \"getField\",\n \"toJSON\",\n]);\n\nexport class RouterError extends Error {\n [key: string]: unknown;\n\n // Using public properties to ensure structural compatibility\n // with RouterError interface in core-types\n readonly segment: string | undefined;\n readonly path: string | undefined;\n\n // Note: code appears to be writable but setCode() should be used\n // to properly update both code and message together\n code: string;\n\n /**\n * Creates a new RouterError instance.\n *\n * The options object accepts built-in fields (message, segment, path)\n * and any additional custom fields, which will all be attached to the error instance.\n *\n * @param code - The error code (e.g., \"ROUTE_NOT_FOUND\", \"CANNOT_ACTIVATE\")\n * @param options - Optional configuration object\n * @param options.message - Custom error message (defaults to code if not provided)\n * @param options.segment - The route segment where the error occurred\n * @param options.path - The full path where the error occurred\n *\n * @example\n * ```typescript\n * // Basic error\n * const err1 = new RouterError(\"ROUTE_NOT_FOUND\");\n *\n * // Error with custom message\n * const err2 = new RouterError(\"ERR\", { message: \"Something went wrong\" });\n *\n * // Error with context and custom fields\n * const err3 = new RouterError(\"CANNOT_ACTIVATE\", {\n * message: \"Insufficient permissions\",\n * segment: \"admin\",\n * path: \"/admin/users\",\n * userId: \"123\" // custom field\n * });\n * ```\n */\n constructor(\n code: string,\n {\n message,\n segment,\n path,\n ...rest\n }: {\n [key: string]: unknown;\n message?: string | undefined;\n segment?: string | undefined;\n path?: string | undefined;\n } = {},\n ) {\n super(message ?? code);\n\n // Subclasses don't auto-set `name`; without this `error.name` inherits\n // \"Error\", breaking `error.name === \"RouterError\"` checks at catch sites that\n // can't `instanceof` across bundle boundaries.\n this.name = \"RouterError\";\n\n this.code = code;\n this.segment = segment;\n this.path = path;\n\n // Assign custom fields, checking reserved properties and filtering out reserved method names\n // Issue #39: Throw for reserved properties to match setAdditionalFields behavior\n for (const [key, value] of Object.entries(rest)) {\n if (reservedProperties.has(key)) {\n throw new TypeError(\n `[RouterError] Cannot set reserved property \"${key}\"`,\n );\n }\n\n if (!reservedMethods.has(key)) {\n this[key] = value;\n }\n }\n }\n\n /**\n * Updates the error code and conditionally updates the message.\n *\n * If the current message is one of the standard error code values\n * (e.g., \"ROUTE_NOT_FOUND\", \"SAME_STATES\"), it will be replaced with the new code.\n * This allows keeping error messages in sync with codes when using standard error codes.\n *\n * If the message is custom (not a standard error code), it will be preserved.\n *\n * @param newCode - The new error code to set\n *\n * @example\n * // Message follows code (standard error code as message)\n * const err = new RouterError(\"ROUTE_NOT_FOUND\", { message: \"ROUTE_NOT_FOUND\" });\n * err.setCode(\"CUSTOM_ERROR\"); // message becomes \"CUSTOM_ERROR\"\n *\n * @example\n * // Custom message is preserved\n * const err = new RouterError(\"ERR\", { message: \"Custom error message\" });\n * err.setCode(\"NEW_CODE\"); // message stays \"Custom error message\"\n */\n setCode(newCode: string): void {\n this.code = newCode;\n\n // Only update message if it's a standard error code value (not a custom message)\n if (errorCodeValues.has(this.message)) {\n this.message = newCode;\n }\n }\n\n /**\n * Copies properties from another Error instance to this RouterError.\n *\n * This method updates the message, cause, and stack trace from the provided error.\n * Useful for wrapping native errors while preserving error context.\n *\n * @param err - The Error instance to copy properties from\n * @throws {TypeError} If err is null or undefined\n *\n * @example\n * ```typescript\n * const routerErr = new RouterError(\"TRANSITION_ERR\");\n * try {\n * // some operation that might fail\n * } catch (nativeErr) {\n * routerErr.setErrorInstance(nativeErr);\n * throw routerErr;\n * }\n * ```\n */\n setErrorInstance(err: Error): void {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!err) {\n throw new TypeError(\n \"[RouterError.setErrorInstance] err parameter is required and must be an Error instance\",\n );\n }\n\n this.message = err.message;\n this.cause = err.cause;\n this.stack = err.stack ?? \"\";\n }\n\n /**\n * Adds custom fields to the error object.\n *\n * This method allows attaching arbitrary data to the error for debugging or logging purposes.\n * All fields become accessible as properties on the error instance and are included in JSON serialization.\n *\n * Reserved method names (setCode, setErrorInstance, setAdditionalFields, hasField, getField, toJSON)\n * are automatically filtered out to prevent accidental overwriting of class methods.\n *\n * @param fields - Object containing custom fields to add to the error\n *\n * @example\n * ```typescript\n * const err = new RouterError(\"CANNOT_ACTIVATE\");\n * err.setAdditionalFields({\n * userId: \"123\",\n * attemptedRoute: \"/admin\",\n * reason: \"insufficient permissions\"\n * });\n *\n * console.log(err.userId); // \"123\"\n * console.log(JSON.stringify(err)); // includes all custom fields\n * ```\n */\n setAdditionalFields(fields: Record<string, unknown>): void {\n // Assign fields, throwing for reserved properties, silently ignoring methods\n for (const [key, value] of Object.entries(fields)) {\n if (reservedProperties.has(key)) {\n throw new TypeError(\n `[RouterError.setAdditionalFields] Cannot set reserved property \"${key}\"`,\n );\n }\n\n if (!reservedMethods.has(key)) {\n this[key] = value;\n }\n }\n }\n\n /**\n * Checks if a custom field exists on the error object.\n *\n * This method checks for both custom fields added via setAdditionalFields()\n * and built-in fields (code, message, segment, etc.).\n *\n * @param key - The field name to check\n * @returns `true` if the field exists, `false` otherwise\n *\n * @example\n * ```typescript\n * const err = new RouterError(\"ERR\", { segment: \"users\" });\n * err.setAdditionalFields({ userId: \"123\" });\n *\n * err.hasField(\"userId\"); // true\n * err.hasField(\"segment\"); // true\n * err.hasField(\"unknown\"); // false\n * ```\n */\n hasField(key: string): boolean {\n return key in this;\n }\n\n /**\n * Retrieves a custom field value from the error object.\n *\n * This method can access both custom fields and built-in fields.\n * Returns `undefined` if the field doesn't exist.\n *\n * @param key - The field name to retrieve\n * @returns The field value, or `undefined` if it doesn't exist\n *\n * @example\n * ```typescript\n * const err = new RouterError(\"ERR\");\n * err.setAdditionalFields({ userId: \"123\", role: \"admin\" });\n *\n * err.getField(\"userId\"); // \"123\"\n * err.getField(\"role\"); // \"admin\"\n * err.getField(\"code\"); // \"ERR\" (built-in field)\n * err.getField(\"unknown\"); // undefined\n * ```\n */\n getField(key: string): unknown {\n return this[key];\n }\n\n /**\n * Serializes the error to a JSON-compatible object.\n *\n * This method is automatically called by JSON.stringify() and includes:\n * - Built-in fields: code, message, segment (if set), path (if set)\n * - All custom fields added via setAdditionalFields() or constructor\n * - Excludes: stack trace (for security/cleanliness)\n *\n * @returns A plain object representation of the error, suitable for JSON serialization\n *\n * @example\n * ```typescript\n * const err = new RouterError(\"ROUTE_NOT_FOUND\", {\n * message: \"Route not found\",\n * path: \"/admin/users/123\"\n * });\n * err.setAdditionalFields({ userId: \"123\" });\n *\n * JSON.stringify(err);\n * // {\n * // \"code\": \"ROUTE_NOT_FOUND\",\n * // \"message\": \"Route not found\",\n * // \"path\": \"/admin/users/123\",\n * // \"userId\": \"123\"\n * // }\n * ```\n */\n toJSON(): Record<string, unknown> {\n const result: Record<string, unknown> = {\n code: this.code,\n message: this.message,\n };\n\n if (this.segment !== undefined) {\n result.segment = this.segment;\n }\n if (this.path !== undefined) {\n result.path = this.path;\n }\n\n // add all public fields\n // Using Set.has() for O(1) lookup instead of Array.includes() O(n)\n // Overall complexity: O(n) instead of O(n*m)\n const excludeKeys = new Set([\n \"code\",\n \"message\",\n \"segment\",\n \"path\",\n \"stack\",\n // `name` is now an own enumerable prop (constructor sets it to\n // \"RouterError\"); it's class metadata, not a custom field — keep it out of\n // the serialized output (preserves toJSON shape).\n \"name\",\n ]);\n\n for (const key in this) {\n if (Object.hasOwn(this, key) && !excludeKeys.has(key)) {\n result[key] = this[key];\n }\n }\n\n return result;\n }\n}\n","// packages/core/src/namespaces/NavigationNamespace/constants.ts\n\nimport { errorCodes } from \"../../constants\";\nimport { RouterError } from \"../../RouterError\";\n\nimport type { State } from \"@real-router/types\";\n\n// =============================================================================\n// Cached Errors & Rejected Promises (Performance Optimization)\n// =============================================================================\n// Pre-create error instances and rejected promises for sync error paths\n// in navigate(). Eliminates per-call allocations:\n// - new RouterError() — object + stack trace capture (~500ns-2μs)\n// - Promise.reject() — promise allocation\n// - .catch(handler) — derived promise from suppression\n//\n// Trade-off: All error instances share the same stack trace (points here).\n// This is acceptable because:\n// 1. These errors indicate expected conditions, not internal bugs\n// 2. Error code and message are sufficient for debugging\n// 3. The facade skips .catch() suppression for cached promises (zero alloc)\n// =============================================================================\n\nexport const CACHED_NOT_STARTED_ERROR = new RouterError(\n errorCodes.ROUTER_NOT_STARTED,\n);\n\nexport const CACHED_ROUTE_NOT_FOUND_ERROR = new RouterError(\n errorCodes.ROUTE_NOT_FOUND,\n);\n\nexport const CACHED_SAME_STATES_ERROR = new RouterError(errorCodes.SAME_STATES);\n\n// Pre-suppressed rejected promises — .catch() at module load prevents\n// unhandled rejection warnings. The facade skips additional .catch() calls\n// via the lastSyncRejected flag (zero derived-promise allocation).\nexport const CACHED_NOT_STARTED_REJECTION: Promise<State> = Promise.reject(\n CACHED_NOT_STARTED_ERROR,\n);\n\nexport const CACHED_ROUTE_NOT_FOUND_REJECTION: Promise<State> = Promise.reject(\n CACHED_ROUTE_NOT_FOUND_ERROR,\n);\n\nexport const CACHED_SAME_STATES_REJECTION: Promise<State> = Promise.reject(\n CACHED_SAME_STATES_ERROR,\n);\n\n// Suppress once at module load — prevents unhandled rejection events.\n// Subsequent .catch() / await by user code still works correctly:\n// a rejected promise stays rejected forever, each .catch() creates\n// its own derived promise and fires its handler.\nCACHED_NOT_STARTED_REJECTION.catch(() => {}); // NOSONAR -- intentional suppression, not a promise chain\nCACHED_ROUTE_NOT_FOUND_REJECTION.catch(() => {}); // NOSONAR\nCACHED_SAME_STATES_REJECTION.catch(() => {}); // NOSONAR\n","import { errorCodes, constants } from \"../../../constants\";\nimport { RouterError } from \"../../../RouterError\";\n\nimport type { NavigationDependencies, NavigationContext } from \"../types\";\nimport type {\n NavigationOptions,\n State,\n TransitionMeta,\n} from \"@real-router/types\";\n\ntype MutableTransitionMeta = {\n -readonly [K in keyof TransitionMeta]: TransitionMeta[K];\n};\n\nfunction buildTransitionMeta(\n fromState: State | undefined,\n opts: NavigationOptions,\n toDeactivate: string[],\n toActivate: string[],\n intersection: string,\n): TransitionMeta {\n Object.freeze(toDeactivate);\n Object.freeze(toActivate);\n\n const segments = Object.freeze({\n deactivated: toDeactivate,\n activated: toActivate,\n intersection,\n });\n\n const meta: MutableTransitionMeta = {\n phase: \"activating\",\n reason: \"success\",\n segments,\n };\n\n if (fromState?.name !== undefined) {\n meta.from = fromState.name;\n }\n\n if (opts.reload !== undefined) {\n meta.reload = opts.reload;\n }\n\n if (opts.replace !== undefined) {\n meta.replace = opts.replace;\n }\n\n if (opts.redirected !== undefined) {\n meta.redirected = opts.redirected;\n }\n\n return Object.freeze(meta);\n}\n\nfunction stripSignal({\n signal: _,\n ...rest\n}: NavigationOptions): NavigationOptions {\n return rest;\n}\n\nexport function completeTransition(\n deps: NavigationDependencies,\n nav: NavigationContext,\n): State {\n const { toState, fromState, opts, toDeactivate, toActivate, intersection } =\n nav;\n\n if (\n toState.name !== constants.UNKNOWN_ROUTE &&\n !deps.hasRoute(toState.name)\n ) {\n const err = new RouterError(errorCodes.ROUTE_NOT_FOUND, {\n routeName: toState.name,\n });\n\n deps.sendTransitionFail(toState, fromState, err);\n\n throw err;\n }\n\n if (fromState) {\n for (const name of toDeactivate) {\n if (!toActivate.includes(name) && nav.canDeactivateFunctions.has(name)) {\n deps.clearCanDeactivate(name);\n }\n }\n }\n\n (toState as { transition: TransitionMeta }).transition = buildTransitionMeta(\n fromState,\n opts,\n toDeactivate,\n toActivate,\n intersection,\n );\n\n const finalState = Object.freeze(toState);\n\n deps.setState(finalState);\n\n const transitionOpts = opts.signal === undefined ? opts : stripSignal(opts);\n\n deps.sendTransitionDone(finalState, fromState, transitionOpts);\n\n return finalState;\n}\n","// packages/core/src/namespaces/NavigationNamespace/transition/errorHandling.ts\n\nimport { errorCodes } from \"../../../constants\";\nimport { RouterError } from \"../../../RouterError\";\n\nimport type { NavigationDependencies } from \"../types\";\nimport type { State } from \"@real-router/types\";\n\nexport function routeTransitionError(\n deps: NavigationDependencies,\n error: unknown,\n toState: State,\n fromState: State | undefined,\n): void {\n const routerError = error as RouterError;\n\n if (\n routerError.code === errorCodes.TRANSITION_CANCELLED ||\n routerError.code === errorCodes.ROUTE_NOT_FOUND\n ) {\n return;\n }\n\n deps.sendTransitionFail(toState, fromState, routerError);\n}\n\nexport function handleGuardError(\n error: unknown,\n errorCode: string,\n segment: string,\n): never {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n // A guard can also signal a quiet cancel by throwing\n // RouterError(TRANSITION_CANCELLED) directly — the same intent as a thrown\n // AbortError. Preserve it as-is instead of letting rethrowAsRouterError\n // overwrite the code with CANNOT_ACTIVATE / CANNOT_DEACTIVATE: that code\n // drives the downstream suppression (routeTransitionError early-returns,\n // fire-and-forget stays silent), so re-coding would surface the intended\n // quiet cancel as a reported transition error (#933).\n if (\n error instanceof RouterError &&\n error.code === errorCodes.TRANSITION_CANCELLED\n ) {\n throw error;\n }\n\n rethrowAsRouterError(error, errorCode, segment);\n}\n\n/**\n * Error metadata structure for transition errors.\n * Contains information extracted from caught exceptions.\n */\nexport interface SyncErrorMetadata {\n [key: string]: unknown;\n message?: string;\n stack?: string | undefined;\n cause?: unknown;\n segment?: string;\n}\n\n/**\n * Re-throws a caught error as a RouterError with the given error code.\n * If the error is already a RouterError, sets the code directly.\n * Otherwise wraps it with wrapSyncError metadata.\n */\nexport function rethrowAsRouterError(\n error: unknown,\n errorCode: string,\n segment: string,\n): never {\n if (error instanceof RouterError) {\n error.setCode(errorCode);\n\n throw error;\n }\n\n throw new RouterError(errorCode, wrapSyncError(error, segment));\n}\n\n// Own-enumerable keys that must never be copied from a thrown object onto the\n// RouterError metadata:\n// - `code` / `segment` / `path` are reserved — the RouterError constructor\n// throws a TypeError on them (#39).\n// - `then` would make the RouterError itself thenable, so a consumer that\n// awaits it (or passes it through Promise.resolve / returns it from an async\n// function) would have it assimilated as a Promise instead of treated as a\n// plain rejection reason (#947).\nconst reservedRouterErrorProps = new Set([\"code\", \"segment\", \"path\", \"then\"]);\n\n/**\n * Wraps a synchronously thrown value into structured error metadata.\n *\n * This helper extracts useful debugging information from various thrown values:\n * - Error instances: extracts message, stack, and cause (ES2022+)\n * - Plain objects: spreads properties into metadata\n * - Primitives (string, number, etc.): returns minimal metadata\n *\n * @param thrown - The value caught in a try-catch block\n * @param segment - Route segment name (for lifecycle hooks)\n * @returns Structured error metadata for RouterError\n */\nexport function wrapSyncError(\n thrown: unknown,\n segment: string,\n): SyncErrorMetadata {\n const base: SyncErrorMetadata = { segment };\n\n // Handle Error instances - extract all useful properties\n if (thrown instanceof Error) {\n return {\n ...base,\n message: thrown.message,\n stack: thrown.stack,\n // Error.cause requires ES2022+ - safely access if present\n ...(\"cause\" in thrown &&\n thrown.cause !== undefined && { cause: thrown.cause }),\n };\n }\n\n // Handle plain objects - spread properties into metadata, filtering reserved props\n if (thrown && typeof thrown === \"object\") {\n const filtered: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(thrown)) {\n // Skip reserved / hazardous keys: #39 (constructor TypeError on code/\n // segment/path) and #947 (`then` would make the error thenable).\n if (!reservedRouterErrorProps.has(key)) {\n filtered[key] = value;\n }\n }\n\n return { ...base, ...filtered };\n }\n\n // Primitives (string, number, boolean, null, undefined, symbol, bigint)\n // Return base metadata only - the primitive value isn't useful as metadata\n return base;\n}\n","import { handleGuardError } from \"./errorHandling\";\nimport { errorCodes } from \"../../../constants\";\nimport { RouterError } from \"../../../RouterError\";\n\nimport type { GuardFn, State } from \"@real-router/types\";\n\nasync function resolveAsyncGuard(\n promise: Promise<boolean>,\n errorCode: string,\n segment: string,\n): Promise<void> {\n let result: boolean;\n\n try {\n result = await promise;\n } catch (error: unknown) {\n handleGuardError(error, errorCode, segment);\n\n return; // unreachable — handleGuardError returns never\n }\n\n if (!result) {\n throw new RouterError(errorCode, { segment });\n }\n}\n\nasync function resolveRemainingGuards( // NOSONAR -- params kept flat to avoid object allocation on hot path\n guards: Map<string, GuardFn>,\n segments: string[],\n errorCode: string,\n toState: State,\n fromState: State | undefined,\n signal: AbortSignal | undefined,\n isActive: () => boolean,\n startIndex: number,\n firstResult: Promise<boolean>,\n firstSegment: string,\n): Promise<void> {\n await resolveAsyncGuard(firstResult, errorCode, firstSegment);\n\n for (let i = startIndex; i < segments.length; i++) {\n // Stryker disable next-line BlockStatement: equivalent — emptying the body drops the async-tail cancellation throw, but supersession is independently enforced by the navigationId/isCurrentNav checks in NavigationNamespace, so the navigation still rejects with TRANSITION_CANCELLED (full suite green). The ConditionalExpression sibling stays live (its →true variant always-cancels and is killed by async-guard tests).\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n const segment = segments[i];\n const guardFn = guards.get(segment);\n\n if (!guardFn) {\n continue;\n }\n\n // Stryker disable next-line BooleanLiteral: equivalent — guardResult is unconditionally reassigned by guardFn() on the next line, or the catch calls handleGuardError() (returns never), so the init value is never read.\n let guardResult: boolean | Promise<boolean> = false;\n\n try {\n guardResult = guardFn(toState, fromState, signal);\n } catch (error: unknown) {\n handleGuardError(error, errorCode, segment);\n }\n\n if (guardResult instanceof Promise) {\n await resolveAsyncGuard(guardResult, errorCode, segment);\n continue;\n }\n\n if (!guardResult) {\n throw new RouterError(errorCode, { segment });\n }\n }\n}\n\nasync function finishAsyncPipeline( // NOSONAR\n deactivateCompletion: Promise<void>,\n activateGuards: Map<string, GuardFn>,\n toActivate: string[],\n shouldActivate: boolean,\n toState: State,\n fromState: State | undefined,\n signal: AbortSignal,\n isActive: () => boolean,\n emitLeaveApprove: () => Promise<void> | undefined,\n): Promise<void> {\n await deactivateCompletion;\n\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n const leaveResult = emitLeaveApprove();\n\n if (leaveResult !== undefined) {\n await leaveResult;\n\n // Stryker disable next-line BlockStatement: equivalent — defensive-redundancy: this post-leave cancellation re-check is already enforced upstream (navigationId / isCurrentNav supersession in NavigationNamespace), so emptying the throw is unobservable. Mirrors the disabled L42/L115/L228 cancel-checks.\n /* v8 ignore next 3 -- @preserve: V8 cannot track cancellation check through async leave continuation after Promise.allSettled */\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n }\n\n if (shouldActivate) {\n const pending = runGuards(\n activateGuards,\n toActivate,\n errorCodes.CANNOT_ACTIVATE,\n toState,\n fromState,\n signal,\n isActive,\n );\n\n if (pending !== undefined) {\n await pending;\n }\n\n // Stryker disable next-line BlockStatement: equivalent — same async-tail cancellation throw as L42; dropping it is covered by the outer navigationId guard (full suite green). CE sibling stays live (→true killed by async-guard tests).\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n }\n}\n\nexport function executeGuardPipeline( // NOSONAR\n deactivateGuards: Map<string, GuardFn>,\n activateGuards: Map<string, GuardFn>,\n toDeactivate: string[],\n toActivate: string[],\n shouldDeactivate: boolean,\n shouldActivate: boolean,\n toState: State,\n fromState: State | undefined,\n signal: AbortSignal,\n isActive: () => boolean,\n emitLeaveApprove: () => Promise<void> | undefined,\n): Promise<void> | undefined {\n if (shouldDeactivate) {\n const pending = runGuards(\n deactivateGuards,\n toDeactivate,\n errorCodes.CANNOT_DEACTIVATE,\n toState,\n fromState,\n signal,\n isActive,\n );\n\n if (pending !== undefined) {\n return finishAsyncPipeline(\n pending,\n activateGuards,\n toActivate,\n shouldActivate,\n toState,\n fromState,\n signal,\n isActive,\n emitLeaveApprove,\n );\n }\n }\n\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n const leaveResult = emitLeaveApprove();\n\n if (leaveResult !== undefined) {\n return finishAfterAsyncLeave(\n leaveResult,\n /* v8 ignore next -- @preserve: false-branch unreachable — navigateToNotFound bypasses guards pipeline */\n shouldActivate ? activateGuards : undefined,\n toActivate,\n toState,\n fromState,\n signal,\n isActive,\n );\n }\n\n if (shouldActivate) {\n return runGuards(\n activateGuards,\n toActivate,\n errorCodes.CANNOT_ACTIVATE,\n toState,\n fromState,\n signal,\n isActive,\n );\n }\n\n return undefined;\n}\n\nasync function finishAfterAsyncLeave(\n leaveCompletion: Promise<void>,\n activateGuards: Map<string, GuardFn> | undefined,\n toActivate: string[],\n toState: State,\n fromState: State | undefined,\n signal: AbortSignal,\n isActive: () => boolean,\n): Promise<void> {\n await leaveCompletion;\n\n // Stryker disable next-line BlockStatement: equivalent — defensive-redundancy: same post-leave cancellation re-check as guardPhase's other `!isActive()` guards; supersession is enforced upstream (navigationId / isCurrentNav), so emptying the throw is unobservable.\n /* v8 ignore next 3 -- @preserve: unreachable after #663 — signal abort\n mid-leave rejects via settleLeavePromises, so `await leaveCompletion`\n throws directly instead of completing with a stale isActive() */\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n /* v8 ignore next -- @preserve: false-branch unreachable — navigateToNotFound bypasses guards pipeline */\n if (activateGuards !== undefined) {\n const pending = runGuards(\n activateGuards,\n toActivate,\n errorCodes.CANNOT_ACTIVATE,\n toState,\n fromState,\n signal,\n isActive,\n );\n\n if (pending !== undefined) {\n await pending;\n }\n\n // Stryker disable next-line BlockStatement: equivalent — same async-tail cancellation throw as L42; dropping it is covered by the outer navigationId guard (full suite green). CE sibling stays live (→true killed by async-guard tests).\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n }\n}\n\nfunction runGuards(\n guards: Map<string, GuardFn>,\n segments: string[],\n errorCode: string,\n toState: State,\n fromState: State | undefined,\n signal: AbortSignal | undefined,\n isActive: () => boolean,\n): Promise<void> | undefined {\n for (const [i, segment] of segments.entries()) {\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n const guardFn = guards.get(segment);\n\n if (!guardFn) {\n continue;\n }\n\n // Stryker disable next-line BooleanLiteral: equivalent — guardResult is unconditionally reassigned by guardFn() on the next line, or the catch calls handleGuardError() (returns never), so the init value is never read.\n let guardResult: boolean | Promise<boolean> = false;\n\n try {\n guardResult = guardFn(toState, fromState, signal);\n } catch (error: unknown) {\n handleGuardError(error, errorCode, segment);\n }\n\n if (guardResult instanceof Promise) {\n return resolveRemainingGuards(\n guards,\n segments,\n errorCode,\n toState,\n fromState,\n signal,\n isActive,\n i + 1,\n guardResult,\n segment,\n );\n }\n\n if (!guardResult) {\n throw new RouterError(errorCode, { segment });\n }\n }\n\n return undefined;\n}\n","import { logger } from \"@real-router/logger\";\n\nimport {\n CACHED_NOT_STARTED_REJECTION,\n CACHED_ROUTE_NOT_FOUND_ERROR,\n CACHED_ROUTE_NOT_FOUND_REJECTION,\n CACHED_SAME_STATES_ERROR,\n CACHED_SAME_STATES_REJECTION,\n} from \"./constants\";\nimport { completeTransition } from \"./transition/completeTransition\";\nimport { routeTransitionError } from \"./transition/errorHandling\";\nimport { executeGuardPipeline } from \"./transition/guardPhase\";\nimport { EMPTY_PARAMS, errorCodes, constants } from \"../../constants\";\nimport { RouterError } from \"../../RouterError\";\nimport { getStateMetaParams, setStateMetaParams } from \"../../stateMetaStore\";\nimport { getTransitionPath, nameToIDs } from \"../../transitionPath\";\n\nimport type { NavigationContext, NavigationDependencies } from \"./types\";\nimport type { TransitionPath } from \"../../transitionPath\";\nimport type {\n GuardFn,\n NavigationOptions,\n Params,\n State,\n TransitionMeta,\n} from \"@real-router/types\";\n\nconst FROZEN_ACTIVATED: string[] = Object.freeze([\n constants.UNKNOWN_ROUTE,\n]) as unknown as string[];\nconst FROZEN_REPLACE_OPTS: NavigationOptions = Object.freeze({ replace: true });\n\nfunction forceReplaceFromUnknown(\n opts: NavigationOptions,\n fromState: State | undefined,\n): NavigationOptions {\n return fromState?.name === constants.UNKNOWN_ROUTE && !opts.replace\n ? { ...opts, replace: true }\n : opts;\n}\n\nfunction isSameNavigation(\n fromState: State | undefined,\n opts: NavigationOptions,\n toState: State,\n): boolean {\n return (\n !!fromState &&\n !opts.reload &&\n !opts.force &&\n fromState.path === toState.path\n );\n}\n\n/**\n * Independent namespace for managing navigation.\n *\n * Handles navigate(), navigateToDefault(), navigateToNotFound(), and transition state.\n *\n * Performance: navigate() uses optimistic sync execution — guards run synchronously\n * until one returns a Promise, then switches to async. This eliminates Promise/AbortController\n * overhead for the common case (no guards or sync guards).\n */\nexport class NavigationNamespace {\n // Stryker disable next-line BooleanLiteral: equivalent — reset to false at the top of every navigate()/navigateToState()/navigateToDefault(), so the initializer value is never observed.\n lastSyncResolved = false;\n lastSyncRejected = false;\n #deps!: NavigationDependencies;\n #currentController: AbortController | null = null;\n #navigationId = 0;\n\n // =========================================================================\n // Dependency injection\n // =========================================================================\n\n setDependencies(deps: NavigationDependencies): void {\n this.#deps = deps;\n }\n\n // =========================================================================\n // Instance methods\n // =========================================================================\n\n navigate(\n name: string,\n params: Params,\n opts: NavigationOptions,\n ): Promise<State> {\n this.lastSyncResolved = false;\n const deps = this.#deps;\n\n // Fast-path sync rejections: cached error + cached Promise.reject\n // No allocations, no throw/catch overhead, facade skips .catch() suppression\n if (!deps.canNavigate()) {\n // Stryker disable next-line BooleanLiteral: equivalent — #721 optimization flag, not a correctness gate. Not flagging the cached (pre-suppressed) rejection routes the facade to the else-branch, which re-attaches a harmless .catch; no observable difference.\n this.lastSyncRejected = true;\n\n return CACHED_NOT_STARTED_REJECTION;\n }\n\n let toState: State | undefined;\n\n try {\n toState = deps.buildNavigateState(name, params);\n } catch (error) {\n /* v8 ignore next 3 -- @preserve: reachable only via validator-driven\n throws from buildNavigateState (validateStateBuilderArgs) — covered\n in @real-router/validation-plugin's suite, not in core. */\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- preserve original throw shape from user-provided buildNavigateState\n return Promise.reject(error);\n }\n\n if (!toState) {\n deps.emitTransitionError(\n undefined,\n deps.getState(),\n CACHED_ROUTE_NOT_FOUND_ERROR,\n );\n // Stryker disable next-line BooleanLiteral: equivalent — #721 optimization flag (see L95): the cached rejection is suppressed by the facade else-branch regardless of the flag.\n this.lastSyncRejected = true;\n\n return CACHED_ROUTE_NOT_FOUND_REJECTION;\n }\n\n return this.#executeNavigation(toState, opts);\n }\n\n /**\n * Navigate to a fully-built `State` directly, skipping `buildNavigateState`\n * (forwardState + buildPath + meta lookup). Used by URL plugins after they\n * have already produced a `State` from a browser-initiated event via\n * `api.matchPath(url)` — see issue #525.\n *\n * Semantics vs. `navigate(name, params, opts)`:\n * - `forwardState` is NOT re-applied. matchPath already runs it; reapplying\n * is redundant in the idempotent case and can race in the dynamic case.\n * - `buildPath` is NOT re-run. The caller's `state.path` is used as-is —\n * so `trailingSlash:\"preserve\"` matchedState paths flow through unchanged\n * (closes #525 Q2). `buildPath` interceptors do NOT run; the URL the\n * user navigated to is the source of truth for this code path.\n * - All other pipeline steps run unchanged: SAME_STATES check, FSM\n * transition, guards, `subscribeLeave`, `completeTransition`,\n * plugin lifecycle hooks.\n */\n navigateToState(state: State, opts: NavigationOptions): Promise<State> {\n this.lastSyncResolved = false;\n const deps = this.#deps;\n\n if (!deps.canNavigate()) {\n // Stryker disable next-line BooleanLiteral: equivalent — #721 optimization flag (see L95): the cached rejection is suppressed by the facade else-branch regardless of the flag.\n this.lastSyncRejected = true;\n\n return CACHED_NOT_STARTED_REJECTION;\n }\n\n // Reject states whose route no longer exists (e.g. the route tree was\n // mutated between matchPath and navigateToState). UNKNOWN_ROUTE is\n // structurally legal — it is the navigateToNotFound output shape.\n if (state.name !== constants.UNKNOWN_ROUTE && !deps.hasRoute(state.name)) {\n const err = new RouterError(errorCodes.ROUTE_NOT_FOUND, {\n routeName: state.name,\n });\n\n deps.emitTransitionError(undefined, deps.getState(), err);\n\n // This is a FRESH reject (carries `routeName`), not one of the\n // pre-suppressed CACHED_*_REJECTION singletons. `lastSyncRejected`\n // contractually means \"I returned a pre-suppressed cached rejection —\n // skip your .catch()\", so leaving it unset lets the facade attach its\n // own suppression. Setting it here leaked an unhandledRejection on\n // fire-and-forget calls (#721).\n return Promise.reject(err);\n }\n\n // States from `matchPath` are deeply frozen (`freezeStateInPlace`).\n // `completeTransition` mutates `toState.transition` and `context` is\n // intentionally extensible for plugin claim writes, so we hand the\n // pipeline a writable shell — same shape `makeState(skipFreeze=true)`\n // produces. `params` stays referentially shared (already frozen).\n // `transition` is omitted so completeTransition can assign it.\n const writableState = {\n name: state.name,\n params: state.params,\n path: state.path,\n context: { ...state.context },\n } as State;\n\n // Carry the route-meta binding (#1170). `matchPath` / `makeState` attach it\n // to the frozen source `state` via a WeakMap keyed by object reference, so\n // the fresh writable shell would otherwise be meta-less. Without it, two\n // consecutive popstate navigations make both `toState` AND `fromState`\n // meta-less, dropping `getTransitionPath` to FAST PATH 3 (full chains):\n // ancestor guards re-run and browser-back can block where `navigate()`\n // succeeds.\n const meta = getStateMetaParams(state);\n\n if (meta !== undefined) {\n setStateMetaParams(writableState, meta);\n }\n\n return this.#executeNavigation(writableState, opts);\n }\n\n navigateToDefault(opts: NavigationOptions): Promise<State> {\n // Reset the sync-resolution flag on entry, mirroring navigate() and\n // navigateToState(). start() leaves `lastSyncResolved = true`, and the\n // early reject paths below return before delegating to navigate(), so a\n // stale `true` would make the facade take the \"already resolved\" branch\n // and skip .catch() suppression — leaking an unhandledRejection on\n // fire-and-forget calls (#721).\n this.lastSyncResolved = false;\n const deps = this.#deps;\n const options = deps.getOptions();\n\n if (!options.defaultRoute) {\n return Promise.reject(\n new RouterError(errorCodes.ROUTE_NOT_FOUND, {\n routeName: \"defaultRoute not configured\",\n }),\n );\n }\n\n let route: string;\n let params: Params;\n\n try {\n ({ route, params } = deps.resolveDefault());\n } catch (error) {\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- preserve original throw shape from user-provided resolveDefault callback\n return Promise.reject(error);\n }\n\n if (!route) {\n return Promise.reject(\n new RouterError(errorCodes.ROUTE_NOT_FOUND, {\n routeName: \"defaultRoute resolved to empty\",\n }),\n );\n }\n\n return this.navigate(route, params, opts);\n }\n\n navigateToNotFound(path: string): State {\n // #1186 — liveness gate. This internal commit primitive has no FSM\n // transition of its own, so without this check a `dispose()` that lands\n // while a start-interceptor is parked (FSM already DISPOSED) would let the\n // resuming pipeline commit an UNKNOWN_ROUTE state on the disposed router and\n // `start()` resolve. Symmetric with `navigateToState`'s `canNavigate()` gate\n // (the matched-route branch is already protected). `!isActive()` also covers\n // a merely-stopped (IDLE) router: the only reachable path to that is a direct\n // `router.navigateToNotFound()` on a stopped instance (internal callers run\n // during STARTING, which is active), so the ROUTER_DISPOSED code is slightly\n // broad there — fail-closed is deliberate (committing on a stopped router is\n // out of contract), and the disposed race is the case that matters.\n if (!this.#deps.isActive()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n this.#abortPreviousNavigation();\n\n const fromState = this.#deps.getState();\n const deactivated: string[] = fromState\n ? nameToIDs(fromState.name).toReversed()\n : [];\n\n Object.freeze(deactivated);\n\n const segments: TransitionMeta[\"segments\"] = {\n deactivated,\n activated: FROZEN_ACTIVATED,\n intersection: \"\",\n };\n\n Object.freeze(segments);\n\n const transitionMeta: TransitionMeta = {\n phase: \"activating\",\n ...(fromState && { from: fromState.name }),\n reason: \"success\",\n replace: true,\n segments,\n };\n\n Object.freeze(transitionMeta);\n\n const state: State = {\n name: constants.UNKNOWN_ROUTE,\n params: EMPTY_PARAMS,\n path,\n transition: transitionMeta,\n context: {},\n };\n\n Object.freeze(state);\n\n this.#deps.setState(state);\n this.#deps.emitTransitionSuccess(state, fromState, FROZEN_REPLACE_OPTS);\n\n return state;\n }\n\n /**\n * Aborts and releases the in-flight navigation's `AbortController` (waking the\n * parked async pipeline via `onInternalAbort`). This is the\n * **effect** of the FSM `CANCEL` action (`handleCancel` → injected\n * `deps.abortCurrentController`), not something cancellation sources call\n * directly — so \"FSM `CANCEL` ⟹ controller aborted\" holds in one place (RFC\n * navigation-cancellation-unification §5). `reason` (e.g. an external\n * `opts.signal`'s reason, #943) becomes the controller's `signal.reason`;\n * defaults to `TRANSITION_CANCELLED`.\n */\n abortCurrentController(reason?: unknown): void {\n this.#currentController?.abort(\n reason ?? new RouterError(errorCodes.TRANSITION_CANCELLED),\n );\n this.#currentController = null;\n }\n\n #executeNavigation(toState: State, opts: NavigationOptions): Promise<State> {\n const deps = this.#deps;\n let fromState: State | undefined;\n let transitionStarted = false;\n let controller: AbortController | null = null;\n\n try {\n fromState = deps.getState();\n opts = forceReplaceFromUnknown(opts, fromState);\n\n if (isSameNavigation(fromState, opts, toState)) {\n deps.emitTransitionError(toState, fromState, CACHED_SAME_STATES_ERROR);\n // Stryker disable next-line BooleanLiteral: equivalent — #721 optimization flag (see L95): the cached rejection is suppressed by the facade else-branch regardless of the flag.\n this.lastSyncRejected = true;\n\n return CACHED_SAME_STATES_REJECTION;\n }\n\n this.#abortPreviousNavigation(opts.signal);\n\n // Stryker disable next-line UpdateOperator: equivalent — `#navigationId` is only ever compared by identity (`!== myId`) to detect supersession; uniqueness per navigation is all that matters, so `--` (decreasing ids) is indistinguishable from `++`.\n const myId = ++this.#navigationId;\n\n // #1169 commit-gate — liveness snapshot captured BEFORE the pre-commit\n // listener windows. A listener's `stop()`/`dispose()` runs `clearAll()`,\n // which empties the listener lists, so the marker must be read now, not at\n // the commit site (that self-destruct was the QB/QE hole, RFC §5-bis).\n // `suspendable` is true only when a synchronous supersede is reachable — an\n // external `opts.signal`, `subscribeLeave` listeners, or a pre-commit\n // plugin listener (`onTransitionStart` / `onTransitionLeaveApprove`); the\n // pure synchronous navigate (none of these) is uncancellable and skips the\n // gate, keeping the #307 hot path perf-neutral.\n const suspendable =\n opts.signal !== undefined ||\n deps.hasLeaveListeners() ||\n deps.hasPreCommitListeners();\n\n deps.startTransition(toState, fromState);\n transitionStarted = true;\n\n // Post-`startTransition` supersession is now caught at the commit-gate\n // below (before `completeTransition`'s setState): a `stop()`/`dispose()`/\n // external-abort from the TRANSITION_START listener leaves the FSM in\n // IDLE/DISPOSED, which `!deps.isActive()` detects. (Async supersession is\n // additionally caught in `#finishAsyncNavigation` / the guard pipeline's\n // `isCurrentNav`; a reentrant navigate() is banned — REENTRANT_NAVIGATION.)\n\n const [canDeactivateFunctions, canActivateFunctions] =\n deps.getLifecycleFunctions();\n const isUnknownRoute = toState.name === constants.UNKNOWN_ROUTE;\n\n const transitionPath = getTransitionPath(toState, fromState);\n const { toDeactivate, toActivate, intersection } = transitionPath;\n\n const shouldDeactivate =\n fromState && !opts.forceDeactivate && toDeactivate.length > 0;\n const shouldActivate = !isUnknownRoute && toActivate.length > 0;\n const hasGuards =\n canDeactivateFunctions.size > 0 || canActivateFunctions.size > 0;\n\n const confirmedToState = toState;\n\n if (!hasGuards) {\n const asyncLeave = this.#handleNoGuardsLeave(\n confirmedToState,\n fromState,\n myId,\n opts,\n transitionPath,\n canDeactivateFunctions,\n );\n\n if (asyncLeave !== undefined) {\n return asyncLeave;\n }\n }\n\n // Stryker disable next-line ConditionalExpression: equivalent — running the guard pipeline on the no-guards path does not double-emit LEAVE_APPROVE (full suite green with `if (true)`); the BlockStatement mutant stays live (killed by guarded-route tests).\n // eslint-disable-next-line unicorn/prefer-else-if -- two exhaustive `if`s read clearer here than an else-if; merging cascades into no-negated-condition / no-unnecessary-condition in this hot guard-setup branch\n if (hasGuards) {\n controller = new AbortController();\n this.#currentController = controller;\n const isCurrentNav = () =>\n this.#navigationId === myId && deps.isActive();\n\n const signal = controller.signal;\n\n const emitLeaveApproveCallback = (): Promise<void> | undefined => {\n deps.sendLeaveApprove(confirmedToState, fromState);\n\n if (deps.hasLeaveListeners()) {\n return deps.awaitLeaveListeners(\n confirmedToState,\n fromState,\n signal,\n );\n }\n\n return undefined;\n };\n\n const guardCompletion = executeGuardPipeline(\n canDeactivateFunctions,\n canActivateFunctions,\n toDeactivate,\n toActivate,\n !!shouldDeactivate,\n shouldActivate,\n toState,\n fromState,\n signal,\n isCurrentNav,\n emitLeaveApproveCallback,\n );\n\n if (guardCompletion !== undefined) {\n return this.#finishAsyncNavigation(\n guardCompletion,\n {\n toState,\n fromState,\n opts,\n toDeactivate,\n toActivate,\n intersection,\n canDeactivateFunctions,\n },\n controller,\n myId,\n );\n }\n\n if (!isCurrentNav()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n this.#cleanupController(controller, false);\n }\n\n // #1169 commit-gate — refuse to commit a navigation cancelled or\n // terminated during a listener window, BEFORE `completeTransition`'s\n // setState. The FSM table (D-full) already prevents the forceState\n // resurrection; this prevents the state commit that precedes the emit.\n // Gated on `suspendable` so the pure sync hot path pays nothing. A\n // `stop()`/`dispose()` from the listener lands the FSM in IDLE/DISPOSED\n // (caught by `!isActive()`); an external `opts.signal` abort is caught\n // directly. No `#navigationId` check: a reentrant navigate() (the only\n // thing that could bump it synchronously) is banned (REENTRANT_NAVIGATION,\n // §4), so on this sync path `#navigationId === myId` always holds — async\n // supersede is caught in `#finishAsyncNavigation`'s `isCurrentNav`.\n if (suspendable && (!deps.isActive() || opts.signal?.aborted === true)) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n const finalState = completeTransition(deps, {\n toState,\n fromState,\n opts,\n toDeactivate,\n toActivate,\n intersection,\n canDeactivateFunctions,\n });\n\n // Mark sync-resolution only AFTER completeTransition returns. It emits\n // TRANSITION_SUCCESS; listener throws are all isolated via onListenerError\n // (the emitter re-throws nothing now that re-entrant emits are coalesced,\n // #1033, and reentrant navigate/CRUD are banned), so the emit itself does\n // not throw. Should completeTransition throw for any other reason, setting\n // the flag optimistically BEFORE it would leave the flag stale-true, so the\n // facade would read lastSyncResolved and skip its suppressing `.catch()` —\n // the rejection would then leak as a Node unhandledRejection. Post-emit\n // placement keeps the flag false on a throw, so control falls to catch and\n // the facade attaches its `.catch`.\n // Stryker disable next-line BooleanLiteral: equivalent — flipping to false routes the facade to the else-branch, which attaches a harmless .catch to an already-resolved promise; there is no rejection to suppress.\n this.lastSyncResolved = true;\n\n return Promise.resolve(finalState);\n } catch (error) {\n this.#handleNavigateError(\n error,\n controller,\n transitionStarted,\n toState,\n fromState,\n );\n\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- preserve original throw shape from guards or transition pipeline\n return Promise.reject(error);\n }\n }\n\n async #finishAsyncNavigation(\n guardCompletion: Promise<void>,\n nav: NavigationContext,\n controller: AbortController,\n myId: number,\n ): Promise<State> {\n const deps = this.#deps;\n const isActive = () =>\n this.#navigationId === myId &&\n !controller.signal.aborted &&\n deps.isActive();\n\n const externalSignal = nav.opts.signal;\n let onExternalAbort: (() => void) | undefined;\n let onInternalAbort: (() => void) | undefined;\n let succeeded = false;\n let failureReason: unknown;\n\n // #1018: race the guard completion against the controller's abort so a\n // non-cooperative guard whose Promise never settles (and ignores `signal`)\n // cannot wedge navigate() forever. `abortRace` RESOLVES on abort, so the\n // post-race `isActive()` check below throws TRANSITION_CANCELLED — the same\n // path that already handles a guard which swallows the abort and resolves\n // `true`. `stop()`/`dispose()`/supersede all abort the controller. Mirrors\n // the leave-path protection `settleLeavePromises` (#663/#673).\n const abortRace = new Promise<void>((resolve) => {\n if (controller.signal.aborted) {\n resolve();\n\n return;\n }\n\n onInternalAbort = () => {\n resolve();\n };\n\n controller.signal.addEventListener(\"abort\", onInternalAbort, {\n once: true,\n });\n });\n\n // Consume `guardCompletion` when the abort wins the race: a slow or\n // never-settling guard that settles later then has no awaiter, which would\n // surface as an unhandled rejection without this catch.\n guardCompletion.catch(() => {\n /* settlement consumed — the race already decided the navigation */\n });\n\n try {\n if (externalSignal) {\n if (externalSignal.aborted) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED, {\n reason: externalSignal.reason,\n });\n }\n\n // Bridge an external `{ signal }` abort onto the internal controller.\n // It is NOT scoped to `controller.signal` (the old `{ signal }` option)\n // because success no longer aborts the controller (#722) — the listener\n // is detached explicitly in `finally` instead.\n onExternalAbort = () => {\n // (#1030): route the external abort through the FSM. The\n // `CANCEL` action aborts the internal controller (waking THIS pipeline)\n // with the external `reason` (#943 — surfaces via the leave signal) AND\n // returns the FSM to READY, atomically. No direct `controller.abort`\n // here — \"FSM CANCEL ⟹ controller aborted\" lives in one place\n // (handleCancel). onExternalAbort only fires while the navigation is in\n // flight (the listener is removed in `finally` once it settles), so the\n // FSM is always cancellable here.\n deps.cancelNavigation(externalSignal.reason);\n };\n // Stryker disable next-line ObjectLiteral: equivalent — `{ once: true }` is redundant: the per-navigation signal aborts at most once and is discarded unaborted on success, and the `finally` block explicitly removeEventListener's it.\n externalSignal.addEventListener(\"abort\", onExternalAbort, {\n // Stryker disable next-line BooleanLiteral: equivalent — `once` redundant (see ObjectLiteral above); the listener is explicitly removed in `finally`.\n once: true,\n });\n }\n\n await Promise.race([guardCompletion, abortRace]);\n\n if (!isActive()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n const state = completeTransition(deps, nav);\n\n succeeded = true;\n\n return state;\n } catch (error) {\n failureReason = error;\n\n routeTransitionError(deps, error, nav.toState, nav.fromState);\n\n throw error;\n // NB: the `} finally {}` BlockStatement mutant SURVIVES but is EQUIVALENT —\n // emptying the finally only skips #cleanupController, which is unobservable\n // (defense-in-depth: on a CANCEL the FSM CANCEL action already aborted+nulled\n // the controller via abortCurrentController, RFC §5; the success-path\n // ref-release is proven unobservable — see #cleanupController's disable). It\n // cannot be inline-`Stryker disable`d: the catch `}` and finally `{` share one\n // line, so there is no comment position that targets the finally body. Left documented.\n } finally {\n // Stryker disable next-line ConditionalExpression,BlockStatement: equivalent — listener cleanup is redundant: the per-navigation signal is discarded on completion, so skipping the removeEventListener leaks nothing observable.\n if (onExternalAbort) {\n // Stryker disable next-line StringLiteral: equivalent — cleanup event name is redundant (listener is `{ once: true }` and the signal is discarded), so a wrong name removes nothing observable.\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n\n // Detach the abort-race listener before #cleanupController aborts the\n // controller below, so the cleanup abort cannot re-fire it. `undefined`\n // only when the controller was already aborted at setup (the early-resolve\n // branch above registered no listener).\n if (onInternalAbort) {\n controller.signal.removeEventListener(\"abort\", onInternalAbort);\n }\n\n // Success drops the controller without aborting (the subscribeLeave signal\n // must stay unaborted); cancel/error aborts it with the originating reason\n // so captured signals expose the real cause via `signal.reason` (#943).\n this.#cleanupController(controller, !succeeded, failureReason);\n }\n }\n\n #handleNavigateError(\n error: unknown,\n controller: AbortController | null,\n transitionStarted: boolean,\n toState: State | undefined,\n fromState: State | undefined,\n ): void {\n if (controller) {\n this.#cleanupController(controller, true, error);\n }\n\n if (transitionStarted && toState) {\n routeTransitionError(this.#deps, error, toState, fromState);\n }\n }\n\n #handleNoGuardsLeave(\n toState: State,\n fromState: State | undefined,\n myId: number,\n opts: NavigationOptions,\n transitionPath: TransitionPath,\n canDeactivateFunctions: Map<string, GuardFn>,\n ): Promise<State> | undefined {\n const deps = this.#deps;\n\n deps.sendLeaveApprove(toState, fromState);\n\n if (deps.hasLeaveListeners()) {\n const controller = new AbortController();\n\n // Track as the current navigation BEFORE listeners run so a reentrant\n // navigate() / stop() / dispose() from a sync listener aborts THIS leave\n // signal — parity with the guard path (#722). On success the controller is\n // released without aborting (see #cleanupController).\n this.#currentController = controller;\n\n let leaveResult: Promise<void> | undefined;\n\n try {\n leaveResult = deps.awaitLeaveListeners(\n toState,\n fromState,\n controller.signal,\n );\n } catch (error) {\n // A sync listener threw — the navigation fails; abort the leave signal\n // with the thrown value so a listener that captured the signal sees the\n // real cause via `signal.reason`, not a generic AbortError (#943).\n this.#cleanupController(controller, true, error);\n\n throw error;\n }\n\n if (leaveResult !== undefined) {\n return this.#finishAsyncNavigation(\n leaveResult,\n {\n toState,\n fromState,\n opts,\n toDeactivate: transitionPath.toDeactivate,\n toActivate: transitionPath.toActivate,\n intersection: transitionPath.intersection,\n canDeactivateFunctions,\n },\n controller,\n myId,\n );\n }\n\n // Sync listeners settled. A synchronous reentrant navigate() can no longer\n // supersede here (banned, RFC §4), so the leave always succeeds: release the\n // controller WITHOUT aborting (the subscribeLeave signal must stay live).\n this.#cleanupController(controller, false);\n\n return undefined;\n }\n\n // No leave listeners: nothing synchronous could have superseded this\n // navigation during the LEAVE_APPROVE emit (reentrant navigate is banned).\n return undefined;\n }\n\n /**\n * Release a navigation's AbortController. The same `controller.signal` is\n * handed to `subscribeLeave` listeners, so it must abort **only** when the\n * navigation is cancelled or errors — never on success (#722). On the success\n * path pass `cancelled = false`: the reference is dropped without aborting, so\n * a listener that captured the signal still sees `aborted === false`.\n *\n * On the failure/cancellation path (`cancelled = true`) pass the originating\n * `reason` so `signal.reason` carries router/error context (a `RouterError`,\n * or the value a sync leave listener threw) — consistent with the cancellation\n * abort `RouterError(TRANSITION_CANCELLED)`, not a generic `AbortError` (#943).\n * `abort()` is idempotent: a controller already aborted by a superseding\n * navigation keeps its first (also-meaningful) reason.\n */\n #cleanupController(\n controller: AbortController,\n cancelled: boolean,\n reason?: unknown,\n ): void {\n if (cancelled) {\n controller.abort(reason);\n }\n\n // Stryker disable next-line ConditionalExpression,EqualityOperator,BlockStatement: equivalent — controller identity-guard; cleanup correctness is enforced by #abortPreviousNavigation + the navigationId/isCurrentNav checks. Full suite stays green with `=== → !==` (nulls the wrong controller) and with the body removed (ref never nulled), so no mutant here is observable.\n if (this.#currentController === controller) {\n this.#currentController = null;\n }\n }\n\n #abortPreviousNavigation(externalSignal?: AbortSignal): void {\n if (this.#deps.isTransitioning()) {\n logger.warn(\n \"router.navigate\",\n \"Concurrent navigation detected on shared router instance. \" +\n \"For SSR, use cloneRouter() to create isolated instance per request.\",\n );\n // The FSM CANCEL action aborts the previous controller — no\n // direct controller.abort here (RFC navigation-cancellation-unification §5).\n this.#deps.cancelNavigation();\n }\n\n if (externalSignal?.aborted) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED, {\n reason: externalSignal.reason,\n });\n }\n }\n}\n","// packages/core/src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts\n\nimport { errorCodes } from \"../../constants\";\nimport { RouterError } from \"../../RouterError\";\n\nimport type { RouterLifecycleDependencies } from \"./types\";\nimport type { NavigationOptions, State } from \"@real-router/types\";\n\nconst REPLACE_OPTS: NavigationOptions = Object.freeze({ replace: true });\n\n/**\n * Independent namespace for managing router lifecycle.\n *\n * Handles start() and stop(). Lifecycle state (isActive, isStarted) is managed\n * by RouterFSM in the facade (Router.ts).\n */\nexport class RouterLifecycleNamespace {\n #deps!: RouterLifecycleDependencies;\n\n // =========================================================================\n // Dependency injection\n // =========================================================================\n\n /**\n * Sets dependencies for lifecycle operations.\n * Must be called before using lifecycle methods.\n */\n setDependencies(deps: RouterLifecycleDependencies): void {\n this.#deps = deps;\n }\n\n // =========================================================================\n // Instance methods\n // =========================================================================\n\n /**\n * Starts the router with the given path.\n *\n * Guards (concurrent start, already started) are handled by the facade via\n * RouterFSM state checks before this method is called.\n */\n async start(startPath: string): Promise<State> {\n const deps = this.#deps;\n\n // #1185: this method is the start-interceptor target — it runs AFTER the\n // whole interceptor chain. A stop() during that window sent STOP\n // (STARTING → IDLE via the FSM table), so if the router is back at IDLE the\n // start was cancelled mid-window; reject instead of committing a state on a\n // stopped router (mirrors the guard phase, which cancels from\n // TRANSITION_STARTED). `isIdle()` is deliberate — a dispose() mid-window\n // leaves the FSM DISPOSED, which the navigateToState / navigateToNotFound\n // liveness gate rejects as ROUTER_DISPOSED (#1186), not conflated with a\n // cancel.\n if (deps.isIdle()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\n }\n\n const options = deps.getOptions();\n\n // Invariant guard (#939): core is platform-agnostic, so the caller must\n // provide a string path. Without a browser-plugin start interceptor to\n // inject a location, a non-string `startPath` (e.g. `start(undefined)`)\n // would otherwise reach matchPath() and throw a cryptic, code-less\n // `TypeError: …codePointAt` deep inside path-matcher. This guard runs AFTER\n // the interceptor chain (browser-plugin substitutes the location upstream),\n // so it only fires when nothing supplied a path — turning the cryptic crash\n // into an actionable error. Symmetric with the subscribe / navigateToNotFound\n // type guards; the validator deliberately permits `undefined` at the facade\n // for exactly the browser-plugin-override case.\n if (typeof startPath !== \"string\") {\n throw new TypeError(\n `[router.start] path must be a string, got ${typeof startPath}`,\n );\n }\n\n const matchedState = deps.matchPath(startPath);\n\n if (!matchedState && !options.allowNotFound) {\n const err = new RouterError(errorCodes.ROUTE_NOT_FOUND, {\n path: startPath,\n });\n\n deps.emitTransitionError(undefined, undefined, err);\n\n throw err;\n }\n\n deps.completeStart();\n\n if (matchedState) {\n // navigateToState commits matchedState verbatim — same primitive URL\n // plugins use on popstate / navigate-event (#525). Keeps trailing-slash\n // and any other source-URL flavor that matchPath produced; skips the\n // redundant forwardState+buildPath round-trip in buildNavigateState.\n return deps.navigateToState(matchedState, REPLACE_OPTS);\n }\n\n return deps.navigateToNotFound(startPath);\n }\n\n /**\n * Stops the router and resets state.\n *\n * Called for READY / TRANSITION_STARTED and, since #1185, STARTING (a stop()\n * that cancels a parked start — state is still `undefined` there, so\n * `clearState()` is a no-op that keeps the reset symmetric). The facade\n * handles IDLE / DISPOSED.\n */\n stop(): void {\n this.#deps.clearState();\n }\n}\n","// packages/core/src/namespaces/EventBusNamespace/EventBusNamespace.ts\n\nimport { errorCodes, events } from \"../../constants\";\nimport { routerEvents, routerStates } from \"../../fsm\";\nimport { RouterError } from \"../../RouterError\";\n\nimport type { EventBusOptions } from \"./types\";\nimport type { RouterEvent, RouterPayloads, RouterState } from \"../../fsm\";\nimport type { EventMethodMap, RouterEventMap } from \"../../types\";\nimport type { RouterValidator } from \"../../types/RouterValidator\";\nimport type { FSM } from \"@real-router/fsm\";\nimport type {\n EventName,\n LeaveFn,\n LeaveState,\n NavigationOptions,\n Plugin,\n State,\n SubscribeFn,\n TreeChangedEvent,\n Unsubscribe,\n} from \"@real-router/types\";\nimport type { EventEmitter } from \"event-emitter\";\n\n/**\n * Internal-only event key for route-tree mutations. Lives on the same\n * `EventEmitter` as the 7 transition events but never enters the public\n * `EventName` union — reachable only through\n * `getRoutesApi(router).subscribeChanges()`.\n */\nconst TREE_CHANGED = \"TREE_CHANGED\";\n\nfunction ensureError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value));\n}\n\nfunction settleLeavePromises(\n promises: Promise<void>[],\n firstSyncError: unknown,\n signal: AbortSignal,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n // #1197 — canonicalize the abort outcome: reject with a\n // `RouterError(TRANSITION_CANCELLED)` carrying the external reason, so the\n // no-guards leave path classifies identically to the guard path (no raw\n // reject, no spurious TRANSITION_ERROR). Internal sources\n // (supersede/stop/dispose) already abort with such a RouterError — thread it\n // through unchanged so the #943 reason is preserved.\n const onAbort = (): void => {\n const reason: unknown = signal.reason;\n\n reject(\n reason instanceof RouterError &&\n reason.code === errorCodes.TRANSITION_CANCELLED\n ? reason\n : new RouterError(errorCodes.TRANSITION_CANCELLED, { reason }),\n );\n };\n\n if (signal.aborted) {\n onAbort();\n\n return;\n }\n\n // Stryker disable next-line ObjectLiteral,BooleanLiteral: equivalent — `{ once: true }` is redundant: onAbort fires at most once (a signal aborts once) and the success path explicitly removeEventListener's it, so dropping `once` is unobservable. StringLiteral sibling stays live (the \"abort\" event name is killed).\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n void Promise.allSettled(promises).then((results) => {\n // Stryker disable next-line StringLiteral: equivalent — this cleanup name is redundant: onAbort is registered with `{ once: true }` and the per-navigation signal is discarded unaborted on success, so failing to remove the listener leaks nothing observable.\n signal.removeEventListener(\"abort\", onAbort);\n\n // Stryker disable next-line BlockStatement: equivalent — emptying the post-allSettled abort-race early-return falls through to resolve()/reject(), but the abort handler already settled the promise, so the extra settle is a no-op. CE sibling stays live (→true hangs the pipeline = killed via timeout).\n if (signal.aborted) {\n // Race lost to abort — the abort handler already rejected; do nothing\n return;\n }\n\n if (firstSyncError !== undefined) {\n reject(ensureError(firstSyncError));\n\n return;\n }\n\n const rejected = results.find(\n (result): result is PromiseRejectedResult =>\n result.status === \"rejected\",\n );\n\n if (rejected !== undefined) {\n reject(ensureError(rejected.reason));\n\n return;\n }\n\n resolve();\n });\n });\n}\n\nexport class EventBusNamespace {\n readonly #fsm: FSM<RouterState, RouterEvent, null, RouterPayloads>;\n readonly #emitter: EventEmitter<RouterEventMap>;\n readonly #onListenerError: (eventName: string, error: unknown) => void;\n // Effect of the FSM CANCEL action: aborts the in-flight navigation's\n // controller. Wired to NavigationNamespace.\n readonly #abortController: (reason?: unknown) => void;\n // Lazy accessor for the opt-in RouterValidator (wired by wireNamespaces).\n // Returns `null` until validation-plugin is registered — so the proactive\n // listener-count threshold (#1188) costs the no-plugin path nothing.\n #getValidator: (() => RouterValidator | null) | undefined;\n readonly #leaveListeners: LeaveFn[] = [];\n\n // Depth of the synchronous transition-dispatch window — elevated while a\n // transition event is being emitted (`emitTransition*`) or a `subscribeLeave`\n // listener batch runs. `isProcessing()` reads it so the navigation facade can\n // reject a synchronous reentrant navigate() from inside a transition listener\n // with REENTRANT_NAVIGATION (RFC navigation-cancellation-unification §4). A\n // counter (not a boolean) tolerates legitimately-nested transition emits. The\n // ceiling-bounded \"allow reentrant navigate, throw RecursionDepthError at\n // `maxEventDepth`\" behaviour (#935/#945) is gone: such a navigate now throws\n // REENTRANT_NAVIGATION at depth 1, before it can recurse. (The emitter's old\n // `maxEventDepth` depth-bound is gone too — re-entrant emits are coalesced to a\n // no-op at the emitter, #1033 — so no event can re-enter its own dispatch.)\n #dispatchDepth = 0;\n\n #currentToState: State | undefined;\n #pendingToState: State | undefined;\n #pendingFromState: State | undefined;\n #pendingError: unknown;\n // Abort reason for the pending CANCEL — read by handleCancel, set by sendCancel.\n #pendingCancelReason: unknown;\n\n constructor(options: EventBusOptions) {\n this.#fsm = options.routerFSM;\n this.#emitter = options.emitter;\n this.#onListenerError = options.onListenerError;\n this.#abortController = options.abortController;\n this.#currentToState = undefined;\n this.#setupFSMActions();\n }\n\n static validateSubscribeListener(listener: unknown): void {\n if (typeof listener !== \"function\") {\n throw new TypeError(\n \"[router.subscribe] Expected a function. \" +\n \"For Observable pattern use observable(router) from @real-router/rx\",\n );\n }\n }\n\n /**\n * Validates the `subscribeLeave` listener. Unlike\n * {@link validateSubscribeListener}, the error carries **no**\n * `@real-router/rx` hint — rx exposes the Observable pattern for *success*\n * transitions (`observable(router)`, `state$`, `events$`), not for leave\n * events, so steering leave-listener misuse toward rx would mislead. The\n * asymmetry is intentional (mirrored in `core/CLAUDE.md`).\n */\n static validateSubscribeLeaveListener(listener: unknown): void {\n if (typeof listener !== \"function\") {\n throw new TypeError(\"[router.subscribeLeave] Expected a function\");\n }\n }\n\n emitRouterStart(): void {\n this.#emitter.emit(events.ROUTER_START);\n }\n\n emitRouterStop(): void {\n this.#emitter.emit(events.ROUTER_STOP);\n }\n\n emitTransitionStart(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_START, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionSuccess(\n toState: State,\n fromState?: State,\n opts?: NavigationOptions,\n ): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_SUCCESS, toState, fromState, opts);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionError(\n toState?: State,\n fromState?: State,\n error?: RouterError,\n ): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_ERROR, toState, fromState, error);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionCancel(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_CANCEL, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionLeaveApprove(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_LEAVE_APPROVE, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n /**\n * True while a transition event is being dispatched synchronously — an\n * `emitTransition*` call or a `subscribeLeave` listener batch is on the stack.\n * The navigation facade reads this to reject a synchronous reentrant\n * navigate() from inside a transition listener (RFC §4).\n */\n isProcessing(): boolean {\n return this.#dispatchDepth > 0;\n }\n\n /**\n * Emits the internal `TREE_CHANGED` event after a structural route-tree\n * mutation. Reuses the shared `EventEmitter` — so re-entrancy coalescing\n * (#1033) and per-listener error isolation (`onListenerError`) apply\n * automatically.\n */\n emitTreeChanged(event: TreeChangedEvent): void {\n this.#emitter.emit(TREE_CHANGED, event);\n }\n\n /**\n * True while a `TREE_CHANGED` event is being dispatched synchronously.\n * Delegates to the emitter's own in-flight tracking (#1034) — `getRoutesApi`\n * reads this to reject reentrant route-CRUD from a `subscribeChanges` handler\n * (#1032).\n */\n isEmittingTreeChanged(): boolean {\n return this.#emitter.isDispatching(TREE_CHANGED);\n }\n\n /**\n * Subscribes to `TREE_CHANGED`. **Lenient** duplicate semantics (mirrors\n * {@link subscribe}): each call wraps the handler in a fresh closure, so N\n * registrations of the same reference produce N independent subscriptions.\n */\n subscribeTreeChanged(\n handler: (event: TreeChangedEvent) => void,\n ): Unsubscribe {\n // Same disposed-state enforcement as subscribe()/subscribeLeave() (#946),\n // completing the guard across all three subscription primitives — extended\n // here to the internal route-tree channel (#982). A `subscribeChanges`\n // reference bound before dispose() (`const s = routes.subscribeChanges\n // .bind(routes)`) reaches this method via the getRoutesApi delegate, which\n // — unlike its add/remove/update siblings — does not itself check\n // isDisposed(). Without this guard, `emitter.on` would re-register a\n // TREE_CHANGED listener that can never fire (clearAll already ran, the FSM\n // is DISPOSED, the route tree is torn down, no future emit) — a silent\n // no-op, the internal-channel counterpart of the #946 hazard.\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n return this.#emitter.on(TREE_CHANGED, (event: TreeChangedEvent) => {\n handler(event);\n });\n }\n\n /** Number of active `TREE_CHANGED` listeners (drives conditional emit). */\n treeChangedListenerCount(): number {\n return this.#emitter.listenerCount(TREE_CHANGED);\n }\n\n sendStart(): void {\n this.#fsm.send(routerEvents.START);\n }\n\n sendStop(): void {\n this.#fsm.send(routerEvents.STOP);\n }\n\n sendDispose(): void {\n this.#fsm.send(routerEvents.DISPOSE);\n }\n\n sendStarted(): void {\n this.#fsm.send(routerEvents.STARTED);\n }\n\n sendNavigate(toState: State, fromState?: State): void {\n this.#currentToState = toState;\n // Table-driven: the FSM action emits TRANSITION_START (#1169 D-full). A\n // NAVIGATE that the table rejects is a no-op — the FSM never leaves an\n // invalid state and no event fires.\n this.#fsm.send(routerEvents.NAVIGATE, { toState, fromState });\n }\n\n sendComplete(\n state: State,\n fromState?: State,\n opts: NavigationOptions = {},\n ): void {\n // Table-driven: the FSM action emits TRANSITION_SUCCESS (#1169 D-full).\n // COMPLETE from IDLE/DISPOSED (a listener stopped/disposed mid-transition)\n // is a table no-op — no resurrection, no phantom success emit.\n this.#fsm.send(routerEvents.COMPLETE, { toState: state, fromState, opts });\n\n // Nav committed — clear so a later stop()/dispose() cannot cancel a finished\n // navigation. Unconditional now that synchronous reentrant navigate is banned\n // (RFC §4): nothing can replace #currentToState during the emit above, so the\n // #308 reentrant-preserve guard is no longer needed.\n this.#currentToState = undefined;\n }\n\n sendLeaveApprove(toState: State, fromState?: State): void {\n // Table-driven: the FSM action emits TRANSITION_LEAVE_APPROVE (#1169 D-full).\n // LEAVE_APPROVE from IDLE/DISPOSED is a table no-op — no resurrection.\n this.#fsm.send(routerEvents.LEAVE_APPROVE, { toState, fromState });\n }\n\n sendFail(toState?: State, fromState?: State, error?: unknown): void {\n this.#pendingToState = toState;\n this.#pendingFromState = fromState;\n this.#pendingError = error;\n this.#fsm.send(routerEvents.FAIL);\n\n // Nav failed — clear (unconditional; synchronous reentrant navigate is\n // banned (RFC §4), so nothing replaces #currentToState during the emit).\n this.#currentToState = undefined;\n }\n\n /**\n * Surfaces a `TRANSITION_ERROR` for callers that do **not** know — or do not\n * control — the current FSM state: the plugin-facing `emitTransitionError`\n * primitive (`getPluginApi`), the dispose chain, and validator / same-state\n * rejections. It is the state-agnostic counterpart to {@link sendFail}.\n *\n * **What \"Safe\" means here.** The error event is never *dropped*, whatever the\n * FSM state — it does **not** mean the method catches every error. Errors\n * thrown *inside* a `TRANSITION_ERROR` listener are isolated by the\n * `EventEmitter`'s per-listener `onListenerError` sink, not by this method.\n *\n * **Why it branches on its own FSM state.** When the FSM is settled in `READY`\n * (no transition in flight) it routes through the FSM `FAIL` action via\n * {@link sendFail}, so the error rides the normal FSM-driven emit. Otherwise —\n * the router may be starting, mid-transition, or torn down — it emits\n * `TRANSITION_ERROR` directly: a fire-and-forget error report from an unknown\n * state must not drive a second FSM transition that could collide with an\n * in-flight one. Both branches guarantee the event reaches subscribers.\n */\n sendFailSafe(toState?: State, fromState?: State, error?: unknown): void {\n if (this.isReady()) {\n this.sendFail(toState, fromState, error);\n } else {\n this.emitTransitionError(toState, fromState, error as RouterError);\n }\n }\n\n sendCancel(toState: State, fromState?: State, reason?: unknown): void {\n this.#pendingToState = toState;\n this.#pendingFromState = fromState;\n this.#pendingCancelReason = reason;\n this.#fsm.send(routerEvents.CANCEL);\n\n // Nav cancelled — clear (unconditional; synchronous reentrant navigate is\n // banned (RFC §4), so nothing replaces #currentToState during the emit).\n this.#currentToState = undefined;\n }\n\n canBeginTransition(): boolean {\n return this.#fsm.canSend(routerEvents.NAVIGATE);\n }\n\n canStart(): boolean {\n return this.#fsm.canSend(routerEvents.START);\n }\n\n canCancel(): boolean {\n return this.#fsm.canSend(routerEvents.CANCEL);\n }\n\n isActive(): boolean {\n const fsmState = this.#fsm.getState();\n\n return fsmState !== routerStates.IDLE && fsmState !== routerStates.DISPOSED;\n }\n\n isDisposed(): boolean {\n return this.#fsm.getState() === routerStates.DISPOSED;\n }\n\n isTransitioning(): boolean {\n const state = this.#fsm.getState();\n\n return (\n state === routerStates.TRANSITION_STARTED ||\n state === routerStates.LEAVE_APPROVED\n );\n }\n\n isLeaveApproved(): boolean {\n return this.#fsm.getState() === routerStates.LEAVE_APPROVED;\n }\n\n isReady(): boolean {\n return this.#fsm.getState() === routerStates.READY;\n }\n\n isStarting(): boolean {\n return this.#fsm.getState() === routerStates.STARTING;\n }\n\n isIdle(): boolean {\n return this.#fsm.getState() === routerStates.IDLE;\n }\n\n /**\n * Plugin-author API for subscribing to internal router events.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — strict (throws).** Passing the same\n * callback reference twice for the same event throws\n * `Error(\"Duplicate listener for ...\")` from the underlying `EventEmitter`.\n * This is loud-on-misuse by design: plugin code is expected to register\n * each callback once. The contract differs from {@link subscribe} /\n * {@link subscribeLeave}, which are end-user surfaces and silently accept\n * duplicates.\n */\n addEventListener<E extends EventName>(\n eventName: E,\n cb: Plugin[EventMethodMap[E]],\n ): Unsubscribe {\n this.#checkListenerThreshold(eventName, \"addEventListener\");\n\n return this.#emitter.on(\n eventName,\n cb as (...args: RouterEventMap[typeof eventName]) => void,\n );\n }\n\n /**\n * End-user / UI-binding API for subscribing to successful transitions.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — independent.** Each call wraps\n * `listener` in a fresh closure and registers it as a distinct internal\n * slot. `router.subscribe(fn)` twice produces **two** active subscriptions;\n * `fn` fires twice per `TRANSITION_SUCCESS`. The returned `Unsubscribe` is\n * paired with its specific call — invoking it removes exactly that\n * registration.\n *\n * This contract differs from {@link addEventListener} (plugin API, throws\n * on duplicate). End-user code that wants idempotent registration must\n * gate itself, e.g. `if (!unsub) unsub = router.subscribe(fn);`.\n */\n subscribe(listener: SubscribeFn): Unsubscribe {\n // Enforce the disposed state HERE, not only on the facade. A reference\n // bound before dispose() (`const s = router.subscribe.bind(router)`)\n // bypasses the facade's #markDisposed swap and reaches this method\n // directly. Without this guard, `emitter.on` would silently re-register a\n // listener that can never fire (clearAll already ran, FSM is DISPOSED, no\n // future emit) — a silent no-op / stuck-UI hazard (#946).\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n this.#checkListenerThreshold(events.TRANSITION_SUCCESS, \"subscribe\");\n\n return this.#emitter.on(\n events.TRANSITION_SUCCESS,\n (toState: State, fromState?: State) => {\n // `subscribe` is fire-and-forget — the listener's return value is\n // intentionally ignored. But the EventEmitter's per-listener try/catch\n // isolates only SYNC throws: an async listener returns a Promise whose\n // rejection would otherwise surface as a Node `unhandledRejection`\n // (fatal under `--unhandled-rejections=strict`, the Node 22+ default).\n // `SubscribeFn` is publicly typed `=> void` (fire-and-forget), but an\n // async listener returns a Promise at runtime that must be isolated\n // (#944). A cast to a `=> unknown` view is auto-stripped by\n // `no-unnecessary-type-assertion` (`=> void` is assignable to\n // `=> unknown`), so read the value as `unknown` and disable the\n // void-expression rule for this single deliberate divergence.\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -- read the runtime Promise of a void-typed async listener (#944)\n const result: unknown = listener({\n route: toState,\n previousRoute: fromState,\n });\n\n if (\n result !== null &&\n result !== undefined &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n ) {\n // Route the rejection to the same `onListenerError` sink a sync throw\n // flows through (#944) — symmetric with `subscribeLeave`, which\n // isolates rejections via `Promise.allSettled`. `.catch(handler)`\n // terminates the chain (not a floating promise — mirrors\n // `Router.#suppressUnhandledRejection`).\n Promise.resolve(result as PromiseLike<unknown>).catch(\n (error: unknown) => {\n this.#onListenerError(events.TRANSITION_SUCCESS, error);\n },\n );\n }\n },\n );\n }\n\n /**\n * End-user / UI-binding API for subscribing to **approved** route departures\n * (`LEAVE_APPROVED` phase): all `canDeactivate` guards have passed, but the\n * departure is **tentative, not committed** — an activation (`canActivate`)\n * guard can still reject (or the target route be removed mid-transition),\n * leaving the user on the current route (#932). Treat the leave as tentative\n * for non-idempotent side-effects and use the payload `signal` (which aborts\n * with the failure reason, #943) to roll back when the navigation does not\n * commit. Async listeners block the activation phase.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — independent.** Each call pushes\n * `listener` onto the internal array; `router.subscribeLeave(fn)` twice\n * produces two entries and `fn` fires twice per leave. Each returned\n * `Unsubscribe` is **idempotent** (a `removed` flag, #1349) and removes\n * exactly ONE entry — the first still matching the reference (`indexOf`\n * semantic). So a repeated call of one unsubscribe is a true no-op and does\n * **not** touch the other registration; N subscribes + M *distinct*\n * unsubscribes leave N − M entries. Which physical entry survives is\n * irrelevant — the reference is the same.\n *\n * Contract differs from {@link addEventListener} (throws on duplicate).\n * For idempotent *registration* (one active subscription), gate at the call site.\n */\n subscribeLeave(listener: LeaveFn): Unsubscribe {\n // Same disposed-state enforcement as subscribe() (#946): a pre-bound\n // reference would otherwise push onto #leaveListeners after dispose() and\n // silently never fire (FSM is DISPOSED, no LEAVE_APPROVE emit).\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n this.#leaveListeners.push(listener);\n\n // Idempotency flag (#1349), mirroring extendRouter / addInterceptor (#1198).\n // Without it, a double call would `indexOf(listener)` again and splice a\n // DUPLICATE registration of the same fn — silently deactivating another\n // subscriber whose own unsubscribe was never called. The `Unsubscribe`\n // contract names subscribeLeave as idempotent. (Unlike addInterceptor, the\n // `idx !== -1` guard stays: `dispose()` empties `#leaveListeners` via\n // `clearAll`, so an unsubscribe called after dispose reaches this with\n // idx === -1.)\n let removed = false;\n\n return () => {\n if (removed) {\n return;\n }\n\n removed = true;\n\n const idx = this.#leaveListeners.indexOf(listener);\n\n if (idx !== -1) {\n this.#leaveListeners.splice(idx, 1);\n }\n };\n }\n\n hasLeaveListeners(): boolean {\n return this.#leaveListeners.length > 0;\n }\n\n /**\n * True when a plugin listener runs in a PRE-COMMIT transition window where a\n * synchronous `stop()`/`dispose()` can supersede the in-flight navigation\n * before it commits — i.e. `onTransitionStart` (TRANSITION_START) or\n * `onTransitionLeaveApprove` (TRANSITION_LEAVE_APPROVE). (`subscribeLeave` is\n * tracked separately via `hasLeaveListeners`; `onTransitionSuccess` fires\n * post-commit and cannot cancel.) Read into the pre-`startTransition` liveness\n * snapshot so the commit-gate (#1169) fires for these windows too.\n */\n hasPreCommitListeners(): boolean {\n return (\n this.#emitter.listenerCount(events.TRANSITION_START) > 0 ||\n this.#emitter.listenerCount(events.TRANSITION_LEAVE_APPROVE) > 0\n );\n }\n\n awaitLeaveListeners(\n toState: State,\n fromState: State | undefined,\n signal: AbortSignal,\n ): Promise<void> | undefined {\n if (fromState === undefined) {\n return undefined;\n }\n\n // Freeze the payload wrapper so listeners cannot mutate it (`payload.route`\n // is already deep-frozen via the State immutability invariant; this closes\n // the wrapper-mutation gap surfaced by audit `probe-05-payload-frozen`).\n const leaveState: LeaveState = Object.freeze({\n route: fromState,\n nextRoute: toState,\n signal,\n });\n\n let promises: Promise<void>[] | undefined;\n let firstSyncError: unknown;\n\n // Snapshot before iteration — a listener that reentrantly calls\n // `subscribeLeave(newFn)` or its own `unsubscribe()` must not affect the\n // current emit cycle. Symmetric with the EventEmitter snapshot invariant\n // (PR #666 / #659).\n const snapshot = [...this.#leaveListeners];\n\n // Elevated across the SYNC leave-listener dispatch: `isProcessing()` reads\n // it, so a sync subscribeLeave listener that calls navigate() is rejected\n // with REENTRANT_NAVIGATION at the facade (RFC §4). The `finally` restores it\n // before any async tail (returned promise) runs, so a DEFERRED navigate from\n // an async listener (after its first `await`) sees depth 0 and is allowed.\n this.#dispatchDepth++;\n\n try {\n for (const listener of snapshot) {\n try {\n const result = listener(leaveState);\n\n if (result !== undefined && typeof result.then === \"function\") {\n promises ??= [];\n promises.push(result);\n }\n } catch (error: unknown) {\n if (firstSyncError === undefined) {\n firstSyncError = error;\n }\n }\n }\n } finally {\n this.#dispatchDepth--;\n }\n\n if (promises === undefined) {\n if (firstSyncError !== undefined) {\n throw ensureError(firstSyncError);\n }\n\n return undefined;\n }\n\n return settleLeavePromises(promises, firstSyncError, signal);\n }\n\n clearAll(): void {\n this.#emitter.clearAll();\n this.#leaveListeners.length = 0;\n }\n\n setLimits(limits: { maxListeners: number; warnListeners: number }): void {\n this.#emitter.setLimits(limits);\n }\n\n /**\n * Injects the lazy validator accessor (wireNamespaces), mirroring\n * `PluginsNamespace` / `RouteLifecycleNamespace`. The closure reads the live\n * `RouterInternals.validator`, so a validation-plugin registered AFTER wiring\n * is still observed on the next `subscribe` / `addEventListener`.\n */\n setValidatorAccessor(getValidator: () => RouterValidator | null): void {\n this.#getValidator = getValidator;\n }\n\n // Single guarded entry point for routing a cancel into the FSM `CANCEL` action\n // — used by every source: stop/dispose (RouterLifecycle) pass no reason;\n // supersede / external `opts.signal` (via the wiring `cancelNavigation` dep)\n // pass the abort reason (#943). `canCancel()` makes it a no-op outside a\n // cancellable FSM state (#1034: was a second, unguarded `cancelNavigation` path).\n sendCancelIfPossible(fromState: State | undefined, reason?: unknown): void {\n const toState = this.#currentToState;\n\n if (!this.canCancel() || toState === undefined) {\n return;\n }\n\n this.sendCancel(toState, fromState, reason);\n }\n\n /**\n * Proactive listener-count threshold (#1188) — mirrors the plugins /\n * lifecycle / dependencies counters. Opt-in: the emitter's per-event count is\n * read ONLY when the validator is installed, so the bare-core hot path pays\n * nothing. `count` is the POST-add size (`listenerCount + 1`), matching\n * `RouteLifecycleNamespace`'s `count + 1`, so warn/error fire exactly when the\n * new listener reaches the threshold. Core keeps the emitter's bare-`Error`\n * hard cap; this only surfaces an actionable signal well before it.\n */\n #checkListenerThreshold(eventName: EventName, methodName: string): void {\n const validator = this.#getValidator?.();\n\n if (validator) {\n validator.eventBus.validateCountThresholds(\n this.#emitter.listenerCount(eventName) + 1,\n eventName,\n methodName,\n );\n }\n }\n\n #emitPendingError(): void {\n this.emitTransitionError(\n this.#pendingToState,\n this.#pendingFromState,\n this.#pendingError as RouterError | undefined,\n );\n\n // Clear the pending payload once this FAIL action has consumed it. `#pending*`\n // is only meaningful in the window between the sendFail()/sendFailSafe() that\n // sets it and this emit; keeping it afterwards pins a stale State/RouterError\n // on the instance and leaves an implicit \"valid only in this window\" coupling\n // (#949). Hygiene only — every consumer overwrites the fields before\n // re-reading (handleCancel reads what its own sendCancel just set), so there\n // is no observable behaviour change.\n this.#pendingToState = undefined;\n this.#pendingFromState = undefined;\n this.#pendingError = undefined;\n }\n\n #setupFSMActions(): void {\n const fsm = this.#fsm;\n\n fsm.on(routerStates.STARTING, routerEvents.STARTED, () => {\n this.emitRouterStart();\n });\n\n fsm.on(routerStates.READY, routerEvents.STOP, () => {\n this.emitRouterStop();\n });\n\n // NAVIGATE / LEAVE_APPROVE / COMPLETE emit their transition event as the FSM\n // action (payload = the transition states), so `send()` from an invalid\n // state (e.g. COMPLETE from IDLE after a listener's stop()/dispose()) is a\n // table no-op that emits nothing — the FSM table is the sole authority over\n // state, no `forceState` resurrection (#1169 D-full). NAVIGATE fires from\n // READY plus the TRANSITION_STARTED / LEAVE_APPROVED self-loops (supersede).\n const emitNavigate = (payload: RouterPayloads[\"NAVIGATE\"]): void => {\n this.emitTransitionStart(payload.toState, payload.fromState);\n };\n\n fsm.on(routerStates.READY, routerEvents.NAVIGATE, emitNavigate);\n fsm.on(\n routerStates.TRANSITION_STARTED,\n routerEvents.NAVIGATE,\n emitNavigate,\n );\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.NAVIGATE, emitNavigate);\n\n fsm.on(\n routerStates.TRANSITION_STARTED,\n routerEvents.LEAVE_APPROVE,\n (payload) => {\n this.emitTransitionLeaveApprove(payload.toState, payload.fromState);\n },\n );\n\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.COMPLETE, (payload) => {\n this.emitTransitionSuccess(\n payload.toState,\n payload.fromState,\n payload.opts,\n );\n });\n\n const handleCancel = () => {\n const toState = this.#pendingToState;\n const reason = this.#pendingCancelReason;\n\n this.#pendingCancelReason = undefined;\n\n // (RFC navigation-cancellation-unification §5): the FSM CANCEL\n // action OWNS the abort. Aborting the in-flight controller wakes the parked\n // async pipeline (#1018) and rejects navigate() — the post-race local\n // isActive() sees signal.aborted; `reason` surfaces as the leave signal's\n // reason (#943). Order: (i) abort (wake) then (ii) emit TRANSITION_CANCEL.\n // No cycle: onInternalAbort is wake-only, it does not re-enter cancel.\n this.#abortController(reason);\n\n /* v8 ignore next -- @preserve: #pendingToState guaranteed set by sendCancel before send() */\n if (toState === undefined) {\n return;\n }\n\n this.emitTransitionCancel(toState, this.#pendingFromState);\n };\n\n fsm.on(routerStates.TRANSITION_STARTED, routerEvents.CANCEL, handleCancel);\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.CANCEL, handleCancel);\n\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.STARTING, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.READY, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.TRANSITION_STARTED, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n }\n}\n","// packages/core/src/namespaces/RouterLifecycleNamespace/constants.ts\n\nimport { errorCodes } from \"../../constants\";\nimport { RouterError } from \"../../RouterError\";\n\n// =============================================================================\n// Cached Errors (Performance Optimization)\n// =============================================================================\n// Pre-create error instances to avoid object allocation on hot paths.\n// Error creation involves: new object, stack trace capture (~500ns-2μs).\n// Cached errors skip this overhead entirely.\n//\n// Trade-off: All error instances share the same stack trace (points here).\n// This is acceptable because:\n// 1. These errors indicate user misconfiguration, not internal bugs\n// 2. Error code and message are sufficient for debugging\n// 3. Performance gain (~80% for error paths) outweighs stack trace loss\n// =============================================================================\n\n/**\n * Cached error for start() called when router is already started/starting.\n */\nexport const CACHED_ALREADY_STARTED_ERROR = new RouterError(\n errorCodes.ROUTER_ALREADY_STARTED,\n);\n","// packages/core/src/typeGuards.ts\n\n/**\n * RealRouter-specific assertion for logger configuration.\n */\nimport type { LoggerConfig, LogLevelConfig } from \"@real-router/logger\";\n\nconst VALID_LEVELS_SET = new Set<string>([\n \"all\",\n \"warn-error\",\n \"error-only\",\n \"none\",\n]);\n\nfunction isValidLevel(value: unknown): value is LogLevelConfig {\n return typeof value === \"string\" && VALID_LEVELS_SET.has(value);\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return `\"${value}\"`;\n }\n if (typeof value === \"object\") {\n return JSON.stringify(value);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return String(value);\n}\n\nexport function assertLoggerConfig(\n config: unknown,\n): asserts config is LoggerConfig {\n if (typeof config !== \"object\") {\n throw new TypeError(\"Logger config must be an object\");\n }\n\n // `typeof null === \"object\"`, so TS still sees `object | null` here — but the\n // sole caller (Router's ctor) gates on `if (loggerConfig)`, so null/falsy never\n // arrives; treat it as the non-null object the gate guarantees.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- gated caller never passes null\n const obj = config!;\n\n // Check for unknown properties\n for (const key of Object.keys(obj)) {\n if (\n key !== \"level\" &&\n key !== \"callback\" &&\n key !== \"callbackIgnoresLevel\"\n ) {\n throw new TypeError(`Unknown logger config property: \"${key}\"`);\n }\n }\n\n // Validate level if present\n if (\"level\" in obj && obj.level !== undefined && !isValidLevel(obj.level)) {\n throw new TypeError(\n `Invalid logger level: ${formatValue(obj.level)}. Expected: \"all\" | \"warn-error\" | \"error-only\" | \"none\"`,\n );\n }\n\n // Validate callback if present\n if (\n \"callback\" in obj &&\n obj.callback !== undefined &&\n typeof obj.callback !== \"function\"\n ) {\n throw new TypeError(\n `Logger callback must be a function, got ${typeof obj.callback}`,\n );\n }\n\n // Validate callbackIgnoresLevel if present (logger.configure does not type-check it)\n if (\n \"callbackIgnoresLevel\" in obj &&\n obj.callbackIgnoresLevel !== undefined &&\n typeof obj.callbackIgnoresLevel !== \"boolean\"\n ) {\n throw new TypeError(\n `Logger callbackIgnoresLevel must be a boolean, got ${typeof obj.callbackIgnoresLevel}`,\n );\n }\n}\n","// packages/core/src/wiring/wireNamespaces.ts\n\nimport { normalizeParams } from \"../helpers\";\nimport { getInternals } from \"../internals\";\nimport { resolveOption } from \"../namespaces/OptionsNamespace\";\n\nimport type { NamespaceBag } from \"./types\";\nimport type { NavigationDependencies } from \"../namespaces/NavigationNamespace\";\nimport type { PluginsDependencies } from \"../namespaces/PluginsNamespace\";\nimport type { RouteLifecycleDependencies } from \"../namespaces/RouteLifecycleNamespace\";\nimport type { RouterLifecycleDependencies } from \"../namespaces/RouterLifecycleNamespace\";\nimport type { RoutesDependencies } from \"../namespaces/RoutesNamespace\";\nimport type { Router } from \"../Router\";\nimport type { RouterValidator } from \"../types/RouterValidator\";\nimport type { DefaultDependencies, Params } from \"@real-router/types\";\n\n/**\n * Compiles a guard/plugin factory against the router + a cached `getDependency`\n * accessor. One generic function serves both RouteLifecycle (`GuardFnFactory →\n * GuardFn`) and Plugins (`PluginFactory → Plugin`).\n */\ntype CompileFactory<Dependencies extends DefaultDependencies> = <T>(\n factory: (\n router: Router<Dependencies>,\n getDependency: <K extends keyof Dependencies>(name: K) => Dependencies[K],\n ) => T,\n) => T;\n\n/**\n * Wires the inter-namespace dependencies of a freshly-constructed router.\n *\n * Replaces the former `RouterWiringBuilder` class + `wireRouter` director\n * (#1334): a single call-site with a fixed sequence and nothing to build is a\n * pack of procedures over a shared bag, not a builder — plain functions over a\n * `NamespaceBag` drop the triple-repeated field list and the builder instance.\n *\n * Call order is arbitrary (#1331): no `wire*` function runs user code or\n * eagerly reads another namespace's deps. (`wireLimits` is the one eager\n * *write* — it hands the frozen limits object to dependenciesStore/eventBus;\n * the rest only store deps-closures.) Initial-route guard factories are\n * flushed afterwards, from the constructor's `flushPendingGuards()` call.\n */\nexport function wireNamespaces<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n): void {\n // One shared factory for both guard and plugin compilation (#1334); the\n // `getDependency` closure is allocated once here, not per compile call.\n const compileFactory = createCompileFactory(ns);\n\n // Shared by RouteLifecycle and Plugins — one allocation. Internals are\n // registered before wiring (#1331), so this never throws; returns null until\n // validation-plugin installs the validator.\n const getValidator = (): RouterValidator | null =>\n getInternals(ns.router).validator;\n\n wireLimits(ns);\n wireEventBus(ns, getValidator);\n wireRouteLifecycle(ns, compileFactory, getValidator);\n wireRoutes(ns);\n wirePlugins(ns, compileFactory, getValidator);\n wireNavigation(ns);\n wireRouterLifecycle(ns);\n wireState(ns);\n}\n\nfunction createCompileFactory<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n): CompileFactory<Dependencies> {\n const { router, dependenciesStore } = ns;\n\n const getDependency = <K extends keyof Dependencies>(\n name: K,\n ): Dependencies[K] => dependenciesStore.dependencies[name] as Dependencies[K];\n\n return <T>(\n factory: (\n router: Router<Dependencies>,\n getDependency: <K extends keyof Dependencies>(name: K) => Dependencies[K],\n ) => T,\n ): T => factory(router, getDependency);\n}\n\nfunction wireLimits<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n): void {\n ns.dependenciesStore.limits = ns.limits;\n ns.eventBus.setLimits({\n maxListeners: ns.limits.maxListeners,\n warnListeners: ns.limits.warnListeners,\n });\n}\n\n/**\n * Hands EventBusNamespace the shared lazy validator accessor so `subscribe` /\n * `addEventListener` can run the opt-in listener-count threshold (#1188) — the\n * emitter-side parallel to the plugins / lifecycle / dependencies counters.\n */\nfunction wireEventBus<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n getValidator: () => RouterValidator | null,\n): void {\n ns.eventBus.setValidatorAccessor(getValidator);\n}\n\nfunction wireRouteLifecycle<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n compileFactory: CompileFactory<Dependencies>,\n getValidator: () => RouterValidator | null,\n): void {\n const deps: RouteLifecycleDependencies<Dependencies> = {\n compileFactory,\n getValidator,\n };\n\n ns.routeLifecycle.setDependencies(deps);\n}\n\nfunction wireRoutes<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n): void {\n const deps: RoutesDependencies<Dependencies> = {\n addActivateGuard: (name, handler, precompiledFn) => {\n ns.routeLifecycle.addCanActivate(name, handler, true, precompiledFn);\n },\n addDeactivateGuard: (name, handler, precompiledFn) => {\n ns.routeLifecycle.addCanDeactivate(name, handler, true, precompiledFn);\n },\n compileGuard: (handler, methodName) =>\n ns.routeLifecycle.compileGuardFactory(handler, methodName),\n makeState: (name, params, path, meta) =>\n ns.state.makeState(name, params, path, meta),\n getState: () => ns.state.get(),\n areStatesEqual: (state1, state2, ignoreQueryParams) =>\n ns.state.areStatesEqual(state1, state2, ignoreQueryParams),\n getDependency: (name) =>\n ns.dependenciesStore.dependencies[name] as Dependencies[typeof name],\n forwardState: <P extends Params = Params>(name: string, params: P) => {\n const ctx = getInternals(ns.router);\n\n ctx.validator?.routes.validateStateBuilderArgs(\n name,\n params,\n \"forwardState\",\n );\n\n return ctx.forwardState(name, params);\n },\n };\n\n ns.routes.setDependencies(deps);\n ns.routes.setLifecycleNamespace(ns.routeLifecycle);\n}\n\nfunction wirePlugins<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n compileFactory: CompileFactory<Dependencies>,\n getValidator: () => RouterValidator | null,\n): void {\n const deps: PluginsDependencies<Dependencies> = {\n addEventListener: (eventName, cb) =>\n ns.eventBus.addEventListener(eventName, cb),\n canNavigate: () => ns.eventBus.canBeginTransition(),\n compileFactory,\n getValidator,\n };\n\n ns.plugins.setDependencies(deps);\n}\n\nfunction wireNavigation<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n): void {\n const deps: NavigationDependencies = {\n getOptions: () => ns.options.get(),\n hasRoute: (name) => ns.routes.hasRoute(name),\n getState: () => ns.state.get(),\n setState: (state) => {\n ns.state.set(state);\n },\n buildNavigateState: (routeName, routeParams) => {\n const ctx = getInternals(ns.router);\n\n ctx.validator?.routes.validateStateBuilderArgs(\n routeName,\n routeParams,\n \"navigate\",\n );\n\n const forwarded = ctx.forwardState(routeName, routeParams);\n const name = forwarded.name;\n const params = normalizeParams(forwarded.params);\n const meta = ns.routes.getMetaForState(name);\n\n if (meta === undefined) {\n return;\n }\n\n const path = ctx.buildPath(name, params);\n\n return ns.state.makeState(name, params, path, meta, true);\n },\n resolveDefault: () => {\n const options = ns.options.get();\n const ctx = getInternals(ns.router);\n\n const route = resolveOption(\n options.defaultRoute,\n (name: string) =>\n ns.dependenciesStore.dependencies[name as keyof Dependencies],\n );\n const params = resolveOption(\n options.defaultParams,\n /* v8 ignore next -- @preserve: unreachable unless defaultParams is a callback that calls getDependency */\n (name: string) =>\n ns.dependenciesStore.dependencies[name as keyof Dependencies],\n );\n\n if (typeof options.defaultRoute === \"function\") {\n ctx.validator?.options.validateResolvedDefaultRoute(\n route,\n ctx.routeGetStore(),\n );\n }\n\n return { route, params };\n },\n startTransition: (toState, fromState) => {\n ns.eventBus.sendNavigate(toState, fromState);\n },\n cancelNavigation: (reason) => {\n ns.eventBus.sendCancelIfPossible(ns.state.get(), reason);\n },\n sendTransitionDone: (state, fromState, opts) => {\n ns.eventBus.sendComplete(state, fromState, opts);\n },\n sendTransitionFail: (toState, fromState, error) => {\n ns.eventBus.sendFail(toState, fromState, error);\n },\n emitTransitionError: (toState, fromState, error) => {\n ns.eventBus.sendFailSafe(toState, fromState, error);\n },\n emitTransitionSuccess: (toState, fromState, opts) => {\n ns.eventBus.emitTransitionSuccess(toState, fromState, opts);\n },\n sendLeaveApprove: (toState, fromState) => {\n ns.eventBus.sendLeaveApprove(toState, fromState);\n },\n canNavigate: () => ns.eventBus.canBeginTransition(),\n getLifecycleFunctions: () => ns.routeLifecycle.getFunctions(),\n isActive: () => ns.router.isActive(),\n isTransitioning: () => ns.eventBus.isTransitioning(),\n // Post-leave auto-cleanup unregisters only the EXTERNAL (component-managed)\n // guard; a route-config (definition) guard survives for re-entry (#1171).\n clearCanDeactivate: (name: string) => {\n ns.routeLifecycle.clearCanDeactivate(name, \"external\");\n },\n hasLeaveListeners: () => ns.eventBus.hasLeaveListeners(),\n hasPreCommitListeners: () => ns.eventBus.hasPreCommitListeners(),\n awaitLeaveListeners: (toState, fromState, signal) =>\n ns.eventBus.awaitLeaveListeners(toState, fromState, signal),\n };\n\n ns.navigation.setDependencies(deps);\n}\n\nfunction wireRouterLifecycle<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n): void {\n const deps: RouterLifecycleDependencies = {\n getOptions: () => ns.options.get(),\n navigateToState: (state, opts) =>\n ns.navigation.navigateToState(state, opts),\n navigateToNotFound: (path) => ns.navigation.navigateToNotFound(path),\n clearState: () => {\n ns.state.set(undefined);\n },\n matchPath: (path) => ns.routes.matchPath(path, ns.options.get()),\n completeStart: () => {\n ns.eventBus.sendStarted();\n },\n isIdle: () => ns.eventBus.isIdle(),\n emitTransitionError: (toState, fromState, error) => {\n ns.eventBus.sendFail(toState, fromState, error);\n },\n };\n\n ns.lifecycle.setDependencies(deps);\n}\n\nfunction wireState<Dependencies extends DefaultDependencies>(\n ns: NamespaceBag<Dependencies>,\n): void {\n ns.state.setDependencies({\n getDefaultParams: () => ns.routes.getStore().config.defaultParams,\n buildPath: (name, params) => {\n const ctx = getInternals(ns.router);\n\n return ctx.buildPath(name, params);\n },\n getUrlParams: (name) => ns.routes.getUrlParams(name),\n });\n}\n","// packages/core/src/Router.ts\n\n/**\n * Router class - facade with integrated namespaces.\n *\n * All functionality is now provided by namespace classes.\n */\n\nimport { logger } from \"@real-router/logger\";\nimport { EventEmitter } from \"event-emitter\";\n\nimport { EMPTY_PARAMS, errorCodes } from \"./constants\";\nimport { createRouterFSM } from \"./fsm\";\nimport { guardDependencies, guardRouteStructure } from \"./guards\";\nimport { createLimits, normalizeParams } from \"./helpers\";\nimport {\n createBinaryInterceptable,\n createInterceptable,\n getInternals,\n registerInternals,\n} from \"./internals\";\nimport {\n EventBusNamespace,\n NavigationNamespace,\n OptionsNamespace,\n PluginsNamespace,\n RouteLifecycleNamespace,\n RouterLifecycleNamespace,\n RoutesNamespace,\n StateNamespace,\n createDependenciesStore,\n} from \"./namespaces\";\nimport { CACHED_ALREADY_STARTED_ERROR } from \"./namespaces/RouterLifecycleNamespace/constants\";\nimport { RouterError } from \"./RouterError\";\nimport { getTransitionPath } from \"./transitionPath\";\nimport { assertLoggerConfig } from \"./typeGuards\";\nimport { wireNamespaces } from \"./wiring\";\n\nimport type { RouterInternals } from \"./internals\";\nimport type { DependenciesStore } from \"./namespaces\";\nimport type { Limits, PluginFactory, Route, RouterEventMap } from \"./types\";\nimport type {\n DefaultDependencies,\n LeaveFn,\n NavigationOptions,\n Options,\n Params,\n Router as RouterInterface,\n State,\n SubscribeFn,\n Unsubscribe,\n} from \"@real-router/types\";\nimport type { CreateMatcherOptions } from \"route-tree\";\n\nconst EMPTY_OPTS: Readonly<NavigationOptions> = Object.freeze({});\n\n// Module-level so #isExpectedRejection allocates nothing per navigate()/start() call.\n// These are expected navigation outcomes owned by the caller, not internal\n// bugs — the safety net stays silent for them and lets awaiting callers see\n// the rejection. CANNOT_ACTIVATE / CANNOT_DEACTIVATE belong here: a guard\n// blocking (or a plugin's guard-blocked back()/forward()) is a normal result,\n// so a fire-and-forget call must not emit a spurious \"Unexpected navigation\n// error\" (#721).\nconst SUPPRESSED_ERROR_CODES: ReadonlySet<string> = new Set([\n errorCodes.SAME_STATES,\n errorCodes.TRANSITION_CANCELLED,\n errorCodes.ROUTER_NOT_STARTED,\n errorCodes.ROUTE_NOT_FOUND,\n errorCodes.CANNOT_ACTIVATE,\n errorCodes.CANNOT_DEACTIVATE,\n]);\n\n// Shared per-listener error sink: the EventEmitter reports synchronous listener\n// throws through it, and the EventBusNamespace.subscribe wrapper routes an async\n// listener's rejected Promise through the SAME sink (#944) so both failure modes\n// land in one place.\nfunction logListenerError(eventName: string, error: unknown): void {\n logger.error(\"Router\", `Error in listener for ${eventName}:`, error);\n}\n\n/**\n * Router class with integrated namespace architecture.\n *\n * All functionality is provided by namespace classes:\n * - OptionsNamespace: getOptions (immutable)\n * - DependenciesStore: get/set/remove dependencies\n * - EventEmitter: subscribe\n * - StateNamespace: state storage (getState, setState, getPreviousState)\n * - RoutesNamespace: route tree operations\n * - RouteLifecycleNamespace: canActivate/canDeactivate guards\n * - PluginsNamespace: plugin lifecycle\n * - NavigationNamespace: navigate\n * - RouterLifecycleNamespace: start, stop, isStarted\n *\n * @internal This class implementation is internal. Use createRouter() instead.\n */\nexport class Router<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n> implements RouterInterface<Dependencies> {\n [key: string]: unknown;\n\n // ============================================================================\n // Namespaces\n // ============================================================================\n\n readonly #options: OptionsNamespace;\n readonly #limits: Limits;\n readonly #dependenciesStore: DependenciesStore<Dependencies>;\n readonly #state: StateNamespace;\n readonly #routes: RoutesNamespace<Dependencies>;\n readonly #routeLifecycle: RouteLifecycleNamespace<Dependencies>;\n readonly #plugins: PluginsNamespace<Dependencies>;\n readonly #navigation: NavigationNamespace;\n readonly #lifecycle: RouterLifecycleNamespace;\n\n readonly #eventBus: EventBusNamespace;\n\n // ============================================================================\n // Constructor\n // ============================================================================\n\n /**\n * @param routes - Route definitions\n * @param options - Router options\n * @param dependencies - DI dependencies\n */\n constructor(\n routes: Route<Dependencies>[] = [],\n options: Partial<Options> = {},\n dependencies: Dependencies = {} as Dependencies,\n ) {\n // Extract the logger config WITHOUT mutating the caller's `options` object\n // (#724). NOTE: `logger` (from @real-router/logger) is a process-global\n // singleton — `configure()` applies process-wide and the last call wins\n // across every router in the process. `routerOptions` is the logger-stripped\n // view handed to the options pipeline so `logger` never lands in the frozen\n // router options.\n const { logger: loggerConfig, ...routerOptions } = options;\n\n if (loggerConfig) {\n assertLoggerConfig(loggerConfig);\n logger.configure(loggerConfig);\n }\n\n // =========================================================================\n // Validate inputs before creating namespaces\n // =========================================================================\n\n // Always validate the caller's options (catches non-object / array inputs)\n OptionsNamespace.validateOptionsIsObject(options);\n\n // Unconditional guard-level validation before creating namespaces\n guardDependencies(dependencies);\n\n // Stryker disable next-line EqualityOperator: equivalent — `>= 0` is always true, but `guardRouteStructure([])` on an empty array is a no-op, so validating an empty list behaves identically to skipping it. (ConditionalExpression stays live: `→false` skips validation of a real route list and is killable.)\n if (routes.length > 0) {\n guardRouteStructure(routes);\n }\n\n // =========================================================================\n // Create Namespaces\n // =========================================================================\n\n this.#options = new OptionsNamespace(routerOptions);\n this.#limits = createLimits(routerOptions.limits);\n this.#dependenciesStore =\n createDependenciesStore<Dependencies>(dependencies);\n this.#state = new StateNamespace();\n this.#routes = new RoutesNamespace<Dependencies>(\n routes,\n deriveMatcherOptions(this.#options.get()),\n );\n this.#routeLifecycle = new RouteLifecycleNamespace<Dependencies>();\n this.#plugins = new PluginsNamespace<Dependencies>();\n this.#navigation = new NavigationNamespace();\n this.#lifecycle = new RouterLifecycleNamespace();\n\n // =========================================================================\n // Initialize EventBus\n // =========================================================================\n\n const routerFSM = createRouterFSM();\n\n const emitter = new EventEmitter<RouterEventMap>({\n onListenerError: logListenerError,\n onListenerWarn: (eventName, count) => {\n logger.warn(\n \"router.addEventListener\",\n `Event \"${eventName}\" has ${count} listeners — possible memory leak`,\n );\n },\n });\n\n this.#eventBus = new EventBusNamespace({\n routerFSM,\n emitter,\n onListenerError: logListenerError,\n // The FSM CANCEL action aborts the in-flight\n // navigation controller via this injected effect — \"FSM CANCEL ⟹\n // controller aborted\" in one place. `#navigation` is constructed above.\n abortController: (reason) => {\n this.#navigation.abortCurrentController(reason);\n },\n });\n\n // =========================================================================\n // Register Internals (WeakMap for plugin/infrastructure access)\n // =========================================================================\n // Registered BEFORE wiring (#1331) so every namespace's deps-closure sees a\n // router already present in the internals registry — `getInternals(router)`\n // never throws during wiring, and guard factories flushed at the end of the\n // constructor see a fully-registered instance.\n\n const interceptorsMap: RouterInternals[\"interceptors\"] = new Map();\n\n registerInternals(this, {\n makeState: (name, params, path, meta) =>\n this.#state.makeState(name, params, path, meta),\n // `as unknown as` is required: createBinaryInterceptable returns a\n // non-generic `(a: A, b: B) => R`, but RouterInternals[\"forwardState\"]\n // is declared with a generic parameter `<P extends Params = Params>`,\n // which tsc will not infer from the non-generic source. Sonar S4325\n // misclassifies this as a redundant cast.\n forwardState: createBinaryInterceptable(\n \"forwardState\",\n (name: string, params: Params) =>\n this.#routes.forwardState(name, params),\n interceptorsMap,\n ) as unknown as RouterInternals[\"forwardState\"],\n buildStateResolved: (name, params) =>\n this.#routes.buildStateResolved(name, params),\n matchPath: (path, matchOptions) =>\n this.#routes.matchPath(path, matchOptions),\n getOptions: () => this.#options.get(),\n addEventListener: (eventName, cb) =>\n this.#eventBus.addEventListener(eventName, cb),\n treeChanged: {\n emit: (event) => {\n this.#eventBus.emitTreeChanged(event);\n },\n subscribe: (handler) => this.#eventBus.subscribeTreeChanged(handler),\n listenerCount: () => this.#eventBus.treeChangedListenerCount(),\n isEmitting: () => this.#eventBus.isEmittingTreeChanged(),\n },\n buildPath: createBinaryInterceptable(\n \"buildPath\",\n (route: string, params?: Params) =>\n this.#routes.buildPath(\n route,\n params ?? EMPTY_PARAMS,\n this.#options.get(),\n ),\n interceptorsMap,\n ),\n emitTransitionError: (error) => {\n this.#eventBus.sendFailSafe(undefined, this.#state.get(), error);\n },\n emitTransitionSuccess: (toState, fromState, opts) => {\n this.#eventBus.emitTransitionSuccess(toState, fromState, opts);\n },\n navigateToNotFound: (path) => this.#navigation.navigateToNotFound(path),\n start: createInterceptable(\n \"start\",\n (path: string) => {\n return this.#lifecycle.start(path);\n },\n interceptorsMap,\n ),\n navigateToState: (state, navOpts) => {\n // Plugin-only navigation primitive (#525). Mirrors the same\n // unhandled-rejection suppression and lastSync* bookkeeping used by\n // the public Router.navigate facade so plugin call-sites can\n // fire-and-forget the returned promise (popstate handlers do).\n this.#assertNotReentrant();\n\n const promiseState = this.#navigation.navigateToState(\n state,\n navOpts ?? EMPTY_OPTS,\n );\n\n if (this.#navigation.lastSyncResolved) {\n this.#navigation.lastSyncResolved = false;\n } else if (this.#navigation.lastSyncRejected) {\n this.#navigation.lastSyncRejected = false;\n } else {\n Router.#suppressUnhandledRejection(promiseState);\n }\n\n return promiseState;\n },\n interceptors: interceptorsMap,\n setRootPath: (rootPath) => {\n this.#routes.setRootPath(rootPath);\n },\n getRootPath: () => this.#routes.getStore().rootPath,\n getTree: () => this.#routes.getStore().tree,\n isDisposed: () => this.#eventBus.isDisposed(),\n validator: null,\n // Dependencies (issue #172)\n dependenciesGetStore: () => this.#dependenciesStore,\n // Clone support (issue #173)\n getCloneState: () => ({\n options: { ...this.#options.get() },\n dependencies: { ...this.#dependenciesStore.dependencies },\n pluginFactories: this.#plugins.getAll(),\n }),\n routeGetStore: () => this.#routes.getStore(),\n // Cross-namespace state (issue #174)\n getStateName: () => this.#state.get()?.name,\n isTransitioning: () => this.#eventBus.isTransitioning(),\n clearState: () => {\n this.#state.set(undefined);\n },\n setState: (state) => {\n this.#state.set(state);\n },\n routerExtensions: [],\n contextClaimRecords: new Set(),\n hydrationState: null,\n });\n\n // =========================================================================\n // Wire Dependencies\n // =========================================================================\n\n wireNamespaces<Dependencies>({\n router: this,\n options: this.#options,\n limits: this.#limits,\n dependenciesStore: this.#dependenciesStore,\n state: this.#state,\n routes: this.#routes,\n routeLifecycle: this.#routeLifecycle,\n plugins: this.#plugins,\n navigation: this.#navigation,\n lifecycle: this.#lifecycle,\n eventBus: this.#eventBus,\n });\n\n // =========================================================================\n // Bind Public Methods\n // =========================================================================\n // All public methods that access private fields must be bound to preserve\n // `this` context when methods are extracted as references.\n // See: https://github.com/tc39/proposal-bind-operator\n // =========================================================================\n\n // Path & State Building\n this.isActiveRoute = this.isActiveRoute.bind(this);\n this.buildPath = this.buildPath.bind(this);\n\n // State Management\n this.getState = this.getState.bind(this);\n this.getPreviousState = this.getPreviousState.bind(this);\n this.areStatesEqual = this.areStatesEqual.bind(this);\n this.shouldUpdateNode = this.shouldUpdateNode.bind(this);\n\n // Router Lifecycle\n this.isActive = this.isActive.bind(this);\n this.start = this.start.bind(this);\n this.stop = this.stop.bind(this);\n this.dispose = this.dispose.bind(this);\n\n // Route Lifecycle (Guards)\n this.canNavigateTo = this.canNavigateTo.bind(this);\n\n // Plugins\n this.usePlugin = this.usePlugin.bind(this);\n\n // Navigation\n this.navigate = this.navigate.bind(this);\n this.navigateToDefault = this.navigateToDefault.bind(this);\n this.navigateToNotFound = this.navigateToNotFound.bind(this);\n\n // Subscription\n this.subscribe = this.subscribe.bind(this);\n this.subscribeLeave = this.subscribeLeave.bind(this);\n this.isLeaveApproved = this.isLeaveApproved.bind(this);\n\n // =========================================================================\n // Flush initial-route guard factories\n // =========================================================================\n // Deferred out of wiring (#1331): the pending canActivate/canDeactivate\n // factories from initial route definitions are compiled and executed HERE,\n // on the fully-built and bound router — a factory calling read-only methods\n // (`buildPath()`, `isActiveRoute()`, `getState()`) no longer hits a\n // half-assembled instance. Side-effectful calls (`navigate`, `usePlugin`,\n // route-CRUD) stay OUT OF CONTRACT: factories re-execute outside the\n // constructor (cloneRouter re-compiles definition guards per clone;\n // #recompileSlot re-runs a factory after a definition-only clear), so any\n // side effect would duplicate per re-execution — see CLAUDE.md. Runtime\n // add()/replace() compile guards in their own PREPARE phase and never touch\n // these pending maps.\n //\n // Fail-closed on a factory throw: by this point a router reference leaked\n // from an earlier factory is fully operational, while later guards would\n // stay silently unregistered — a fail-open guard bypass. Disposing before\n // the rethrow turns any leaked reference into a ROUTER_DISPOSED-throwing\n // husk (pre-#1331 such a reference was inert because getInternals threw).\n try {\n this.#routes.flushPendingGuards();\n } catch (error) {\n this.dispose();\n\n throw error;\n }\n }\n\n // ============================================================================\n // Path & State Building\n // ============================================================================\n\n isActiveRoute(\n name: string,\n params?: Params,\n strictEquality?: boolean,\n ignoreQueryParams?: boolean,\n ): boolean {\n getInternals(this).validator?.routes.validateIsActiveRouteArgs(\n name,\n params,\n strictEquality,\n ignoreQueryParams,\n );\n\n getInternals(this).validator?.routes.validateRouteName(\n name,\n \"isActiveRoute\",\n );\n\n // Empty string is special case - warn and return false (root node is not a parent)\n if (name === \"\") {\n logger.warn(\n \"real-router\",\n 'isActiveRoute(\"\") called with empty string. Root node is not considered a parent of any route.',\n );\n\n return false;\n }\n\n return this.#routes.isActiveRoute(\n name,\n params,\n strictEquality,\n ignoreQueryParams,\n );\n }\n\n buildPath(route: string, params?: Params): string {\n const ctx = getInternals(this);\n\n ctx.validator?.routes.validateBuildPathArgs(route);\n ctx.validator?.navigation.validateParams(params, \"buildPath\");\n\n return ctx.buildPath(route, normalizeParams(params));\n }\n\n // ============================================================================\n // State Management (delegated to StateNamespace)\n // ============================================================================\n\n getState<P extends Params = Params>(): State<P> | undefined {\n return this.#state.get<P>();\n }\n\n getPreviousState(): State | undefined {\n return this.#state.getPrevious();\n }\n\n areStatesEqual(\n state1: State | undefined,\n state2: State | undefined,\n ignoreQueryParams = true,\n ): boolean {\n getInternals(this).validator?.state.validateAreStatesEqualArgs(\n state1,\n state2,\n ignoreQueryParams,\n );\n\n return this.#state.areStatesEqual(state1, state2, ignoreQueryParams);\n }\n\n shouldUpdateNode(\n nodeName: string,\n ): (toState: State, fromState?: State) => boolean {\n getInternals(this).validator?.routes.validateShouldUpdateNodeArgs(nodeName);\n\n return RoutesNamespace.shouldUpdateNode(nodeName);\n }\n\n // ============================================================================\n // Router Lifecycle\n // ============================================================================\n\n isActive(): boolean {\n return this.#eventBus.isActive();\n }\n\n start(startPath: string): Promise<State> {\n if (!this.#eventBus.canStart()) {\n return Promise.reject(CACHED_ALREADY_STARTED_ERROR);\n }\n\n getInternals(this).validator?.navigation.validateStartArgs(startPath);\n\n // FSM bookkeeping is split across the facade and RouterLifecycleNamespace by\n // design, NOT a missed consolidation (#940): `sendStart()` runs HERE, before\n // the interceptor chain, so the STARTING window spans the whole start\n // pipeline. A pre-`next()` interceptor throw then unwinds via STARTING →\n // `sendFail`, which emits TRANSITION_ERROR from STARTING (EventBusNamespace\n // FAIL action) for `onTransitionError` plugins. Moving `sendStart()` into the\n // namespace (the interceptor *target*) would skip STARTING on a pre-`next()`\n // throw — the namespace is never reached — silently dropping that\n // TRANSITION_ERROR: a #668 regression. The commit (`completeStart`) lives in\n // the namespace; recovery needs facade state (`#state`, `#lifecycle`), so it\n // stays here in `#unwindFailedStart`.\n this.#eventBus.sendStart();\n\n // Convert sync interceptor throws to rejections so the recovery path is\n // reachable; otherwise the throw escapes synchronously, the FSM is left in\n // STARTING, and the router is permanently bricked (#668).\n let internalStart: Promise<State>;\n\n try {\n const chainResult: unknown = getInternals(this).start(startPath);\n\n // A `start` interceptor that returns without calling next() yields a\n // non-thenable (typically undefined); the `.catch` below would then throw\n // a cryptic `TypeError: ...reading 'catch'` and leave the FSM stuck in\n // STARTING. Reject with an actionable message so recovery unwinds via\n // #unwindFailedStart — the same deferred-crash class as the #939\n // start-path guard (#1411).\n internalStart =\n typeof (chainResult as { then?: unknown } | null | undefined)?.then ===\n \"function\"\n ? (chainResult as Promise<State>)\n : Promise.reject(\n new TypeError(\n \"[router.start] a `start` interceptor returned without calling next(). Every start interceptor must return `next(path)`.\",\n ),\n );\n } catch (syncError: unknown) {\n // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors -- preserve original throw shape from user-provided start interceptor\n internalStart = Promise.reject(syncError);\n }\n\n const promiseState = internalStart.catch((error: unknown) =>\n this.#unwindFailedStart(error),\n );\n\n Router.#suppressUnhandledRejection(\n promiseState,\n Router.#onSuppressedStartError,\n );\n\n return promiseState;\n }\n\n stop(): this {\n // SendCancelIfPossible → FSM CANCEL → the CANCEL action\n // aborts the in-flight controller (waking the pipeline). No separate abort.\n this.#eventBus.sendCancelIfPossible(this.#state.get());\n\n // `isStarting()` is included (#1185): a stop() while `start()` is parked in\n // an async start-interceptor (FSM STARTING, before `next()`) must cancel the\n // start, not silently no-op. `sendStop()` takes STARTING → IDLE (FSM table),\n // and `RouterLifecycleNamespace.start` re-checks `isIdle()` after the\n // interceptor chain and rejects with TRANSITION_CANCELLED — mirroring the\n // guard-phase behavior (which already cancels from TRANSITION_STARTED).\n if (\n !this.#eventBus.isReady() &&\n !this.#eventBus.isTransitioning() &&\n !this.#eventBus.isStarting()\n ) {\n return this;\n }\n\n this.#lifecycle.stop();\n this.#eventBus.sendStop();\n\n return this;\n }\n\n dispose(): void {\n // Stryker disable next-line BlockStatement: equivalent — emptying the early-return re-runs the dispose body on a 2nd call, but it is fully idempotent (FSM `send(DISPOSE)` no-ops from DISPOSED, `disposeAll()` already cleared `#unsubscribes`, every clear is idempotent). (ConditionalExpression stays live: `→true` always-returns and never disposes = killed.)\n if (this.#eventBus.isDisposed()) {\n return;\n }\n\n // the FSM CANCEL action aborts the in-flight controller.\n this.#eventBus.sendCancelIfPossible(this.#state.get());\n\n if (this.#eventBus.isReady() || this.#eventBus.isTransitioning()) {\n this.#lifecycle.stop();\n this.#eventBus.sendStop();\n }\n\n this.#eventBus.sendDispose();\n this.#eventBus.clearAll();\n\n this.#plugins.disposeAll();\n\n // Safety net: clean up extensions plugins failed to remove in teardown\n const ctx = getInternals(this);\n\n for (const extension of ctx.routerExtensions) {\n for (const key of extension.keys) {\n delete (this as Record<string, unknown>)[key];\n }\n }\n\n ctx.routerExtensions.length = 0;\n\n // Safety net: release context namespace claims plugins failed to release in teardown\n ctx.contextClaimRecords.clear();\n\n // Safety net: drop interceptors plugins failed to remove in teardown (#1199).\n // The third per-plugin registration channel — symmetric with routerExtensions\n // / contextClaimRecords above. `buildPath` is not method-swapped by dispose\n // and reads this Map live, so a leaked interceptor would otherwise still run\n // on the disposed router.\n ctx.interceptors.clear();\n\n this.#routes.clearRoutes();\n this.#routeLifecycle.clearAll();\n this.#state.reset();\n this.#dependenciesStore.dependencies = Object.create(\n null,\n ) as Partial<Dependencies>;\n\n this.#markDisposed();\n }\n\n // ============================================================================\n // Route Lifecycle (Guards)\n // ============================================================================\n\n canNavigateTo(name: string, params?: Params): boolean {\n const ctx = getInternals(this);\n\n ctx.validator?.routes.validateRouteName(name, \"canNavigateTo\");\n ctx.validator?.navigation.validateParams(params, \"canNavigateTo\");\n\n if (!this.#routes.hasRoute(name)) {\n return false;\n }\n\n const { name: resolvedName, params: resolvedParams } = ctx.forwardState(\n name,\n params ?? {},\n );\n\n // Build `toState` exactly as `buildNavigateState` does — WITH route-meta and\n // normalized params — so `getTransitionPath` takes its STANDARD PATH and\n // trims the shared ancestor, mirroring navigate's guard set (#970). A\n // meta-less `toState` makes both sides meta-less (the committed `getState()`\n // carries no meta after a path-matched `start()`), so `getTransitionPath`\n // takes FAST PATH 3 and (de)activates the WHOLE chain incl. shared ancestors\n // → false-negative (\"Link disabled though the click would succeed\").\n // `normalizeParams` also aligns the params guards observe with navigate's.\n // `skipFreeze` (5th arg) mirrors the navigate guard phase, where guards see\n // an unfrozen, transition-less `toState` (freeze happens later in\n // `completeTransition`).\n //\n // A capability predicate must answer, not throw: if the target path can't be\n // built from these params (e.g. a required path param is missing), the route\n // is simply unreachable with this input — return `false` rather than letting\n // `buildPath` throw (#725).\n let toState: State;\n\n try {\n const normalizedParams = normalizeParams(resolvedParams);\n const meta = this.#routes.getMetaForState(resolvedName);\n const path = ctx.buildPath(resolvedName, normalizedParams);\n\n toState = this.#state.makeState(\n resolvedName,\n normalizedParams,\n path,\n meta,\n true,\n );\n } catch {\n return false;\n }\n\n const fromState = this.#state.get();\n\n const { toDeactivate, toActivate } = getTransitionPath(toState, fromState);\n\n return this.#routeLifecycle.canNavigateTo(\n toDeactivate,\n toActivate,\n toState,\n fromState,\n );\n }\n\n // ============================================================================\n // Plugins\n // ============================================================================\n\n usePlugin(\n ...plugins: (PluginFactory<Dependencies> | false | null | undefined)[]\n ): Unsubscribe {\n // Post-dispose guard, mirroring #946 for subscribe/subscribeLeave. A\n // reference captured before dispose() (`const up = router.usePlugin`)\n // bypasses the #markDisposed method swap, so the swap alone is not enough:\n // without this, the factory would run on a disposed router (real side\n // effects), listeners would land in the cleared emitter, and teardown would\n // never fire — a silent zombie plugin (#1196).\n if (this.#eventBus.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n const filtered = plugins.filter(Boolean) as PluginFactory<Dependencies>[];\n\n if (filtered.length === 0) {\n return () => {};\n }\n\n const ctx = getInternals(this);\n\n ctx.validator?.plugins.validatePluginLimit(\n this.#plugins.count(),\n this.#limits,\n );\n for (const plugin of filtered) {\n // `getAll()` sits inside the optional-chain argument on purpose: with no\n // validator installed (production default) the `?.` short-circuits and the\n // array is never allocated. Hoisting it out would either allocate on the\n // no-validator hot path or push the dev-only branch out of coverage.\n ctx.validator?.plugins.validateNoDuplicatePlugins(\n plugin,\n this.#plugins.getAll(),\n );\n }\n\n return this.#plugins.use(...filtered);\n }\n\n // ============================================================================\n // Subscription (backed by EventEmitter)\n // ============================================================================\n\n subscribe(listener: SubscribeFn): Unsubscribe {\n EventBusNamespace.validateSubscribeListener(listener);\n\n return this.#eventBus.subscribe(listener);\n }\n\n subscribeLeave(listener: LeaveFn): Unsubscribe {\n EventBusNamespace.validateSubscribeLeaveListener(listener);\n\n return this.#eventBus.subscribeLeave(listener);\n }\n\n isLeaveApproved(): boolean {\n return this.#eventBus.isLeaveApproved();\n }\n\n // ============================================================================\n // Navigation\n // ============================================================================\n\n navigate(\n routeName: string,\n routeParams?: Params,\n options?: NavigationOptions,\n ): Promise<State> {\n this.#assertNotReentrant();\n\n const ctx = getInternals(this);\n\n ctx.validator?.navigation.validateNavigateArgs(routeName);\n ctx.validator?.navigation.validateParams(routeParams, \"navigate\");\n\n const opts = options ?? EMPTY_OPTS;\n\n ctx.validator?.navigation.validateNavigationOptions(opts, \"navigate\");\n\n const promiseState = this.#navigation.navigate(\n routeName,\n routeParams ?? EMPTY_PARAMS,\n opts,\n );\n\n if (this.#navigation.lastSyncResolved) {\n this.#navigation.lastSyncResolved = false;\n } else if (this.#navigation.lastSyncRejected) {\n // Cached rejection — already pre-suppressed at module load, skip .catch()\n this.#navigation.lastSyncRejected = false;\n } else {\n Router.#suppressUnhandledRejection(promiseState);\n }\n\n return promiseState;\n }\n\n navigateToDefault(options?: NavigationOptions): Promise<State> {\n this.#assertNotReentrant();\n\n const ctx = getInternals(this);\n\n ctx.validator?.navigation.validateNavigateToDefaultArgs(options);\n\n const opts = options ?? EMPTY_OPTS;\n\n ctx.validator?.navigation.validateNavigationOptions(\n opts,\n \"navigateToDefault\",\n );\n\n const promiseState = this.#navigation.navigateToDefault(opts);\n\n if (this.#navigation.lastSyncResolved) {\n this.#navigation.lastSyncResolved = false;\n } else if (this.#navigation.lastSyncRejected) {\n this.#navigation.lastSyncRejected = false;\n } else {\n Router.#suppressUnhandledRejection(promiseState);\n }\n\n return promiseState;\n }\n\n navigateToNotFound(path?: string): State {\n this.#assertNotReentrant();\n\n if (!this.#eventBus.isActive()) {\n throw new RouterError(errorCodes.ROUTER_NOT_STARTED);\n }\n\n if (path !== undefined && typeof path !== \"string\") {\n throw new TypeError(\n `[router.navigateToNotFound] path must be a string, got ${typeof path}`,\n );\n }\n\n if (path !== undefined) {\n return this.#navigation.navigateToNotFound(path);\n }\n\n // #1172: a path-less call derives the default path from the committed state.\n // During the two-phase start window the router is active (`isActive()` true)\n // while `getState()` is still undefined, so throw an actionable RouterError\n // instead of a cryptic `TypeError` from dereferencing the absent state —\n // same class as the #939 always-on invariant guards.\n const current = this.#state.get();\n\n if (current === undefined) {\n throw new RouterError(errorCodes.ROUTER_NOT_STARTED, {\n message:\n \"[router.navigateToNotFound] cannot derive the path before the start navigation commits — pass an explicit path\",\n });\n }\n\n return this.#navigation.navigateToNotFound(current.path);\n }\n\n /**\n * Classifies a fire-and-forget rejection as an EXPECTED outcome that must\n * stay silent (no log). Shared by the navigate and start suppressors so the\n * suppression contract lives in one place.\n *\n * A suppressed RouterError code is a normal caller-owned navigation result\n * (a guard block, SAME_STATES, ROUTER_NOT_STARTED, …) — see\n * SUPPRESSED_ERROR_CODES (#721).\n *\n * The #945 RecursionDepthError carve-out is gone: a reentrant navigate() from a\n * listener can no longer self-feed — it throws REENTRANT_NAVIGATION\n * synchronously at the facade (RFC navigation-cancellation-unification §4), so\n * navigate()'s promise never rejects with a recursion error (re-entrant emits\n * are coalesced at the emitter, #1033; reentrant route-CRUD throws\n * REENTRANT_TREE_MUTATION to the CRUD caller, #1032 — not through a navigate\n * promise).\n */\n static #isExpectedRejection(error: unknown): boolean {\n return (\n error instanceof RouterError && SUPPRESSED_ERROR_CODES.has(error.code)\n );\n }\n\n /**\n * Pre-allocated suppressor for navigate / navigateToDefault / navigateToState.\n * Avoids creating a new closure on every navigate() call.\n *\n * The log line IS reachable (contrary to the pre-#931 \"unreachable\" comment):\n * a subscribeLeave listener that throws (sync or async) rejects navigate()\n * with the original NON-suppressed error — not re-coded to TRANSITION_CANCELLED\n * — and a Symbol path-param's stringify TypeError is likewise non-suppressed.\n * Both surface here under \"router.navigate\". Tested in guard-block-suppression\n * (negative) and the positive case below.\n */\n static readonly #onSuppressedNavigateError = (error: unknown): void => {\n if (Router.#isExpectedRejection(error)) {\n return;\n }\n\n logger.error(\"router.navigate\", \"Unexpected navigation error\", error);\n };\n\n /**\n * Pre-allocated suppressor for start(). Its failures must surface under their\n * own \"router.start\" category rather than being misattributed to\n * \"router.navigate\" (#931). The log line is reachable: a start interceptor\n * that throws a plain Error after next() committed (the SSR/RSC loader window,\n * #763) — or a cryptic path TypeError — is not a suppressed RouterError.\n */\n static readonly #onSuppressedStartError = (error: unknown): void => {\n if (Router.#isExpectedRejection(error)) {\n return;\n }\n\n logger.error(\"router.start\", \"Unexpected start error\", error);\n };\n\n /**\n * Fire-and-forget safety: prevents unhandled rejection warnings when\n * navigate/navigateToDefault/start is called without await. Expected errors\n * are silently suppressed; unexpected ones are logged under `onSuppressed`'s\n * category — navigate by default; start() passes #onSuppressedStartError so\n * its failures are logged as \"router.start\", not \"router.navigate\" (#931).\n */\n static #suppressUnhandledRejection(\n promise: Promise<State>,\n onSuppressed: (error: unknown) => void = Router.#onSuppressedNavigateError,\n ): void {\n promise.catch(onSuppressed);\n }\n\n /**\n * Rejects a synchronous reentrant navigation — `navigate` /\n * `navigateToDefault` / `navigateToState` / `navigateToNotFound` called from\n * inside a transition-event listener while a transition is being dispatched\n * (RFC navigation-cancellation-unification §4). Throws synchronously: inside a\n * listener the emit's `onListenerError` isolation surfaces it (visible,\n * non-fatal); a DEFERRED (async / microtask) navigate from a listener runs\n * after dispatch settles and is allowed. Always-on core invariant guard (not\n * validator-gated).\n */\n #assertNotReentrant(): void {\n if (this.#eventBus.isProcessing()) {\n throw new RouterError(errorCodes.REENTRANT_NAVIGATION);\n }\n }\n\n /**\n * Settles the FSM after a failed start pipeline, then re-throws so the\n * rejection still surfaces to the caller. Three cases, by what the pipeline\n * reached before throwing:\n *\n * - **Pre-commit, READY** (`isReady()` and no committed state): an interceptor\n * threw after `completeStart()` reached READY but before any state committed\n * (e.g. an activation guard blocked the start navigation) — return READY →\n * IDLE via `stop()` so the router is reusable.\n * - **Pre-commit, STARTING** (`isStarting()`): the pipeline threw before\n * `completeStart()` — a sync interceptor throw before `next()`, or a throw\n * inside the namespace before commit — so unwind STARTING → IDLE via\n * `sendFail`, which also emits TRANSITION_ERROR from STARTING (#668).\n * - **Post-commit, READY with committed state** (neither branch fires): a\n * loader/interceptor threw AFTER `navigateToState` committed and emitted\n * TRANSITION_SUCCESS (the SSR/RSC loader window). Keep the committed state —\n * rolling back would retract an observed success (\"phantom success\", #763);\n * the error still surfaces via the re-throw.\n */\n #unwindFailedStart(error: unknown): never {\n if (this.#eventBus.isReady() && this.#state.get() === undefined) {\n this.#lifecycle.stop();\n this.#eventBus.sendStop();\n } else if (this.#eventBus.isStarting()) {\n this.#eventBus.sendFail(undefined, undefined, error);\n }\n\n throw error;\n }\n\n #markDisposed(): void {\n this.navigate = throwDisposed;\n this.navigateToDefault = throwDisposed;\n this.navigateToNotFound = throwDisposed;\n this.start = throwDisposed;\n this.stop = throwDisposed;\n this.usePlugin = throwDisposed;\n\n this.subscribe = throwDisposed;\n this.subscribeLeave = throwDisposed;\n this.canNavigateTo = throwDisposed;\n }\n}\n\nfunction throwDisposed(): never {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n}\n\n/**\n * Derives CreateMatcherOptions from router Options.\n * Maps core option names to matcher option names.\n */\nfunction deriveMatcherOptions(\n options: Readonly<Options>,\n): CreateMatcherOptions {\n return {\n strictTrailingSlash: options.trailingSlash === \"strict\",\n caseSensitive: options.caseSensitive,\n strictQueryParams: options.queryParamsMode === \"strict\",\n urlParamsEncoding: options.urlParamsEncoding,\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n queryParams: options.queryParams!,\n };\n}\n"],"mappings":"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"}