@real-router/core 0.82.3 → 0.83.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- const e=require("./channels/guard.js"),t=require("./constants.js"),n=require("./guards.js"),r=require("./helpers.js"),i=require("./internals.js"),a=require("./limits.js"),o=require("./namespaces/DependenciesNamespace/dependenciesStore.js"),s=require("./namespaces/OptionsNamespace/OptionsNamespace.js"),c=require("./pipeline/canonicalize.js"),l=require("./pipeline/buildURL.js"),u=require("./pipeline/materialize.js"),d=require("./namespaces/StateNamespace/StateNamespace.js"),f=require("./namespaces/PluginsNamespace/PluginsNamespace.js"),p=require("./namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.js"),m=require("./transitionPath.js"),h=require("./namespaces/RoutesNamespace/RoutesNamespace.js"),g=require("./RouterError.js"),_=require("./namespaces/NavigationNamespace/constants.js"),v=require("./namespaces/NavigationNamespace/NavigationNamespace.js"),y=require("./namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.js"),b=require("./routerFSM.js"),x=require("./namespaces/EventBusNamespace/EventBusNamespace.js"),S=require("./namespaces/RouterLifecycleNamespace/constants.js"),C=require("./utils/event-emitter/EventEmitter.js"),w=require("./utils/logger/RouterLogger.js"),T=require("./wiring/wireNamespaces.js"),E=Object.freeze({});var D=class D{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l;#u;constructor(r=[],c={},l={}){let{logger:u,...m}=c;u&&n.assertLoggerConfig(u);let g=new w.RouterLogger(u);this.#u=e=>{_.isExpectedRejection(e)||g.error(`router.start`,`Unexpected start error`,e)},s.OptionsNamespace.validateOptionsIsObject(c),n.guardDependencies(l),r.length>0&&n.guardRouteStructure(r),this.#e=new s.OptionsNamespace(m),this.#t=a.createLimits(m.limits),this.#n=o.createDependenciesStore(l),this.#r=new d.StateNamespace,this.#i=new h.RoutesNamespace(r,k(this.#e.get()),g),this.#a=new p.RouteLifecycleNamespace,this.#o=new f.PluginsNamespace,this.#s=new v.NavigationNamespace,this.#c=new y.RouterLifecycleNamespace;let S=b.createRouterFSM(),O=new C.EventEmitter({onListenerError:(e,t)=>{g.error(`Router`,`Error in listener for ${e}:`,t)},onListenerWarn:(e,t)=>{g.warn(`router.addEventListener`,`Event "${e}" has ${t} listeners — possible memory leak`)}});this.#l=new x.EventBusNamespace({routerFSM:S,emitter:O,abortController:e=>{this.#s.abortCurrentController(e)}});let A=new Map,j=i.createTernaryInterceptable(`forwardState`,(e,t,n)=>this.#i.forwardState(e,t,n),A);i.registerInternals(this,{logger:g,makeState:(e,t,n,r)=>this.#r.makeState(e,t,n,r),getMetaForState:e=>this.#i.getMetaForState(e),getQueryParams:e=>this.#i.getQueryParams(e),forwardState:((n,r,i)=>{let a=j(n,r,i);return e.assertChannelCorrect(`forwardState`,a.name,a.params,this.#i.getQueryParams(a.name),()=>a.name===n?"the `params` bag leaving the forwardState chain":`the \`params\` bag leaving the forwardState chain (forwarded here from "${n}")`),{name:a.name,params:a.params??t.EMPTY_PARAMS,search:a.search}}),buildStateResolved:(e,t)=>this.#i.buildStateResolved(e,t),port:()=>this.#i.getPort(),matchPath:(e,t)=>this.#i.matchPath(e,t),getOptions:()=>this.#e.get(),addEventListener:(e,t)=>this.#l.addEventListener(e,t),treeChanged:{emit:e=>{this.#l.emitTreeChanged(e)},subscribe:e=>this.#l.subscribeTreeChanged(e),listenerCount:()=>this.#l.treeChangedListenerCount(),isEmitting:()=>this.#l.isEmittingTreeChanged()},buildPath:i.createTernaryInterceptable(`buildPath`,(e,n,r)=>this.#i.buildPath(e,n??t.EMPTY_PARAMS,r,this.#e.get()),A),emitTransitionError:e=>{this.#l.sendFailSafe(void 0,this.#r.get(),e)},emitTransitionSuccess:(e,t,n)=>{this.#l.emitTransitionSuccess(e,t,n)},navigateToNotFound:e=>this.#s.navigateToNotFound(e),start:i.createInterceptable(`start`,e=>this.#c.start(e),A),navigateToState:(e,t)=>(this.#p(),D.#d(this.#s.navigateToState(e,t??E))),interceptors:A,setRootPath:e=>{this.#i.setRootPath(e)},getRootPath:()=>this.#i.getStore().rootPath,getTree:()=>this.#i.getStore().tree,isDisposed:()=>this.#l.isDisposed(),validator:null,dependenciesGetStore:()=>this.#n,getCloneState:()=>({options:{...this.#e.get()},dependencies:{...this.#n.dependencies},pluginFactories:this.#o.getAll(),loggerConfig:g.getConfig()}),routeGetStore:()=>this.#i.getStore(),getStateName:()=>this.#r.get()?.name,isTransitioning:()=>this.#l.isTransitioning(),clearState:()=>{this.#r.set(void 0)},setState:e=>{this.#r.set(e)},routerExtensions:[],contextClaimRecords:new Set,hydrationState:null}),T.wireNamespaces({router:this,options:this.#e,limits:this.#t,dependenciesStore:this.#n,state:this.#r,routes:this.#i,routeLifecycle:this.#a,plugins:this.#o,navigation:this.#s,lifecycle:this.#c,eventBus:this.#l}),this.isActiveRoute=this.isActiveRoute.bind(this),this.buildPath=this.buildPath.bind(this),this.getState=this.getState.bind(this),this.getPreviousState=this.getPreviousState.bind(this),this.areStatesEqual=this.areStatesEqual.bind(this),this.shouldUpdateNode=this.shouldUpdateNode.bind(this),this.isActive=this.isActive.bind(this),this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.dispose=this.dispose.bind(this),this.canNavigateTo=this.canNavigateTo.bind(this),this.usePlugin=this.usePlugin.bind(this),this.navigate=this.navigate.bind(this),this.navigateToDefault=this.navigateToDefault.bind(this),this.navigateToNotFound=this.navigateToNotFound.bind(this),this.subscribe=this.subscribe.bind(this),this.subscribeLeave=this.subscribeLeave.bind(this),this.isLeaveApproved=this.isLeaveApproved.bind(this);try{this.#i.flushPendingGuards()}catch(e){throw this.dispose(),e}}isActiveRoute(e,t,n,r,a){return i.getInternals(this).validator?.routes.validateIsActiveRouteArgs(e,t,r,a),i.getInternals(this).validator?.routes.validateRouteName(e,`isActiveRoute`),e===``?(i.getInternals(this).logger.warn(`real-router`,`isActiveRoute("") called with empty string. Root node is not considered a parent of any route.`),!1):this.#i.isActiveRoute(e,t,n,r,a)}buildPath(e,t,n){let a=i.getInternals(this);return a.validator?.routes.validateBuildPathArgs(e),a.validator?.navigation.validateParams(t,`buildPath`),a.buildPath(e,r.normalizeParams(t),n)}getState(){return this.#r.get()}getPreviousState(){return this.#r.getPrevious()}areStatesEqual(e,t,n=!0){return i.getInternals(this).validator?.state.validateAreStatesEqualArgs(e,t,n),this.#r.areStatesEqual(e,t,n)}shouldUpdateNode(e){return i.getInternals(this).validator?.routes.validateShouldUpdateNodeArgs(e),h.RoutesNamespace.shouldUpdateNode(e,e=>this.#i.getMetaForState(e))}isActive(){return this.#l.isActive()}start(e){let t=this.#f(e);return t.catch(this.#u),t}stop(){return this.#l.sendCancelIfPossible(this.#r.get()),!this.#l.isReady()&&!this.#l.isTransitioning()&&!this.#l.isStarting()?this:(this.#c.stop(),this.#l.sendStop(),this)}dispose(){if(this.#l.isDisposed())return;this.#l.sendCancelIfPossible(this.#r.get()),(this.#l.isReady()||this.#l.isTransitioning())&&(this.#c.stop(),this.#l.sendStop()),this.#l.sendDispose(),this.#l.clearAll(),this.#o.disposeAll();let e=i.getInternals(this);for(let t of e.routerExtensions)for(let e of t.keys)delete this[e];e.routerExtensions.length=0,e.contextClaimRecords.clear(),e.interceptors.clear(),this.#i.clearRoutes(),this.#a.clearAll(),this.#r.reset(),this.#n.dependencies=Object.create(null),this.#h()}canNavigateTo(n,r,a){let o=i.getInternals(this);if(o.validator?.routes.validateRouteName(n,`canNavigateTo`),o.validator?.navigation.validateParams(r,`canNavigateTo`),!this.#i.hasRoute(n)||e.findMisChanneledKey(r,this.#i.getQueryParams(n))!==void 0)return!1;let s=this.#i.getPort(),d;try{d=c.canonicalize(s,n,r??t.EMPTY_PARAMS,a)}catch(e){return o.logger.warn(`router.canNavigateTo`,`Resolving route "${n}" threw while answering the predicate; treating the route as unreachable.`,e),!1}let f;try{f=u.materialize(d,{path:l.buildURL(d,s),skipFreeze:!0})}catch{return!1}let p=this.#r.get(),{toDeactivate:h,toActivate:g}=m.getTransitionPath(f,p,e=>this.#i.getMetaForState(e));return this.#a.canNavigateTo(h,g,f,p)}usePlugin(...e){if(this.#l.isDisposed())throw new g.RouterError(t.errorCodes.ROUTER_DISPOSED);let n=e.filter(Boolean);if(n.length===0)return()=>{};let r=i.getInternals(this);r.validator?.plugins.validatePluginLimit(this.#o.count(),this.#t);for(let e of n)r.validator?.plugins.validateNoDuplicatePlugins(e,this.#o.getAll());return this.#o.use(...n)}subscribe(e){return x.EventBusNamespace.validateSubscribeListener(e),this.#l.subscribe(e)}subscribeLeave(e){return x.EventBusNamespace.validateSubscribeLeaveListener(e),this.#l.subscribeLeave(e)}isLeaveApproved(){return this.#l.isLeaveApproved()}navigate(e,n,r,a){this.#p();let o=i.getInternals(this),s,c,l,u;return typeof e==`object`&&e?(s=e.name,c=e.params,l=e.search,u=n??E):(s=e,c=n,l=r,u=a??E),i.throwOnMisChanneledKey(o,`navigate`,s,c),o.validator?.navigation.validateNavigateArgs(s),o.validator?.navigation.validateParams(c,`navigate`),o.validator?.navigation.validateNavigationOptions(u,`navigate`),D.#d(this.#s.navigate(s,c??t.EMPTY_PARAMS,l,u))}navigateToDefault(e){this.#p();let t=i.getInternals(this);t.validator?.navigation.validateNavigateToDefaultArgs(e);let n=e??E;return t.validator?.navigation.validateNavigationOptions(n,`navigateToDefault`),D.#d(this.#s.navigateToDefault(n))}navigateToNotFound(e){if(this.#p(),!this.#l.isActive())throw new g.RouterError(t.errorCodes.ROUTER_NOT_STARTED);if(e!==void 0&&typeof e!=`string`)throw TypeError(`[router.navigateToNotFound] path must be a string, got ${typeof e}`);if(e!==void 0)return this.#s.navigateToNotFound(e);let n=this.#r.get();if(n===void 0)throw new g.RouterError(t.errorCodes.ROUTER_NOT_STARTED,{message:`[router.navigateToNotFound] cannot derive the path before the start navigation commits — pass an explicit path`});return this.#s.navigateToNotFound(n.path)}static#d(e){return e instanceof Promise?e:Promise.resolve(e)}#f(e){if(!this.#l.canStart())return Promise.reject(S.CACHED_ALREADY_STARTED_ERROR);i.getInternals(this).validator?.navigation.validateStartArgs(e),this.#l.sendStart();let t;try{let n=i.getInternals(this).start(e);t=typeof n?.then==`function`?n:Promise.reject(TypeError("[router.start] a `start` interceptor returned without calling next(). Every start interceptor must return `next(path)`."))}catch(e){t=Promise.reject(e)}return t.catch(e=>this.#m(e))}#p(){if(this.#l.isProcessing())throw new g.RouterError(t.errorCodes.REENTRANT_NAVIGATION)}#m(e){throw this.#l.isReady()&&this.#r.get()===void 0?(this.#c.stop(),this.#l.sendStop()):this.#l.isStarting()&&this.#l.sendFail(void 0,void 0,e),e}#h(){this.navigate=O,this.navigateToDefault=O,this.navigateToNotFound=O,this.start=O,this.stop=O,this.usePlugin=O,this.subscribe=O,this.subscribeLeave=O,this.canNavigateTo=O}};function O(){throw new g.RouterError(t.errorCodes.ROUTER_DISPOSED)}function k(e){return{strictTrailingSlash:e.trailingSlash===`strict`,caseSensitive:e.caseSensitive,strictQueryParams:e.queryParamsMode===`strict`,urlParamsEncoding:e.urlParamsEncoding,queryParams:e.queryParams}}exports.Router=D;
1
+ const e=require("./channels/guard.js"),t=require("./constants.js"),n=require("./guards.js"),r=require("./helpers.js"),i=require("./internals.js"),a=require("./limits.js"),o=require("./namespaces/DependenciesNamespace/dependenciesStore.js"),s=require("./namespaces/OptionsNamespace/OptionsNamespace.js"),c=require("./pipeline/canonicalize.js"),l=require("./pipeline/buildURL.js"),u=require("./pipeline/materialize.js"),d=require("./namespaces/StateNamespace/StateNamespace.js"),f=require("./namespaces/PluginsNamespace/PluginsNamespace.js"),p=require("./namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.js"),m=require("./transitionPath.js"),h=require("./namespaces/RoutesNamespace/RoutesNamespace.js"),g=require("./RouterError.js"),_=require("./namespaces/NavigationNamespace/constants.js"),v=require("./namespaces/NavigationNamespace/NavigationNamespace.js"),y=require("./namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.js"),b=require("./routerFSM.js"),x=require("./namespaces/EventBusNamespace/EventBusNamespace.js"),S=require("./namespaces/RouterLifecycleNamespace/constants.js"),C=require("./utils/event-emitter/EventEmitter.js"),w=require("./utils/logger/RouterLogger.js"),T=require("./wiring/wireNamespaces.js"),E=Object.freeze({});var D=class D{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l;#u;constructor(r=[],c={},l={}){let{logger:u,...m}=c;u&&n.assertLoggerConfig(u);let g=new w.RouterLogger(u);this.#u=e=>{_.isExpectedRejection(e)||g.error(`router.start`,`Unexpected start error`,e)},s.OptionsNamespace.validateOptionsIsObject(c),n.guardDependencies(l),r.length>0&&n.guardRouteStructure(r),this.#e=new s.OptionsNamespace(m),this.#t=a.createLimits(m.limits),this.#n=o.createDependenciesStore(l),this.#r=new d.StateNamespace,this.#i=new h.RoutesNamespace(r,k(this.#e.get()),g),this.#a=new p.RouteLifecycleNamespace,this.#o=new f.PluginsNamespace,this.#s=new v.NavigationNamespace,this.#c=new y.RouterLifecycleNamespace;let S=b.createRouterFSM(),O=new C.EventEmitter({onListenerError:(e,t)=>{g.error(`Router`,`Error in listener for ${e}:`,t)},onListenerWarn:(e,t)=>{g.warn(`router.addEventListener`,`Event "${e}" has ${t} listeners — possible memory leak`)}});this.#l=new x.EventBusNamespace({routerFSM:S,emitter:O,abortController:e=>{this.#s.abortCurrentController(e)}});let A=new Map,j=i.createTernaryInterceptable(`forwardState`,(e,t,n)=>this.#i.forwardState(e,t,n),A);i.registerInternals(this,{logger:g,makeState:(e,t,n,r)=>this.#r.makeState(e,t,n,r),getMetaForState:e=>this.#i.getMetaForState(e),getQueryParams:e=>this.#i.getQueryParams(e),forwardState:((n,r,i)=>{let a=j(n,r,i);return e.assertChannelCorrect(`forwardState`,a.name,a.params,this.#i.getQueryParams(a.name),()=>a.name===n?"the `params` bag leaving the forwardState chain":`the \`params\` bag leaving the forwardState chain (forwarded here from "${n}")`),{name:a.name,params:a.params??t.EMPTY_PARAMS,search:a.search}}),buildStateResolved:(e,t)=>this.#i.buildStateResolved(e,t),port:()=>this.#i.getPort(),matchPath:(e,t)=>this.#i.matchPath(e,t),getOptions:()=>this.#e.get(),addEventListener:(e,t)=>this.#l.addEventListener(e,t),treeChanged:{emit:e=>{this.#l.emitTreeChanged(e)},subscribe:e=>this.#l.subscribeTreeChanged(e),listenerCount:()=>this.#l.treeChangedListenerCount(),isEmitting:()=>this.#l.isEmittingTreeChanged()},buildPath:i.createTernaryInterceptable(`buildPath`,(e,n,r)=>this.#i.buildPath(e,n??t.EMPTY_PARAMS,r,this.#e.get()),A),emitTransitionError:e=>{this.#l.sendFailSafe(void 0,this.#r.get(),e)},emitTransitionSuccess:(e,t,n)=>{this.#l.emitTransitionSuccess(e,t,n)},navigateToNotFound:e=>this.#s.navigateToNotFound(e),start:i.createInterceptable(`start`,e=>this.#c.start(e),A),navigateToState:(e,t)=>(this.#p(),D.#d(this.#s.navigateToState(e,t??E))),interceptors:A,setRootPath:e=>{this.#i.setRootPath(e)},getRootPath:()=>this.#i.getStore().rootPath,getTree:()=>this.#i.getStore().tree,isDisposed:()=>this.#l.isDisposed(),validator:null,dependenciesGetStore:()=>this.#n,getCloneState:()=>({options:{...this.#e.get()},dependencies:{...this.#n.dependencies},pluginFactories:this.#o.getAll(),loggerConfig:g.getConfig()}),routeGetStore:()=>this.#i.getStore(),getStateName:()=>this.#r.get()?.name,isTransitioning:()=>this.#l.isTransitioning(),clearState:()=>{this.#r.set(void 0)},setState:e=>{this.#r.set(e)},routerExtensions:[],contextClaimRecords:new Set,hydrationState:null}),T.wireNamespaces({router:this,options:this.#e,limits:this.#t,dependenciesStore:this.#n,state:this.#r,routes:this.#i,routeLifecycle:this.#a,plugins:this.#o,navigation:this.#s,lifecycle:this.#c,eventBus:this.#l}),this.isActiveRoute=this.isActiveRoute.bind(this),this.buildPath=this.buildPath.bind(this),this.getState=this.getState.bind(this),this.getPreviousState=this.getPreviousState.bind(this),this.areStatesEqual=this.areStatesEqual.bind(this),this.shouldUpdateNode=this.shouldUpdateNode.bind(this),this.isActive=this.isActive.bind(this),this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.dispose=this.dispose.bind(this),this.canNavigateTo=this.canNavigateTo.bind(this),this.usePlugin=this.usePlugin.bind(this),this.navigate=this.navigate.bind(this),this.navigateToDefault=this.navigateToDefault.bind(this),this.navigateToNotFound=this.navigateToNotFound.bind(this),this.subscribe=this.subscribe.bind(this),this.subscribeLeave=this.subscribeLeave.bind(this),this.isLeaveApproved=this.isLeaveApproved.bind(this);try{this.#i.flushPendingGuards()}catch(e){throw this.dispose(),e}}isActiveRoute(e,t,n,r,a){return i.getInternals(this).validator?.routes.validateIsActiveRouteArgs(e,t,r,a),i.getInternals(this).validator?.routes.validateRouteName(e,`isActiveRoute`),e===``?(i.getInternals(this).logger.warn(`real-router`,`isActiveRoute("") called with empty string. Root node is not considered a parent of any route.`),!1):this.#i.isActiveRoute(e,t,n,r,a)}buildPath(e,t,n){let a=i.getInternals(this);return a.validator?.routes.validateBuildPathArgs(e),a.validator?.navigation.validateParams(t,`buildPath`),a.buildPath(e,r.normalizeParams(t),n)}getState(){return this.#r.get()}getPreviousState(){return this.#r.getPrevious()}areStatesEqual(e,t,n=!0){return i.getInternals(this).validator?.state.validateAreStatesEqualArgs(e,t,n),this.#r.areStatesEqual(e,t,n)}shouldUpdateNode(e){return i.getInternals(this).validator?.routes.validateShouldUpdateNodeArgs(e),h.RoutesNamespace.shouldUpdateNode(e,e=>this.#i.getMetaForState(e))}isActive(){return this.#l.isActive()}start(e){let t=this.#f(e);return t.catch(this.#u),t}stop(){return this.#l.sendCancelIfPossible(this.#r.get()),!this.#l.isReady()&&!this.#l.isTransitioning()&&!this.#l.isStarting()?this:(this.#c.stop(),this.#l.sendStop(),this)}dispose(){if(this.#l.isDisposed())return;this.#l.sendCancelIfPossible(this.#r.get()),(this.#l.isReady()||this.#l.isTransitioning())&&(this.#c.stop(),this.#l.sendStop()),this.#l.sendDispose(),this.#l.clearAll(),this.#o.disposeAll();let e=i.getInternals(this);for(let t of e.routerExtensions)for(let e of t.keys)delete this[e];e.routerExtensions.length=0,e.contextClaimRecords.clear(),e.interceptors.clear(),this.#i.clearRoutes(),this.#a.clearAll(),this.#r.reset(),this.#n.dependencies=Object.create(null),this.#h()}canNavigateTo(n,r,a){let o=i.getInternals(this);if(o.validator?.routes.validateRouteName(n,`canNavigateTo`),o.validator?.navigation.validateParams(r,`canNavigateTo`),!this.#i.hasRoute(n)||e.findMisChanneledKey(r,this.#i.getQueryParams(n))!==void 0)return!1;let s=this.#i.getPort(),d;try{d=c.canonicalize(s,n,r??t.EMPTY_PARAMS,a)}catch(e){return o.logger.warn(`router.canNavigateTo`,`Resolving route "${n}" threw while answering the predicate; treating the route as unreachable.`,e),!1}let f;try{f=u.materialize(d,{path:l.buildURL(d,s),skipFreeze:!0})}catch{return!1}let p=this.#r.get(),{toDeactivate:h,toActivate:g}=m.getTransitionPath(f,p,e=>this.#i.getMetaForState(e));return this.#a.canNavigateTo(h,g,f,p)}usePlugin(...e){if(this.#l.isDisposed())throw new g.RouterError(t.errorCodes.ROUTER_DISPOSED);let n=e.filter(Boolean);if(n.length===0)return()=>{};let r=i.getInternals(this);r.validator?.plugins.validatePluginLimit(this.#o.count(),this.#t);for(let e of n)r.validator?.plugins.validateNoDuplicatePlugins(e,this.#o.getAll());return this.#o.use(...n)}subscribe(e){return x.EventBusNamespace.validateSubscribeListener(e),this.#l.subscribe(e)}subscribeLeave(e){return x.EventBusNamespace.validateSubscribeLeaveListener(e),this.#l.subscribeLeave(e)}isLeaveApproved(){return this.#l.isLeaveApproved()}navigate(e,n,r,a){this.#p();let o=i.getInternals(this),s,c,l,u;return typeof e==`object`&&e?(s=e.name,c=e.params,l=e.search,u=n??E):(s=e,c=n,l=r,u=a??E),i.throwOnMisChanneledKey(o,`navigate`,s,c),o.validator?.navigation.validateNavigateArgs(s),o.validator?.navigation.validateParams(c,`navigate`),o.validator?.navigation.validateNavigationOptions(u,`navigate`),D.#d(this.#s.navigate(s,c??t.EMPTY_PARAMS,l,u))}navigateToDefault(e){this.#p();let t=i.getInternals(this);t.validator?.navigation.validateNavigateToDefaultArgs(e);let n=e??E;return t.validator?.navigation.validateNavigationOptions(n,`navigateToDefault`),D.#d(this.#s.navigateToDefault(n))}navigateToNotFound(e){if(this.#p(),!this.#l.isActive())throw new g.RouterError(t.errorCodes.ROUTER_NOT_STARTED);if(e!==void 0&&typeof e!=`string`)throw TypeError(`[router.navigateToNotFound] path must be a string, got ${typeof e}`);if(e!==void 0)return this.#s.navigateToNotFound(e);let n=this.#r.get();if(n===void 0)throw new g.RouterError(t.errorCodes.ROUTER_NOT_STARTED,{message:`[router.navigateToNotFound] cannot derive the path before the start navigation commits — pass an explicit path`});return this.#s.navigateToNotFound(n.path)}static#d(e){return e instanceof Promise?e:Promise.resolve(e)}#f(e){if(!this.#l.canStart())return Promise.reject(S.CACHED_ALREADY_STARTED_ERROR);i.getInternals(this).validator?.navigation.validateStartArgs(e),this.#l.sendStart();let t;try{let n=i.getInternals(this).start(e);t=typeof n?.then==`function`?n:Promise.reject(TypeError("[router.start] a `start` interceptor returned without calling next(). Every start interceptor must return `next(path)`."))}catch(e){t=Promise.reject(e)}return t.catch(e=>this.#m(e))}#p(){if(this.#l.isProcessing()||this.#s.isPreparing())throw new g.RouterError(t.errorCodes.REENTRANT_NAVIGATION)}#m(e){throw this.#l.isReady()&&this.#r.get()===void 0?(this.#c.stop(),this.#l.sendStop()):this.#l.isStarting()&&this.#l.sendFail(void 0,void 0,e),e}#h(){this.navigate=O,this.navigateToDefault=O,this.navigateToNotFound=O,this.start=O,this.stop=O,this.usePlugin=O,this.subscribe=O,this.subscribeLeave=O,this.canNavigateTo=O}};function O(){throw new g.RouterError(t.errorCodes.ROUTER_DISPOSED)}function k(e){return{strictTrailingSlash:e.trailingSlash===`strict`,caseSensitive:e.caseSensitive,strictQueryParams:e.queryParamsMode===`strict`,urlParamsEncoding:e.urlParamsEncoding,queryParams:e.queryParams}}exports.Router=D;
2
2
  //# sourceMappingURL=Router.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Router.js","names":["#options","#limits","#dependenciesStore","#state","#routes","#routeLifecycle","#plugins","#navigation","#lifecycle","#eventBus","#onSuppressedStartError","RouterLogger","isExpectedRejection","OptionsNamespace","createLimits","createDependenciesStore","StateNamespace","RoutesNamespace","RouteLifecycleNamespace","PluginsNamespace","NavigationNamespace","RouterLifecycleNamespace","createRouterFSM","EventEmitter","EventBusNamespace","createTernaryInterceptable","EMPTY_PARAMS","createInterceptable","#assertNotReentrant","#asPromise","getInternals","normalizeParams","#runStart","#markDisposed","findMisChanneledKey","canonicalize","materialize","buildURL","getTransitionPath","RouterError","errorCodes","CACHED_ALREADY_STARTED_ERROR","#unwindFailedStart"],"sources":["../../src/Router.ts"],"sourcesContent":["// 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 { assertChannelCorrect, findMisChanneledKey } from \"./channels\";\nimport { EMPTY_PARAMS, errorCodes } from \"./constants\";\nimport {\n assertLoggerConfig,\n guardDependencies,\n guardRouteStructure,\n} from \"./guards\";\nimport { normalizeParams } from \"./helpers\";\nimport {\n createInterceptable,\n createTernaryInterceptable,\n getInternals,\n registerInternals,\n throwOnMisChanneledKey,\n} from \"./internals\";\nimport { createLimits } from \"./limits\";\nimport {\n EventBusNamespace,\n NavigationNamespace,\n OptionsNamespace,\n PluginsNamespace,\n RouteLifecycleNamespace,\n RouterLifecycleNamespace,\n RoutesNamespace,\n StateNamespace,\n createDependenciesStore,\n} from \"./namespaces\";\nimport { isExpectedRejection } from \"./namespaces/NavigationNamespace/constants\";\nimport { CACHED_ALREADY_STARTED_ERROR } from \"./namespaces/RouterLifecycleNamespace/constants\";\nimport { buildURL, canonicalize, materialize } from \"./pipeline\";\nimport { RouterError } from \"./RouterError\";\nimport { createRouterFSM } from \"./routerFSM\";\nimport { getTransitionPath } from \"./transitionPath\";\nimport { EventEmitter } from \"./utils/event-emitter\";\nimport { RouterLogger } from \"./utils/logger\";\nimport { wireNamespaces } from \"./wiring\";\n\nimport type { CreateMatcherOptions } from \"./engine\";\nimport type { RouterInternals } from \"./internals\";\nimport type { DependenciesStore } from \"./namespaces\";\nimport type {\n DefaultDependencies,\n LeaveFn,\n NavigationOptions,\n NavigationTarget,\n Options,\n Params,\n Router as RouterInterface,\n SearchParams,\n State,\n SubscribeFn,\n Unsubscribe,\n PluginFactory,\n Route,\n} from \"./types\";\nimport type { Limits, RouterEventMap } from \"./types/internal\";\n\nconst EMPTY_OPTS: Readonly<NavigationOptions> = Object.freeze({});\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<Dependencies>;\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 * Per-instance suppressor for fire-and-forget `start()`. It logs through THIS\n * router's logger (built in the constructor), so it cannot be static (#724).\n *\n * Only start, since Step 0: the navigate/navigateToState/navigateToDefault\n * suppressor moved to `NavigationNamespace`, which is where those promises are\n * created and therefore the only layer that knows which of them are already\n * pre-suppressed. Start keeps its own because it suppresses a promise the\n * FACADE builds (`internalStart.catch(#unwindFailedStart)`), and because the\n * #931 category split survives — start failures log under \"router.start\" (a\n * start interceptor throwing a plain Error after next() committed, #763, or a\n * cryptic path TypeError — neither a suppressed RouterError). Both sides still\n * classify through ONE shared policy, `isExpectedRejection`.\n */\n readonly #onSuppressedStartError: (error: unknown) => void;\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<Dependencies>> = {},\n dependencies: Dependencies = {} as Dependencies,\n ) {\n // Extract the logger config WITHOUT mutating the caller's `options` object\n // (#724). `routerOptions` is the logger-stripped view handed to the options\n // pipeline so `logger` never lands in the frozen router options.\n const { logger: loggerConfig, ...routerOptions } = options;\n\n if (loggerConfig) {\n assertLoggerConfig(loggerConfig);\n }\n\n // Per-router logger instance — replaces the former process-global singleton\n // whose configure() leaked across every router in the process, last\n // createRouter winning (#724). Stored on ctx (registerInternals below), so\n // the facade reads getInternals(this).logger; namespaces receive it via\n // their deps at wiring; plugins reach it through getPluginApi(router).logger.\n const logger = new RouterLogger(loggerConfig);\n\n // Per-instance fire-and-forget suppressor (see the field declaration): it\n // logs through THIS router's logger, so it is built here, not static.\n this.#onSuppressedStartError = (error: unknown): void => {\n if (isExpectedRejection(error)) {\n return;\n }\n\n logger.error(\"router.start\", \"Unexpected start error\", error);\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 logger,\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 // Shared per-listener error sink: EventEmitter reports synchronous listener\n // throws here, and EventBusNamespace.subscribe routes an async listener's\n // rejected Promise through the SAME sink (#944) — both land in one place.\n onListenerError: (eventName, error) => {\n logger.error(\"Router\", `Error in listener for ${eventName}:`, error);\n },\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 // 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 // THE single forwardState boundary (#1548/#1549). The interceptable resolves\n // the route (forwardTo) and runs the whole interceptor chain — a plugin\n // injecting params, a search-schema validation, etc. The outer layer then\n // CHECKS the channels once, keyed on the RESOLVED route's `?`-declaration.\n //\n // It used to REPAIR them instead (`separateChannels`, stage ②): a declared\n // query key left in the params bag was moved into the query channel behind\n // the producer's back. Three things were wrong with that. The producer kept\n // believing the bag it wrote was the one that shipped. A plugin could\n // inject past a validation that had already run — search-schema documented\n // exactly that leak, with a test named LEAKS. And the caller's own\n // mis-channelled key and a chain default's query half landed in DIFFERENT\n // channels, where no merge ranks them, so the default silently won (#1570).\n // Refusing is the whole fix: whoever names the route knows its declaration.\n //\n // `as unknown as` is required: the closure is non-generic, but\n // RouterInternals[\"forwardState\"] is declared generic `<P, S>`, which tsc\n // will not infer from a non-generic source (Sonar S4325 misclassifies this\n // as a redundant cast).\n const rawForwardState = createTernaryInterceptable(\n \"forwardState\",\n (name: string, params: Params, search?: SearchParams) =>\n this.#routes.forwardState(name, params, search),\n interceptorsMap,\n );\n\n const forwardState = ((\n name: string,\n params: Params,\n search?: SearchParams,\n ) => {\n const forwarded = rawForwardState(name, params, search);\n\n // The DECLARATION that matters is the RESOLVED route's — it owns the URL\n // that gets printed. When a chain resolved to a different route, say so:\n // a caller who wrote `navigate(\"src\", { lang })` looked at `src`'s config,\n // where `lang` is undeclared and legitimate, and needs to be told that the\n // hop landed somewhere that spells it `?lang`. Naming only the target\n // would read as a message about a route they never mentioned.\n assertChannelCorrect(\n \"forwardState\",\n forwarded.name,\n forwarded.params,\n this.#routes.getQueryParams(forwarded.name),\n () =>\n forwarded.name === name\n ? \"the `params` bag leaving the forwardState chain\"\n : `the \\`params\\` bag leaving the forwardState chain (forwarded here from \"${name}\")`,\n );\n\n return {\n name: forwarded.name,\n // The type says `params: P`, and across THIS boundary the type is a\n // contract, not a guarantee: `rawForwardState` is an interceptable, so\n // the value has passed through user code that can spread a partial\n // result. The net used to be reached by stage ②'s split (an all-query\n // bag left the path half undefined) and is now reached only by that\n // contract violation — still worth surviving rather than putting\n // `undefined` into `state.params`. Pinned by \"normalises a params bag an\n // interceptor dropped to `undefined`\" in forwardState.test.ts, which\n // fails if this is removed.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- see above: the declared type cannot model an interceptor's runtime return\n params: forwarded.params ?? EMPTY_PARAMS,\n search: forwarded.search,\n };\n }) as unknown as RouterInternals[\"forwardState\"];\n\n registerInternals(this, {\n logger,\n makeState: (name, params, search, path) =>\n this.#state.makeState(name, params, search, path),\n getMetaForState: (name) => this.#routes.getMetaForState(name),\n getQueryParams: (name) => this.#routes.getQueryParams(name),\n forwardState,\n buildStateResolved: (name, params) =>\n this.#routes.buildStateResolved(name, params),\n port: () => this.#routes.getPort(),\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: createTernaryInterceptable(\n \"buildPath\",\n (route: string, params?: Params, search?: SearchParams) =>\n this.#routes.buildPath(\n route,\n params ?? EMPTY_PARAMS,\n search,\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). Fire-and-forget safe like the\n // public facade methods — popstate handlers call it without awaiting —\n // but the safety now belongs to the namespace that creates the promise,\n // so this closure only owes callers the Promise shape.\n this.#assertNotReentrant();\n\n return Router.#asPromise(\n this.#navigation.navigateToState(state, navOpts ?? EMPTY_OPTS),\n );\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 // `logger` is a const in this constructor's scope (a RouterLogger class\n // instance), so getConfig() yields the resolved config a clone inherits\n // — frozen options don't carry `logger`, so cloneRouter reads it here.\n loggerConfig: logger.getConfig(),\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 search?: SearchParams,\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 getInternals(this).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 // Slot-shift (RFC-4 M2 / #1548): `search` is the explicit query channel at\n // position 3; `strictEquality` / `ignoreQueryParams` shift to 4 / 5.\n return this.#routes.isActiveRoute(\n name,\n params,\n search,\n strictEquality,\n ignoreQueryParams,\n );\n }\n\n buildPath(route: string, params?: Params, search?: SearchParams): string {\n const ctx = getInternals(this);\n\n ctx.validator?.routes.validateBuildPathArgs(route);\n ctx.validator?.navigation.validateParams(params, \"buildPath\");\n\n // `search` (RFC-4 M2 / #1548) is the explicit query channel; the matcher\n // builds the query string from it and the path from `params`, resolving a\n // colliding name (`/items/:id?id`). Omitted → the v1 single-bag path.\n return ctx.buildPath(route, normalizeParams(params), search);\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, (name) =>\n this.#routes.getMetaForState(name),\n );\n }\n\n // ============================================================================\n // Router Lifecycle\n // ============================================================================\n\n isActive(): boolean {\n return this.#eventBus.isActive();\n }\n\n /**\n * ONE fire-and-forget checkpoint for `start()`, deliberately — the same shape\n * `NavigationNamespace.#settle` gives the navigate family, and for the same\n * reason: a `.catch()` remembered at each `return` site is a thing that can be\n * forgotten, and a forgotten one is invisible until it leaks.\n *\n * It HAD been forgotten (#1605). The `ALREADY_STARTED` rejection left through\n * an early `return` above the suppressor, so a second, unawaited `start()`\n * raised an `unhandledRejection` — process-fatal under Node 22+'s default\n * `--unhandled-rejections=throw`, with a stack pointing at the cached error's\n * module constant rather than at the caller. Every return site now leaves\n * through `#runStart`, so no future early return can reopen it.\n */\n start(startPath: string): Promise<State> {\n const promiseState = this.#runStart(startPath);\n\n promiseState.catch(this.#onSuppressedStartError);\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, search?: SearchParams): 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 // Mirror EVERY way `navigate` refuses these same arguments, not only the\n // guard verdict (#1576). A declared query key handed in the PATH bag makes\n // `navigate` throw synchronously at the facade (channel guard P1, #1572), so\n // the route is unreachable with this input — exactly the situation invariant\n // canNavigateTo #5 already answers `false` to for an unbuildable path (#725).\n // Answering `true` here promised a navigation that throws on the click.\n //\n // The RAW caller bag, before `forwardState`: the same argument, the same\n // registry and the same name P1 reads, so the predicate cannot be stricter\n // OR laxer than the verb. The `/items/:id?id` collision is absent from\n // `queryNames` by construction (#843 / #1549), so it stays navigable in both.\n //\n // A `false` rather than a rethrow: a capability predicate answers, it never\n // throws (#725), and it runs on every `<Link>` render across six adapters —\n // which is exactly why P1 does not instrument the predicates (#1572).\n if (\n findMisChanneledKey(params, this.#routes.getQueryParams(name)) !==\n undefined\n ) {\n return false;\n }\n\n // Resolution runs USER code and must not escape as an exception (#1577):\n // a dynamic `forwardTo` callback, a plugin's `forwardState` interceptor, and\n // the caller's own bag (the merge walks it key by key, so an accessor-backed\n // key throws here — the channel guard itself does NOT, it catches its own\n // read) all sit on this one call. The\n // predicate is documented TOTAL — it answers, it never throws (INVARIANTS\n // canNavigateTo #5, #725) — and its sibling `isActiveRoute` has wrapped the\n // very same primitive since #1573 (`RoutesNamespace.ts:631-645`). Leaving\n // this one bare made the two render-path predicates disagree about what a\n // throwing resolution means.\n //\n // A separate `try` rather than widening the one below: that one is SILENT by\n // design (an unbuildable path is a normal \"unreachable with this input\"\n // answer, #725), while user code crashing is an operational fault that must\n // never vanish — the same split #959 draws for a throwing guard.\n // Stages ① + ③ + the mode gate, one pass through the pipeline (nav-pipeline\n // Phase 2, step 2-3). `canonicalize` reaches the same `forwardState` seam\n // this method used to call directly (`port.resolveForward` IS\n // `ctx.forwardState`), so the resolution, the interceptor zone and the\n // channel CHECK on the seam are all unchanged — what the pipeline\n // replaces is the hand-rolled composition that followed.\n // Read ONCE (#1589): this predicate reached for the port twice — here and\n // again for `buildURL` below — on every `<Link>` render. The port is one\n // object per router, created at wiring time, so the second read could only\n // ever return the same reference.\n const port = this.#routes.getPort();\n\n let canonical;\n\n try {\n canonical = canonicalize(\n port,\n name,\n // The singleton, not a fresh `{}` (#1589): this predicate runs on every\n // `<Link>` render too, and `normalizeParams` recognises `EMPTY_PARAMS` by\n // identity — a literal makes it walk and re-allocate instead.\n params ?? EMPTY_PARAMS,\n search,\n );\n } catch (error) {\n ctx.logger.warn(\n \"router.canNavigateTo\",\n `Resolving route \"${name}\" threw while answering the predicate; treating the route as unreachable.`,\n error,\n );\n\n return false;\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 // ⑤a then ⑤b. `buildURL` is usable HERE (unlike in `buildPath` itself,\n // where it would recurse through the interceptable `ctx.buildPath` that\n // wraps that very method): this point is not the one the port prints\n // through, so the URL is built by the pipeline and the state materialised\n // from the SAME canonical intent — `toState.search` and `toState.path`\n // cannot drift. `skipFreeze` mirrors the navigate guard phase, where\n // guards see an unfrozen, transition-less `toState`.\n toState = materialize(canonical, {\n path: buildURL(canonical, port),\n skipFreeze: true,\n });\n } catch {\n return false;\n }\n\n const fromState = this.#state.get();\n\n const { toDeactivate, toActivate } = getTransitionPath(\n toState,\n fromState,\n (routeName) => this.#routes.getMetaForState(routeName),\n );\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 target: NavigationTarget,\n options?: NavigationOptions,\n ): Promise<State>;\n navigate(\n routeName: string,\n routeParams?: Params,\n routeSearch?: SearchParams,\n options?: NavigationOptions,\n ): Promise<State>;\n navigate(\n nameOrTarget: string | NavigationTarget,\n paramsOrOptions?: Params | NavigationOptions,\n routeSearch?: SearchParams,\n options?: NavigationOptions,\n ): Promise<State> {\n this.#assertNotReentrant();\n\n const ctx = getInternals(this);\n\n // Two equal-standing forms (RFC-4 M2 / #1548): the descriptor\n // `navigate(target, opts)` (opts at position 2) and the positional\n // `navigate(name, params, search, opts)` (opts at position 4). The v1\n // `navigate(name, params, opts)` form is gone — its position-3 opts is now\n // the `search` slot; unpack whichever form the caller used into one path.\n let routeName: string;\n let routeParams: Params | undefined;\n let search: SearchParams | undefined;\n let opts: NavigationOptions;\n\n // The static type excludes null, but `navigate(null)` is a real runtime\n // misuse that must stay graceful (ROUTE_NOT_FOUND, not a crash on\n // `null.name`) — the null check routes it to the positional branch.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null guard for navigate(null)\n if (typeof nameOrTarget === \"object\" && nameOrTarget !== null) {\n routeName = nameOrTarget.name;\n routeParams = nameOrTarget.params;\n search = nameOrTarget.search;\n opts = (paramsOrOptions as NavigationOptions | undefined) ?? EMPTY_OPTS;\n } else {\n routeName = nameOrTarget;\n routeParams = paramsOrOptions as Params | undefined;\n search = routeSearch;\n opts = options ?? EMPTY_OPTS;\n }\n\n throwOnMisChanneledKey(ctx, \"navigate\", routeName, routeParams);\n\n ctx.validator?.navigation.validateNavigateArgs(routeName);\n ctx.validator?.navigation.validateParams(routeParams, \"navigate\");\n ctx.validator?.navigation.validateNavigationOptions(opts, \"navigate\");\n\n return Router.#asPromise(\n this.#navigation.navigate(\n routeName,\n routeParams ?? EMPTY_PARAMS,\n search,\n opts,\n ),\n );\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 return Router.#asPromise(this.#navigation.navigateToDefault(opts));\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 * Hands the namespace's result back as the `Promise<State>` the public API\n * owes, and does nothing else.\n *\n * A non-Promise means the navigation already settled synchronously — the\n * return TYPE says so, which is what retired `lastSyncResolved`. Suppression is\n * not the facade's business any more: the namespace attaches it where the\n * promise is created, the only layer that can tell a fresh rejection from one\n * of its own pre-suppressed singletons.\n *\n * The Promise wrap is not a new cost — the namespace used to allocate exactly\n * this one and return it.\n */\n static #asPromise(result: State | Promise<State>): Promise<State> {\n return result instanceof Promise ? result : Promise.resolve(result);\n }\n\n #runStart(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 return internalStart.catch((error: unknown) =>\n this.#unwindFailedStart(error),\n );\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<Dependencies extends DefaultDependencies>(\n options: Readonly<Options<Dependencies>>,\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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"}
1
+ {"version":3,"file":"Router.js","names":["#options","#limits","#dependenciesStore","#state","#routes","#routeLifecycle","#plugins","#navigation","#lifecycle","#eventBus","#onSuppressedStartError","RouterLogger","isExpectedRejection","OptionsNamespace","createLimits","createDependenciesStore","StateNamespace","RoutesNamespace","RouteLifecycleNamespace","PluginsNamespace","NavigationNamespace","RouterLifecycleNamespace","createRouterFSM","EventEmitter","EventBusNamespace","createTernaryInterceptable","EMPTY_PARAMS","createInterceptable","#assertNotReentrant","#asPromise","getInternals","normalizeParams","#runStart","#markDisposed","findMisChanneledKey","canonicalize","materialize","buildURL","getTransitionPath","RouterError","errorCodes","CACHED_ALREADY_STARTED_ERROR","#unwindFailedStart"],"sources":["../../src/Router.ts"],"sourcesContent":["// 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 { assertChannelCorrect, findMisChanneledKey } from \"./channels\";\nimport { EMPTY_PARAMS, errorCodes } from \"./constants\";\nimport {\n assertLoggerConfig,\n guardDependencies,\n guardRouteStructure,\n} from \"./guards\";\nimport { normalizeParams } from \"./helpers\";\nimport {\n createInterceptable,\n createTernaryInterceptable,\n getInternals,\n registerInternals,\n throwOnMisChanneledKey,\n} from \"./internals\";\nimport { createLimits } from \"./limits\";\nimport {\n EventBusNamespace,\n NavigationNamespace,\n OptionsNamespace,\n PluginsNamespace,\n RouteLifecycleNamespace,\n RouterLifecycleNamespace,\n RoutesNamespace,\n StateNamespace,\n createDependenciesStore,\n} from \"./namespaces\";\nimport { isExpectedRejection } from \"./namespaces/NavigationNamespace/constants\";\nimport { CACHED_ALREADY_STARTED_ERROR } from \"./namespaces/RouterLifecycleNamespace/constants\";\nimport { buildURL, canonicalize, materialize } from \"./pipeline\";\nimport { RouterError } from \"./RouterError\";\nimport { createRouterFSM } from \"./routerFSM\";\nimport { getTransitionPath } from \"./transitionPath\";\nimport { EventEmitter } from \"./utils/event-emitter\";\nimport { RouterLogger } from \"./utils/logger\";\nimport { wireNamespaces } from \"./wiring\";\n\nimport type { CreateMatcherOptions } from \"./engine\";\nimport type { RouterInternals } from \"./internals\";\nimport type { DependenciesStore } from \"./namespaces\";\nimport type {\n DefaultDependencies,\n LeaveFn,\n NavigationOptions,\n NavigationTarget,\n Options,\n Params,\n Router as RouterInterface,\n SearchParams,\n State,\n SubscribeFn,\n Unsubscribe,\n PluginFactory,\n Route,\n} from \"./types\";\nimport type { Limits, RouterEventMap } from \"./types/internal\";\n\nconst EMPTY_OPTS: Readonly<NavigationOptions> = Object.freeze({});\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<Dependencies>;\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 * Per-instance suppressor for fire-and-forget `start()`. It logs through THIS\n * router's logger (built in the constructor), so it cannot be static (#724).\n *\n * Only start, since Step 0: the navigate/navigateToState/navigateToDefault\n * suppressor moved to `NavigationNamespace`, which is where those promises are\n * created and therefore the only layer that knows which of them are already\n * pre-suppressed. Start keeps its own because it suppresses a promise the\n * FACADE builds (`internalStart.catch(#unwindFailedStart)`), and because the\n * #931 category split survives — start failures log under \"router.start\" (a\n * start interceptor throwing a plain Error after next() committed, #763, or a\n * cryptic path TypeError — neither a suppressed RouterError). Both sides still\n * classify through ONE shared policy, `isExpectedRejection`.\n */\n readonly #onSuppressedStartError: (error: unknown) => void;\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<Dependencies>> = {},\n dependencies: Dependencies = {} as Dependencies,\n ) {\n // Extract the logger config WITHOUT mutating the caller's `options` object\n // (#724). `routerOptions` is the logger-stripped view handed to the options\n // pipeline so `logger` never lands in the frozen router options.\n const { logger: loggerConfig, ...routerOptions } = options;\n\n if (loggerConfig) {\n assertLoggerConfig(loggerConfig);\n }\n\n // Per-router logger instance — replaces the former process-global singleton\n // whose configure() leaked across every router in the process, last\n // createRouter winning (#724). Stored on ctx (registerInternals below), so\n // the facade reads getInternals(this).logger; namespaces receive it via\n // their deps at wiring; plugins reach it through getPluginApi(router).logger.\n const logger = new RouterLogger(loggerConfig);\n\n // Per-instance fire-and-forget suppressor (see the field declaration): it\n // logs through THIS router's logger, so it is built here, not static.\n this.#onSuppressedStartError = (error: unknown): void => {\n if (isExpectedRejection(error)) {\n return;\n }\n\n logger.error(\"router.start\", \"Unexpected start error\", error);\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 logger,\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 // Shared per-listener error sink: EventEmitter reports synchronous listener\n // throws here, and EventBusNamespace.subscribe routes an async listener's\n // rejected Promise through the SAME sink (#944) — both land in one place.\n onListenerError: (eventName, error) => {\n logger.error(\"Router\", `Error in listener for ${eventName}:`, error);\n },\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 // 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 // THE single forwardState boundary (#1548/#1549). The interceptable resolves\n // the route (forwardTo) and runs the whole interceptor chain — a plugin\n // injecting params, a search-schema validation, etc. The outer layer then\n // CHECKS the channels once, keyed on the RESOLVED route's `?`-declaration.\n //\n // It used to REPAIR them instead (`separateChannels`, stage ②): a declared\n // query key left in the params bag was moved into the query channel behind\n // the producer's back. Three things were wrong with that. The producer kept\n // believing the bag it wrote was the one that shipped. A plugin could\n // inject past a validation that had already run — search-schema documented\n // exactly that leak, with a test named LEAKS. And the caller's own\n // mis-channelled key and a chain default's query half landed in DIFFERENT\n // channels, where no merge ranks them, so the default silently won (#1570).\n // Refusing is the whole fix: whoever names the route knows its declaration.\n //\n // `as unknown as` is required: the closure is non-generic, but\n // RouterInternals[\"forwardState\"] is declared generic `<P, S>`, which tsc\n // will not infer from a non-generic source (Sonar S4325 misclassifies this\n // as a redundant cast).\n const rawForwardState = createTernaryInterceptable(\n \"forwardState\",\n (name: string, params: Params, search?: SearchParams) =>\n this.#routes.forwardState(name, params, search),\n interceptorsMap,\n );\n\n const forwardState = ((\n name: string,\n params: Params,\n search?: SearchParams,\n ) => {\n const forwarded = rawForwardState(name, params, search);\n\n // The DECLARATION that matters is the RESOLVED route's — it owns the URL\n // that gets printed. When a chain resolved to a different route, say so:\n // a caller who wrote `navigate(\"src\", { lang })` looked at `src`'s config,\n // where `lang` is undeclared and legitimate, and needs to be told that the\n // hop landed somewhere that spells it `?lang`. Naming only the target\n // would read as a message about a route they never mentioned.\n assertChannelCorrect(\n \"forwardState\",\n forwarded.name,\n forwarded.params,\n this.#routes.getQueryParams(forwarded.name),\n () =>\n forwarded.name === name\n ? \"the `params` bag leaving the forwardState chain\"\n : `the \\`params\\` bag leaving the forwardState chain (forwarded here from \"${name}\")`,\n );\n\n return {\n name: forwarded.name,\n // The type says `params: P`, and across THIS boundary the type is a\n // contract, not a guarantee: `rawForwardState` is an interceptable, so\n // the value has passed through user code that can spread a partial\n // result. The net used to be reached by stage ②'s split (an all-query\n // bag left the path half undefined) and is now reached only by that\n // contract violation — still worth surviving rather than putting\n // `undefined` into `state.params`. Pinned by \"normalises a params bag an\n // interceptor dropped to `undefined`\" in forwardState.test.ts, which\n // fails if this is removed.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- see above: the declared type cannot model an interceptor's runtime return\n params: forwarded.params ?? EMPTY_PARAMS,\n search: forwarded.search,\n };\n }) as unknown as RouterInternals[\"forwardState\"];\n\n registerInternals(this, {\n logger,\n makeState: (name, params, search, path) =>\n this.#state.makeState(name, params, search, path),\n getMetaForState: (name) => this.#routes.getMetaForState(name),\n getQueryParams: (name) => this.#routes.getQueryParams(name),\n forwardState,\n buildStateResolved: (name, params) =>\n this.#routes.buildStateResolved(name, params),\n port: () => this.#routes.getPort(),\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: createTernaryInterceptable(\n \"buildPath\",\n (route: string, params?: Params, search?: SearchParams) =>\n this.#routes.buildPath(\n route,\n params ?? EMPTY_PARAMS,\n search,\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). Fire-and-forget safe like the\n // public facade methods — popstate handlers call it without awaiting —\n // but the safety now belongs to the namespace that creates the promise,\n // so this closure only owes callers the Promise shape.\n this.#assertNotReentrant();\n\n return Router.#asPromise(\n this.#navigation.navigateToState(state, navOpts ?? EMPTY_OPTS),\n );\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 // `logger` is a const in this constructor's scope (a RouterLogger class\n // instance), so getConfig() yields the resolved config a clone inherits\n // — frozen options don't carry `logger`, so cloneRouter reads it here.\n loggerConfig: logger.getConfig(),\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 search?: SearchParams,\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 getInternals(this).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 // Slot-shift (RFC-4 M2 / #1548): `search` is the explicit query channel at\n // position 3; `strictEquality` / `ignoreQueryParams` shift to 4 / 5.\n return this.#routes.isActiveRoute(\n name,\n params,\n search,\n strictEquality,\n ignoreQueryParams,\n );\n }\n\n buildPath(route: string, params?: Params, search?: SearchParams): string {\n const ctx = getInternals(this);\n\n ctx.validator?.routes.validateBuildPathArgs(route);\n ctx.validator?.navigation.validateParams(params, \"buildPath\");\n\n // `search` (RFC-4 M2 / #1548) is the explicit query channel; the matcher\n // builds the query string from it and the path from `params`, resolving a\n // colliding name (`/items/:id?id`). Omitted → the v1 single-bag path.\n return ctx.buildPath(route, normalizeParams(params), search);\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, (name) =>\n this.#routes.getMetaForState(name),\n );\n }\n\n // ============================================================================\n // Router Lifecycle\n // ============================================================================\n\n isActive(): boolean {\n return this.#eventBus.isActive();\n }\n\n /**\n * ONE fire-and-forget checkpoint for `start()`, deliberately — the same shape\n * `NavigationNamespace.#settle` gives the navigate family, and for the same\n * reason: a `.catch()` remembered at each `return` site is a thing that can be\n * forgotten, and a forgotten one is invisible until it leaks.\n *\n * It HAD been forgotten (#1605). The `ALREADY_STARTED` rejection left through\n * an early `return` above the suppressor, so a second, unawaited `start()`\n * raised an `unhandledRejection` — process-fatal under Node 22+'s default\n * `--unhandled-rejections=throw`, with a stack pointing at the cached error's\n * module constant rather than at the caller. Every return site now leaves\n * through `#runStart`, so no future early return can reopen it.\n */\n start(startPath: string): Promise<State> {\n const promiseState = this.#runStart(startPath);\n\n promiseState.catch(this.#onSuppressedStartError);\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, search?: SearchParams): 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 // Mirror EVERY way `navigate` refuses these same arguments, not only the\n // guard verdict (#1576). A declared query key handed in the PATH bag makes\n // `navigate` throw synchronously at the facade (channel guard P1, #1572), so\n // the route is unreachable with this input — exactly the situation invariant\n // canNavigateTo #5 already answers `false` to for an unbuildable path (#725).\n // Answering `true` here promised a navigation that throws on the click.\n //\n // The RAW caller bag, before `forwardState`: the same argument, the same\n // registry and the same name P1 reads, so the predicate cannot be stricter\n // OR laxer than the verb. The `/items/:id?id` collision is absent from\n // `queryNames` by construction (#843 / #1549), so it stays navigable in both.\n //\n // A `false` rather than a rethrow: a capability predicate answers, it never\n // throws (#725), and it runs on every `<Link>` render across six adapters —\n // which is exactly why P1 does not instrument the predicates (#1572).\n if (\n findMisChanneledKey(params, this.#routes.getQueryParams(name)) !==\n undefined\n ) {\n return false;\n }\n\n // Resolution runs USER code and must not escape as an exception (#1577):\n // a dynamic `forwardTo` callback, a plugin's `forwardState` interceptor, and\n // the caller's own bag (the merge walks it key by key, so an accessor-backed\n // key throws here — the channel guard itself does NOT, it catches its own\n // read) all sit on this one call. The\n // predicate is documented TOTAL — it answers, it never throws (INVARIANTS\n // canNavigateTo #5, #725) — and its sibling `isActiveRoute` has wrapped the\n // very same primitive since #1573 (`RoutesNamespace.ts:631-645`). Leaving\n // this one bare made the two render-path predicates disagree about what a\n // throwing resolution means.\n //\n // A separate `try` rather than widening the one below: that one is SILENT by\n // design (an unbuildable path is a normal \"unreachable with this input\"\n // answer, #725), while user code crashing is an operational fault that must\n // never vanish — the same split #959 draws for a throwing guard.\n // Stages ① + ③ + the mode gate, one pass through the pipeline (nav-pipeline\n // Phase 2, step 2-3). `canonicalize` reaches the same `forwardState` seam\n // this method used to call directly (`port.resolveForward` IS\n // `ctx.forwardState`), so the resolution, the interceptor zone and the\n // channel CHECK on the seam are all unchanged — what the pipeline\n // replaces is the hand-rolled composition that followed.\n // Read ONCE (#1589): this predicate reached for the port twice — here and\n // again for `buildURL` below — on every `<Link>` render. The port is one\n // object per router, created at wiring time, so the second read could only\n // ever return the same reference.\n const port = this.#routes.getPort();\n\n let canonical;\n\n try {\n canonical = canonicalize(\n port,\n name,\n // The singleton, not a fresh `{}` (#1589): this predicate runs on every\n // `<Link>` render too, and `normalizeParams` recognises `EMPTY_PARAMS` by\n // identity — a literal makes it walk and re-allocate instead.\n params ?? EMPTY_PARAMS,\n search,\n );\n } catch (error) {\n ctx.logger.warn(\n \"router.canNavigateTo\",\n `Resolving route \"${name}\" threw while answering the predicate; treating the route as unreachable.`,\n error,\n );\n\n return false;\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 // ⑤a then ⑤b. `buildURL` is usable HERE (unlike in `buildPath` itself,\n // where it would recurse through the interceptable `ctx.buildPath` that\n // wraps that very method): this point is not the one the port prints\n // through, so the URL is built by the pipeline and the state materialised\n // from the SAME canonical intent — `toState.search` and `toState.path`\n // cannot drift. `skipFreeze` mirrors the navigate guard phase, where\n // guards see an unfrozen, transition-less `toState`.\n toState = materialize(canonical, {\n path: buildURL(canonical, port),\n skipFreeze: true,\n });\n } catch {\n return false;\n }\n\n const fromState = this.#state.get();\n\n const { toDeactivate, toActivate } = getTransitionPath(\n toState,\n fromState,\n (routeName) => this.#routes.getMetaForState(routeName),\n );\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 target: NavigationTarget,\n options?: NavigationOptions,\n ): Promise<State>;\n navigate(\n routeName: string,\n routeParams?: Params,\n routeSearch?: SearchParams,\n options?: NavigationOptions,\n ): Promise<State>;\n navigate(\n nameOrTarget: string | NavigationTarget,\n paramsOrOptions?: Params | NavigationOptions,\n routeSearch?: SearchParams,\n options?: NavigationOptions,\n ): Promise<State> {\n this.#assertNotReentrant();\n\n const ctx = getInternals(this);\n\n // Two equal-standing forms (RFC-4 M2 / #1548): the descriptor\n // `navigate(target, opts)` (opts at position 2) and the positional\n // `navigate(name, params, search, opts)` (opts at position 4). The v1\n // `navigate(name, params, opts)` form is gone — its position-3 opts is now\n // the `search` slot; unpack whichever form the caller used into one path.\n let routeName: string;\n let routeParams: Params | undefined;\n let search: SearchParams | undefined;\n let opts: NavigationOptions;\n\n // The static type excludes null, but `navigate(null)` is a real runtime\n // misuse that must stay graceful (ROUTE_NOT_FOUND, not a crash on\n // `null.name`) — the null check routes it to the positional branch.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime null guard for navigate(null)\n if (typeof nameOrTarget === \"object\" && nameOrTarget !== null) {\n routeName = nameOrTarget.name;\n routeParams = nameOrTarget.params;\n search = nameOrTarget.search;\n opts = (paramsOrOptions as NavigationOptions | undefined) ?? EMPTY_OPTS;\n } else {\n routeName = nameOrTarget;\n routeParams = paramsOrOptions as Params | undefined;\n search = routeSearch;\n opts = options ?? EMPTY_OPTS;\n }\n\n throwOnMisChanneledKey(ctx, \"navigate\", routeName, routeParams);\n\n ctx.validator?.navigation.validateNavigateArgs(routeName);\n ctx.validator?.navigation.validateParams(routeParams, \"navigate\");\n ctx.validator?.navigation.validateNavigationOptions(opts, \"navigate\");\n\n return Router.#asPromise(\n this.#navigation.navigate(\n routeName,\n routeParams ?? EMPTY_PARAMS,\n search,\n opts,\n ),\n );\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 return Router.#asPromise(this.#navigation.navigateToDefault(opts));\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 * Hands the namespace's result back as the `Promise<State>` the public API\n * owes, and does nothing else.\n *\n * A non-Promise means the navigation already settled synchronously — the\n * return TYPE says so, which is what retired `lastSyncResolved`. Suppression is\n * not the facade's business any more: the namespace attaches it where the\n * promise is created, the only layer that can tell a fresh rejection from one\n * of its own pre-suppressed singletons.\n *\n * The Promise wrap is not a new cost — the namespace used to allocate exactly\n * this one and return it.\n */\n static #asPromise(result: State | Promise<State>): Promise<State> {\n return result instanceof Promise ? result : Promise.resolve(result);\n }\n\n #runStart(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 return internalStart.catch((error: unknown) =>\n this.#unwindFailedStart(error),\n );\n }\n\n /**\n * Rejects a synchronous reentrant navigation — `navigate` /\n * `navigateToDefault` / `navigateToState` / `navigateToNotFound` called from\n * inside a navigation core has not finished with. Throws synchronously: inside\n * a listener the emit's `onListenerError` isolation surfaces it (visible,\n * non-fatal); a DEFERRED (async / microtask) navigate runs after the window\n * closes and is allowed. Always-on core invariant guard (not validator-gated).\n *\n * TWO windows, because application code runs in two places core does not\n * control, on opposite sides of the announce:\n *\n * - **Dispatch** (`isProcessing`) — a transition-event listener, mid-emit\n * (RFC navigation-cancellation-unification §4).\n * - **Pre-start** (`isPreparing`, #1610) — a `forwardState` / `buildPath`\n * interceptor or a route codec, BEFORE the first emit. The dispatch depth\n * cannot see it: there has been no emit yet, which is exactly how a nested\n * `navigate()` used to run to completion here, commit a phantom\n * `TRANSITION_SUCCESS`, and shift the outer transition's `fromState`.\n *\n * A guard is deliberately NOT either of them: it runs after the announce, so\n * the classic guard-redirect (`navigate(...)` then `return false`) stays a\n * plain supersede.\n */\n #assertNotReentrant(): void {\n if (this.#eventBus.isProcessing() || this.#navigation.isPreparing()) {\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<Dependencies extends DefaultDependencies>(\n options: Readonly<Options<Dependencies>>,\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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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../channels/guard.js"),t=require("../../constants.js"),n=require("../../RouterError.js"),r=require("./constants.js"),i=require("./InFlightNavigation.js"),a=require("./transition/executeNavigation.js"),o=require("./transition/navigateToNotFound.js");var s=class{#e;#t;#n=new i.InFlightNavigation;setDependencies(e){this.#e=e,this.#t=t=>{r.isExpectedRejection(t)||e.logger.error(`router.navigate`,`Unexpected navigation error`,t)}}navigate(e,t,n,r){return this.#r(this.#i(e,t,n,r))}navigateToState(e,t){return this.#r(this.#a(e,t))}navigateToDefault(e){return this.#r(this.#o(e))}navigateToNotFound(e){return o.navigateToNotFound(this.#e,e)}abortCurrentController(e){this.#n.abort(e)}#r(e){return e instanceof Promise&&!r.PRE_SUPPRESSED.has(e)&&e.catch(this.#t),e}#i(e,t,n,i){let o=this.#e;if(!o.canNavigate())return r.CACHED_NOT_STARTED_REJECTION;let s;try{s=o.buildNavigateState(e,t,n)}catch(e){return Promise.reject(e)}return s?a.executeNavigation(this.#e,this.#n,s,i):(o.emitTransitionError(void 0,o.getState(),r.CACHED_ROUTE_NOT_FOUND_ERROR),r.CACHED_ROUTE_NOT_FOUND_REJECTION)}#a(i,o){let s=this.#e;if(!s.canNavigate())return r.CACHED_NOT_STARTED_REJECTION;if(i.name!==t.constants.UNKNOWN_ROUTE&&!s.hasRoute(i.name)){let e=new n.RouterError(t.errorCodes.ROUTE_NOT_FOUND,{routeName:i.name});return s.emitTransitionError(void 0,s.getState(),e),Promise.reject(e)}let c=e.findMisChanneledKey(i.params,s.getQueryParams(i.name));if(c!==void 0){let r=new n.RouterError(t.errorCodes.WRONG_CHANNEL,{routeName:i.name,message:`[router.navigateToState] ${e.misChanneledKeyMessage(i.name,c,"`state.params`")}`});return s.emitTransitionError(void 0,s.getState(),r),Promise.reject(r)}let l={name:i.name,params:i.params,search:i.search,path:i.path,context:{...i.context}};return a.executeNavigation(this.#e,this.#n,l,o)}#o(e){let r=this.#e;if(!r.getOptions().defaultRoute)return Promise.reject(new n.RouterError(t.errorCodes.ROUTE_NOT_FOUND,{routeName:`defaultRoute not configured`}));let i,a,o;try{({route:i,params:a,search:o}=r.resolveDefault())}catch(e){return Promise.reject(e)}return i?this.#i(i,a,o,e):Promise.reject(new n.RouterError(t.errorCodes.ROUTE_NOT_FOUND,{routeName:`defaultRoute resolved to empty`}))}};exports.NavigationNamespace=s;
1
+ const e=require("../../channels/guard.js"),t=require("../../constants.js"),n=require("../../RouterError.js"),r=require("./constants.js"),i=require("./InFlightNavigation.js"),a=require("./transition/executeNavigation.js"),o=require("./transition/navigateToNotFound.js");var s=class{#e;#t;#n=new i.InFlightNavigation;#r=0;setDependencies(e){this.#e=e,this.#t=t=>{r.isExpectedRejection(t)||e.logger.error(`router.navigate`,`Unexpected navigation error`,t)}}navigate(e,t,n,r){return this.#i(this.#a(e,t,n,r))}navigateToState(e,t){return this.#i(this.#o(e,t))}navigateToDefault(e){return this.#i(this.#s(e))}navigateToNotFound(e){return o.navigateToNotFound(this.#e,e)}abortCurrentController(e){this.#n.abort(e)}isPreparing(){return this.#r>0}#i(e){return e instanceof Promise&&!r.PRE_SUPPRESSED.has(e)&&e.catch(this.#t),e}#a(e,t,n,i){let o=this.#e;if(!o.canNavigate())return r.CACHED_NOT_STARTED_REJECTION;let s;this.#r++;try{s=o.buildNavigateState(e,t,n)}catch(e){return Promise.reject(e)}finally{this.#r--}return s?a.executeNavigation(this.#e,this.#n,s,i):(o.emitTransitionError(void 0,o.getState(),r.CACHED_ROUTE_NOT_FOUND_ERROR),r.CACHED_ROUTE_NOT_FOUND_REJECTION)}#o(i,o){let s=this.#e;if(!s.canNavigate())return r.CACHED_NOT_STARTED_REJECTION;if(i.name!==t.constants.UNKNOWN_ROUTE&&!s.hasRoute(i.name)){let e=new n.RouterError(t.errorCodes.ROUTE_NOT_FOUND,{routeName:i.name});return s.emitTransitionError(void 0,s.getState(),e),Promise.reject(e)}let c=e.findMisChanneledKey(i.params,s.getQueryParams(i.name));if(c!==void 0){let r=new n.RouterError(t.errorCodes.WRONG_CHANNEL,{routeName:i.name,message:`[router.navigateToState] ${e.misChanneledKeyMessage(i.name,c,"`state.params`")}`});return s.emitTransitionError(void 0,s.getState(),r),Promise.reject(r)}let l={name:i.name,params:i.params,search:i.search,path:i.path,context:{...i.context}};return a.executeNavigation(this.#e,this.#n,l,o)}#s(e){let r=this.#e;if(!r.getOptions().defaultRoute)return Promise.reject(new n.RouterError(t.errorCodes.ROUTE_NOT_FOUND,{routeName:`defaultRoute not configured`}));let i,a,o;this.#r++;try{({route:i,params:a,search:o}=r.resolveDefault())}catch(e){return Promise.reject(e)}finally{this.#r--}return i?this.#a(i,a,o,e):Promise.reject(new n.RouterError(t.errorCodes.ROUTE_NOT_FOUND,{routeName:`defaultRoute resolved to empty`}))}};exports.NavigationNamespace=s;
2
2
  //# sourceMappingURL=NavigationNamespace.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NavigationNamespace.js","names":["#inFlight","InFlightNavigation","#deps","#onSuppressed","isExpectedRejection","#settle","#navigate","#navigateToState","#navigateToDefault","navigateToNotFound","PRE_SUPPRESSED","CACHED_NOT_STARTED_REJECTION","CACHED_ROUTE_NOT_FOUND_ERROR","CACHED_ROUTE_NOT_FOUND_REJECTION","executeNavigation","constants","RouterError","errorCodes","findMisChanneledKey","misChanneledKeyMessage"],"sources":["../../../../src/namespaces/NavigationNamespace/NavigationNamespace.ts"],"sourcesContent":["import {\n CACHED_NOT_STARTED_REJECTION,\n CACHED_ROUTE_NOT_FOUND_ERROR,\n CACHED_ROUTE_NOT_FOUND_REJECTION,\n isExpectedRejection,\n PRE_SUPPRESSED,\n} from \"./constants\";\nimport { InFlightNavigation } from \"./InFlightNavigation\";\nimport { executeNavigation } from \"./transition/executeNavigation\";\nimport { navigateToNotFound } from \"./transition/navigateToNotFound\";\nimport { findMisChanneledKey, misChanneledKeyMessage } from \"../../channels\";\nimport { errorCodes, constants } from \"../../constants\";\nimport { RouterError } from \"../../RouterError\";\n\nimport type { NavigationDependencies } from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n SearchParams,\n State,\n} from \"../../types\";\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 #deps!: NavigationDependencies;\n #onSuppressed!: (error: unknown) => void;\n // The controller + supersession token of the navigation in flight, owned as\n // one thing (#1607). Built here, one per router — never per navigation.\n readonly #inFlight = new InFlightNavigation();\n\n // =========================================================================\n // Dependency injection\n // =========================================================================\n\n setDependencies(deps: NavigationDependencies): void {\n this.#deps = deps;\n // Built once here rather than per call: the closure needs THIS router's\n // logger, so it cannot be static, and `setDependencies` is a pure\n // assignment that runs exactly once at wiring (#1331).\n this.#onSuppressed = (error: unknown): void => {\n if (isExpectedRejection(error)) {\n return;\n }\n\n deps.logger.error(\n \"router.navigate\",\n \"Unexpected navigation error\",\n error,\n );\n };\n }\n\n // =========================================================================\n // Instance methods\n // =========================================================================\n\n navigate(\n name: string,\n params: Params,\n search: SearchParams | undefined,\n opts: NavigationOptions,\n ): State | Promise<State> {\n return this.#settle(this.#navigate(name, params, search, 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(\n state: State,\n opts: NavigationOptions,\n ): State | Promise<State> {\n return this.#settle(this.#navigateToState(state, opts));\n }\n\n navigateToDefault(opts: NavigationOptions): State | Promise<State> {\n return this.#settle(this.#navigateToDefault(opts));\n }\n\n navigateToNotFound(path: string): State {\n return navigateToNotFound(this.#deps, path);\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.#inFlight.abort(reason);\n }\n\n /**\n * The producer's own fire-and-forget guarantee (#721): whatever leaves a\n * public method here is safe to drop on the floor.\n *\n * ONE checkpoint per public method, deliberately — not a `.catch()` at each of\n * the six return sites. A forgotten site is invisible until it leaks, which is\n * the bug this replaces; a single choke point cannot be forgotten. And the\n * discriminator is the returned VALUE's identity, not a flag the facade reads\n * afterwards: `PRE_SUPPRESSED` promises already carry a module-load handler, so\n * re-suppressing them buys nothing and costs a derived promise.\n *\n * A synchronously-returned `State` needs nothing at all — there is no rejection\n * to suppress, which is precisely what makes `lastSyncResolved` unnecessary:\n * the TYPE now carries what the flag used to announce.\n */\n #settle(result: State | Promise<State>): State | Promise<State> {\n if (result instanceof Promise && !PRE_SUPPRESSED.has(result)) {\n result.catch(this.#onSuppressed);\n }\n\n return result;\n }\n\n /**\n * `navigate`'s body, minus the fire-and-forget checkpoint.\n *\n * Split out because `navigateToDefault` delegates HERE, not to the public\n * method: routing it through `navigate` would run `#settle` twice per default\n * navigation and attach a second, pointless `.catch()` — an extra derived\n * promise on a path that has none today.\n */\n #navigate(\n name: string,\n params: Params,\n search: SearchParams | undefined,\n opts: NavigationOptions,\n ): State | Promise<State> {\n const deps = this.#deps;\n\n // Fast-path sync rejections: cached error + cached Promise.reject.\n // No allocations, no throw/catch overhead; `#settle` recognises the\n // singleton by identity and skips its `.catch()`.\n if (!deps.canNavigate()) {\n return CACHED_NOT_STARTED_REJECTION;\n }\n\n let toState: State | undefined;\n\n try {\n toState = deps.buildNavigateState(name, params, search);\n } catch (error) {\n // No `v8 ignore` here any more: the comment that used to sit above this\n // line claimed the path was \"reachable only via validator-driven throws\n // … covered in @real-router/validation-plugin's suite, not in core\", and\n // that stopped being true when the always-on channel guard (#1572) began\n // throwing from core's own `buildNavigateState`. Measured: with the ignore\n // removed, core's suite still reports 100% — it was masking a live region.\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\n return CACHED_ROUTE_NOT_FOUND_REJECTION;\n }\n\n return executeNavigation(this.#deps, this.#inFlight, toState, opts);\n }\n\n #navigateToState(\n state: State,\n opts: NavigationOptions,\n ): State | Promise<State> {\n const deps = this.#deps;\n\n if (!deps.canNavigate()) {\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 // A FRESH reject (it carries `routeName`), so it is deliberately NOT in\n // `PRE_SUPPRESSED` and `#settle` will attach its `.catch()`. Adding it to\n // that set — the modern shape of the mistake that caused #721 — would skip\n // suppression on a promise nobody else handles and leak it.\n return Promise.reject(err);\n }\n\n // Channel guard, position P3 (#1572). `navigateToState` is the ONE producer\n // that takes a ready-made `State` instead of a `params` argument, so the\n // predicate reads `state.params ∩ queryNames(state.name)`. What it commits\n // becomes `getState()`, so a pre-M2 layout would be silent corruption: the\n // key sits in `state.params` and never reaches `state.path`.\n //\n // Costs nothing on healthy flows — a state produced by core (`matchPath`,\n // `makeState`) is channel-correct by construction, so the predicate is\n // empty on every popstate / memory-restore / SSR-hydration commit. `start()`\n // commits THROUGH here (`RouterLifecycleNamespace`), which is why the guard\n // lives in the namespace rather than on the plugin-API door.\n //\n // Rejects rather than throwing, mirroring the ROUTE_NOT_FOUND guard above:\n // this method returns `Promise<State>` and its URL-plugin callers invoke it\n // from popstate handlers, where a new synchronous throw would be a change\n // of failure shape rather than a new failure.\n const misChanneled = findMisChanneledKey(\n state.params,\n deps.getQueryParams(state.name),\n );\n\n if (misChanneled !== undefined) {\n const err = new RouterError(errorCodes.WRONG_CHANNEL, {\n routeName: state.name,\n message: `[router.navigateToState] ${misChanneledKeyMessage(\n state.name,\n misChanneled,\n \"`state.params`\",\n )}`,\n });\n\n deps.emitTransitionError(undefined, deps.getState(), err);\n\n return Promise.reject(err);\n }\n\n // States from `matchPath` are deeply frozen (`freezeStateShell`).\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 // Carry the query channel through the writable shell (RFC-4 M2 / #1548) —\n // without this, start()'s navigateToState(matchPath(...)) would drop the\n // matched query from the committed state.\n search: state.search,\n path: state.path,\n context: { ...state.context },\n } as State;\n\n // No route-meta to carry any more (RFC-4 M2 / #1548): ownership is read from\n // the live matcher by `state.name` (`getTransitionPath`'s `getMeta`), not\n // from a per-State WeakMap. The former #1170 carry — which existed only so a\n // matchPath-derived writable shell stayed non-meta-less across consecutive\n // popstate navs — is obsolete: any state whose name is in the tree takes the\n // STANDARD PATH regardless of object identity.\n\n return executeNavigation(this.#deps, this.#inFlight, writableState, opts);\n }\n\n #navigateToDefault(opts: NavigationOptions): State | Promise<State> {\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 let search: SearchParams;\n\n try {\n ({ route, params, search } = 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 // Both channels, never one bag (RFC-4 M2 / #1548). The query slot took\n // `undefined` until `defaultSearch` existed as a router option, so a\n // query-declared name in `defaultParams` reached the URL only via the\n // `forwardState` seam's channel re-separation — the repair the pipeline\n // design removes. Passing the query here makes the default route's query\n // defaults independent of that stage.\n //\n // Delegates to the PRIVATE core: the public `navigate` would run `#settle`\n // here and again in this method's own wrapper, costing a second `.catch()`\n // per default navigation.\n return this.#navigate(route, params, search, opts);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"NavigationNamespace.js","names":["#inFlight","InFlightNavigation","#deps","#onSuppressed","isExpectedRejection","#settle","#navigate","#navigateToState","#navigateToDefault","navigateToNotFound","#preparingDepth","PRE_SUPPRESSED","CACHED_NOT_STARTED_REJECTION","CACHED_ROUTE_NOT_FOUND_ERROR","CACHED_ROUTE_NOT_FOUND_REJECTION","executeNavigation","constants","RouterError","errorCodes","findMisChanneledKey","misChanneledKeyMessage"],"sources":["../../../../src/namespaces/NavigationNamespace/NavigationNamespace.ts"],"sourcesContent":["import {\n CACHED_NOT_STARTED_REJECTION,\n CACHED_ROUTE_NOT_FOUND_ERROR,\n CACHED_ROUTE_NOT_FOUND_REJECTION,\n isExpectedRejection,\n PRE_SUPPRESSED,\n} from \"./constants\";\nimport { InFlightNavigation } from \"./InFlightNavigation\";\nimport { executeNavigation } from \"./transition/executeNavigation\";\nimport { navigateToNotFound } from \"./transition/navigateToNotFound\";\nimport { findMisChanneledKey, misChanneledKeyMessage } from \"../../channels\";\nimport { errorCodes, constants } from \"../../constants\";\nimport { RouterError } from \"../../RouterError\";\n\nimport type { NavigationDependencies } from \"./types\";\nimport type {\n NavigationOptions,\n Params,\n SearchParams,\n State,\n} from \"../../types\";\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 #deps!: NavigationDependencies;\n #onSuppressed!: (error: unknown) => void;\n // The controller + supersession token of the navigation in flight, owned as\n // one thing (#1607). Built here, one per router — never per navigation.\n readonly #inFlight = new InFlightNavigation();\n // Depth of the PRE-START window — see `#prepare`. Interim form of what\n // becomes a machine state in the state-ownership plan (§10, phase 4).\n #preparingDepth = 0;\n\n // =========================================================================\n // Dependency injection\n // =========================================================================\n\n setDependencies(deps: NavigationDependencies): void {\n this.#deps = deps;\n // Built once here rather than per call: the closure needs THIS router's\n // logger, so it cannot be static, and `setDependencies` is a pure\n // assignment that runs exactly once at wiring (#1331).\n this.#onSuppressed = (error: unknown): void => {\n if (isExpectedRejection(error)) {\n return;\n }\n\n deps.logger.error(\n \"router.navigate\",\n \"Unexpected navigation error\",\n error,\n );\n };\n }\n\n // =========================================================================\n // Instance methods\n // =========================================================================\n\n navigate(\n name: string,\n params: Params,\n search: SearchParams | undefined,\n opts: NavigationOptions,\n ): State | Promise<State> {\n return this.#settle(this.#navigate(name, params, search, 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(\n state: State,\n opts: NavigationOptions,\n ): State | Promise<State> {\n return this.#settle(this.#navigateToState(state, opts));\n }\n\n navigateToDefault(opts: NavigationOptions): State | Promise<State> {\n return this.#settle(this.#navigateToDefault(opts));\n }\n\n navigateToNotFound(path: string): State {\n return navigateToNotFound(this.#deps, path);\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.#inFlight.abort(reason);\n }\n\n /**\n * Is a navigation between its entry point and its announce — the PRE-START\n * window (#1610)?\n *\n * Two stretches raise `#preparingDepth`, and both are application code running\n * before `TRANSITION_START`: `buildNavigateState` (the `forwardState` and\n * `buildPath` interceptor chains plus the route's own codecs) and\n * `resolveDefault` (`defaultRoute` / `defaultParams` / `defaultSearch` may\n * each be a dependency-resolved callback). That is precisely why the\n * reentrancy ban did not reach them: `Router.#assertNotReentrant` keys off the\n * emitter's dispatch depth, and there has been no emit yet — so a nested\n * `navigate()` ran to completion here, committing a state the outer navigation\n * overwrote a tick later and leaving the outer transition to report departing\n * from wherever the nested one had stopped.\n *\n * Those two calls are the whole window. Everything after them and before the\n * announce is core's own code, except the `CANCEL` emit inside\n * `abortPreviousNavigation` — which the dispatch depth already covers.\n *\n * A DEPTH rather than a boolean, raised inline rather than through a wrapper\n * taking a callback: the wrapper allocated a closure per navigation on the\n * #307 hot path, and the depth makes the two sites' relationship a\n * non-question (they are sequential today, and nesting would be safe anyway).\n * Each site lowers it in a `finally`, because every exit has to — the early\n * refusals live in this same window (`ROUTE_NOT_FOUND` when no state comes\n * back, `WRONG_CHANNEL` from the always-on channel guard, any throw from user\n * code), and a marker left raised deadlocks the router against its own next\n * call.\n *\n * Read by `Router.#assertNotReentrant` alongside `EventBus.isProcessing()`;\n * between them they span every window in which application code runs inside a\n * navigation core has not finished setting up. A GUARD is deliberately\n * neither: it runs after the announce, so the guard-redirect stays a supersede.\n *\n * ⚠ Interim form. It is absorbed by pre-start becoming a STATE of the machine\n * — then a nested navigation is an ordinary supersede for the table and needs\n * no marker at all. See `packages/core/.claude/fsm-as-state-owner-2026-07-31.md`\n * §8 (why that is 10b and not 10a) and the plan in §10, phase 4.\n */\n isPreparing(): boolean {\n return this.#preparingDepth > 0;\n }\n\n /**\n * The producer's own fire-and-forget guarantee (#721): whatever leaves a\n * public method here is safe to drop on the floor.\n *\n * ONE checkpoint per public method, deliberately — not a `.catch()` at each of\n * the six return sites. A forgotten site is invisible until it leaks, which is\n * the bug this replaces; a single choke point cannot be forgotten. And the\n * discriminator is the returned VALUE's identity, not a flag the facade reads\n * afterwards: `PRE_SUPPRESSED` promises already carry a module-load handler, so\n * re-suppressing them buys nothing and costs a derived promise.\n *\n * A synchronously-returned `State` needs nothing at all — there is no rejection\n * to suppress, which is precisely what makes `lastSyncResolved` unnecessary:\n * the TYPE now carries what the flag used to announce.\n */\n #settle(result: State | Promise<State>): State | Promise<State> {\n if (result instanceof Promise && !PRE_SUPPRESSED.has(result)) {\n result.catch(this.#onSuppressed);\n }\n\n return result;\n }\n\n /**\n * `navigate`'s body, minus the fire-and-forget checkpoint.\n *\n * Split out because `navigateToDefault` delegates HERE, not to the public\n * method: routing it through `navigate` would run `#settle` twice per default\n * navigation and attach a second, pointless `.catch()` — an extra derived\n * promise on a path that has none today.\n */\n #navigate(\n name: string,\n params: Params,\n search: SearchParams | undefined,\n opts: NavigationOptions,\n ): State | Promise<State> {\n const deps = this.#deps;\n\n // Fast-path sync rejections: cached error + cached Promise.reject.\n // No allocations, no throw/catch overhead; `#settle` recognises the\n // singleton by identity and skips its `.catch()`.\n if (!deps.canNavigate()) {\n return CACHED_NOT_STARTED_REJECTION;\n }\n\n let toState: State | undefined;\n\n // PRE-START window (#1610) — user code runs in here, before any emit. See\n // `isPreparing`. The `finally` is load-bearing: the early refusals exit\n // through it too.\n this.#preparingDepth++;\n\n try {\n toState = deps.buildNavigateState(name, params, search);\n } catch (error) {\n // No `v8 ignore` here any more: the comment that used to sit above this\n // line claimed the path was \"reachable only via validator-driven throws\n // … covered in @real-router/validation-plugin's suite, not in core\", and\n // that stopped being true when the always-on channel guard (#1572) began\n // throwing from core's own `buildNavigateState`. Measured: with the ignore\n // removed, core's suite still reports 100% — it was masking a live region.\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 } finally {\n this.#preparingDepth--;\n }\n\n if (!toState) {\n deps.emitTransitionError(\n undefined,\n deps.getState(),\n CACHED_ROUTE_NOT_FOUND_ERROR,\n );\n\n return CACHED_ROUTE_NOT_FOUND_REJECTION;\n }\n\n return executeNavigation(this.#deps, this.#inFlight, toState, opts);\n }\n\n #navigateToState(\n state: State,\n opts: NavigationOptions,\n ): State | Promise<State> {\n const deps = this.#deps;\n\n if (!deps.canNavigate()) {\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 // A FRESH reject (it carries `routeName`), so it is deliberately NOT in\n // `PRE_SUPPRESSED` and `#settle` will attach its `.catch()`. Adding it to\n // that set — the modern shape of the mistake that caused #721 — would skip\n // suppression on a promise nobody else handles and leak it.\n return Promise.reject(err);\n }\n\n // Channel guard, position P3 (#1572). `navigateToState` is the ONE producer\n // that takes a ready-made `State` instead of a `params` argument, so the\n // predicate reads `state.params ∩ queryNames(state.name)`. What it commits\n // becomes `getState()`, so a pre-M2 layout would be silent corruption: the\n // key sits in `state.params` and never reaches `state.path`.\n //\n // Costs nothing on healthy flows — a state produced by core (`matchPath`,\n // `makeState`) is channel-correct by construction, so the predicate is\n // empty on every popstate / memory-restore / SSR-hydration commit. `start()`\n // commits THROUGH here (`RouterLifecycleNamespace`), which is why the guard\n // lives in the namespace rather than on the plugin-API door.\n //\n // Rejects rather than throwing, mirroring the ROUTE_NOT_FOUND guard above:\n // this method returns `Promise<State>` and its URL-plugin callers invoke it\n // from popstate handlers, where a new synchronous throw would be a change\n // of failure shape rather than a new failure.\n const misChanneled = findMisChanneledKey(\n state.params,\n deps.getQueryParams(state.name),\n );\n\n if (misChanneled !== undefined) {\n const err = new RouterError(errorCodes.WRONG_CHANNEL, {\n routeName: state.name,\n message: `[router.navigateToState] ${misChanneledKeyMessage(\n state.name,\n misChanneled,\n \"`state.params`\",\n )}`,\n });\n\n deps.emitTransitionError(undefined, deps.getState(), err);\n\n return Promise.reject(err);\n }\n\n // States from `matchPath` are deeply frozen (`freezeStateShell`).\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 // Carry the query channel through the writable shell (RFC-4 M2 / #1548) —\n // without this, start()'s navigateToState(matchPath(...)) would drop the\n // matched query from the committed state.\n search: state.search,\n path: state.path,\n context: { ...state.context },\n } as State;\n\n // No route-meta to carry any more (RFC-4 M2 / #1548): ownership is read from\n // the live matcher by `state.name` (`getTransitionPath`'s `getMeta`), not\n // from a per-State WeakMap. The former #1170 carry — which existed only so a\n // matchPath-derived writable shell stayed non-meta-less across consecutive\n // popstate navs — is obsolete: any state whose name is in the tree takes the\n // STANDARD PATH regardless of object identity.\n\n return executeNavigation(this.#deps, this.#inFlight, writableState, opts);\n }\n\n #navigateToDefault(opts: NavigationOptions): State | Promise<State> {\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 let search: SearchParams;\n\n // PRE-START window too, and for the same reason (#1610): `defaultRoute` /\n // `defaultParams` / `defaultSearch` may each be a dependency-resolved\n // CALLBACK, so this runs user code before there is even a route name to\n // navigate to. Sequential with `#navigate`'s window below, never nested.\n this.#preparingDepth++;\n\n try {\n ({ route, params, search } = 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 } finally {\n this.#preparingDepth--;\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 // Both channels, never one bag (RFC-4 M2 / #1548). The query slot took\n // `undefined` until `defaultSearch` existed as a router option, so a\n // query-declared name in `defaultParams` reached the URL only via the\n // `forwardState` seam's channel re-separation — the repair the pipeline\n // design removes. Passing the query here makes the default route's query\n // defaults independent of that stage.\n //\n // Delegates to the PRIVATE core: the public `navigate` would run `#settle`\n // here and again in this method's own wrapper, costing a second `.catch()`\n // per default navigation.\n return this.#navigate(route, params, search, opts);\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require("../../../constants.js"),t=require("../../../RouterError.js");function n(e,t,n,r,i){Object.freeze(n),Object.freeze(r);let a={phase:`activating`,reason:`success`,segments:Object.freeze({deactivated:n,activated:r,intersection:i})};return e?.name!==void 0&&(a.from=e.name),t.reload!==void 0&&(a.reload=t.reload),t.replace!==void 0&&(a.replace=t.replace),t.redirected!==void 0&&(a.redirected=t.redirected),Object.freeze(a)}function r({signal:e,...t}){return t}function i(i,a){let{toState:o,fromState:s,opts:c,toDeactivate:l,toActivate:u,intersection:d}=a;if(o.name!==e.constants.UNKNOWN_ROUTE&&!i.hasRoute(o.name)){let n=new t.RouterError(e.errorCodes.ROUTE_NOT_FOUND,{routeName:o.name});throw i.sendTransitionFail(o,s,n),n}if(s)for(let e of l)!u.includes(e)&&a.canDeactivateFunctions.has(e)&&i.clearCanDeactivate(e);o.transition=n(s,c,l,u,d);let f=Object.freeze(o);i.setState(f);let p=c.signal===void 0?c:r(c);return i.sendTransitionDone(f,s,p),f}exports.completeTransition=i;
1
+ const e=require("../../../constants.js"),t=require("../../../RouterError.js");function n(e,t,n,r,i){Object.freeze(n),Object.freeze(r);let a={phase:`activating`,reason:`success`,segments:Object.freeze({deactivated:n,activated:r,intersection:i})};return e?.name!==void 0&&(a.from=e.name),t.reload!==void 0&&(a.reload=t.reload),t.replace!==void 0&&(a.replace=t.replace),t.redirected!==void 0&&(a.redirected=t.redirected),Object.freeze(a)}function r({signal:e,...t}){return t}function i(i,a){let{toState:o,fromState:s,opts:c,toDeactivate:l,toActivate:u,intersection:d}=a;if(o.name!==e.constants.UNKNOWN_ROUTE&&!i.hasRoute(o.name)){let n=new t.RouterError(e.errorCodes.ROUTE_NOT_FOUND,{routeName:o.name});throw i.sendTransitionFail(o,s,n),n}if(s){let n=!1;for(let e of l)u.includes(e)||!a.canDeactivateFunctions.has(e)||(i.clearCanDeactivate(e),n=!0);if(n&&!i.isTransitioning())throw new t.RouterError(e.errorCodes.TRANSITION_CANCELLED)}o.transition=n(s,c,l,u,d);let f=Object.freeze(o);i.setState(f);let p=c.signal===void 0?c:r(c);return i.sendTransitionDone(f,s,p),f}exports.completeTransition=i;
2
2
  //# sourceMappingURL=completeTransition.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"completeTransition.js","names":["constants","RouterError","errorCodes"],"sources":["../../../../../src/namespaces/NavigationNamespace/transition/completeTransition.ts"],"sourcesContent":["import { errorCodes, constants } from \"../../../constants\";\nimport { RouterError } from \"../../../RouterError\";\n\nimport type { NavigationOptions, State, TransitionMeta } from \"../../../types\";\nimport type { NavigationDependencies, NavigationContext } from \"../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"],"mappings":"8EAUA,SAAS,EACP,EACA,EACA,EACA,EACA,EACgB,CAChB,OAAO,OAAO,CAAY,EAC1B,OAAO,OAAO,CAAU,EAQxB,IAAM,EAA8B,CAClC,MAAO,aACP,OAAQ,UACR,SATe,OAAO,OAAO,CAC7B,YAAa,EACb,UAAW,EACX,cACF,CAKS,CACT,EAkBA,OAhBI,GAAW,OAAS,IAAA,KACtB,EAAK,KAAO,EAAU,MAGpB,EAAK,SAAW,IAAA,KAClB,EAAK,OAAS,EAAK,QAGjB,EAAK,UAAY,IAAA,KACnB,EAAK,QAAU,EAAK,SAGlB,EAAK,aAAe,IAAA,KACtB,EAAK,WAAa,EAAK,YAGlB,OAAO,OAAO,CAAI,CAC3B,CAEA,SAAS,EAAY,CACnB,OAAQ,EACR,GAAG,GACoC,CACvC,OAAO,CACT,CAEA,SAAgB,EACd,EACA,EACO,CACP,GAAM,CAAE,UAAS,YAAW,OAAM,eAAc,aAAY,gBAC1D,EAEF,GACE,EAAQ,OAASA,EAAAA,UAAU,eAC3B,CAAC,EAAK,SAAS,EAAQ,IAAI,EAC3B,CACA,IAAM,EAAM,IAAIC,EAAAA,YAAYC,EAAAA,WAAW,gBAAiB,CACtD,UAAW,EAAQ,IACrB,CAAC,EAID,MAFA,EAAK,mBAAmB,EAAS,EAAW,CAAG,EAEzC,CACR,CAEA,GAAI,EACG,IAAA,IAAM,KAAQ,EACb,CAAC,EAAW,SAAS,CAAI,GAAK,EAAI,uBAAuB,IAAI,CAAI,GACnE,EAAK,mBAAmB,CAAI,EAKlC,EAA4C,WAAa,EACvD,EACA,EACA,EACA,EACA,CACF,EAEA,IAAM,EAAa,OAAO,OAAO,CAAO,EAExC,EAAK,SAAS,CAAU,EAExB,IAAM,EAAiB,EAAK,SAAW,IAAA,GAAY,EAAO,EAAY,CAAI,EAI1E,OAFA,EAAK,mBAAmB,EAAY,EAAW,CAAc,EAEtD,CACT"}
1
+ {"version":3,"file":"completeTransition.js","names":["constants","RouterError","errorCodes"],"sources":["../../../../../src/namespaces/NavigationNamespace/transition/completeTransition.ts"],"sourcesContent":["import { errorCodes, constants } from \"../../../constants\";\nimport { RouterError } from \"../../../RouterError\";\n\nimport type { NavigationOptions, State, TransitionMeta } from \"../../../types\";\nimport type { NavigationDependencies, NavigationContext } from \"../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 let cleared = false;\n\n for (const name of toDeactivate) {\n if (toActivate.includes(name) || !nav.canDeactivateFunctions.has(name)) {\n continue;\n }\n\n deps.clearCanDeactivate(name);\n cleared = true;\n }\n\n // The one place this function runs USER code before committing (#1611).\n // Clearing the external guard recompiles the slot from a surviving\n // DEFINITION factory, and that factory is the application's: a `dispose()`\n // or `stop()` from it terminated the router one statement before `setState`,\n // and the commit landed anyway — `navigate()` resolved while `COMPLETE` from\n // `DISPOSED`/`IDLE` was a table no-op, so no subscriber was ever told. The\n // #1169 commit-gate cannot cover it: it sits BEFORE this function, i.e. on\n // the far side of that user code, and is `suspendable`-gated while this\n // reproduces on the uncancellable `completeImmediate` arc.\n //\n // Gated on `cleared` so the question is asked only when the factory could\n // actually have run — a navigation that clears nothing pays a boolean, not\n // an `isTransitioning()` call, on a path #307 counts nanoseconds on. The\n // gate is a PERF gate and behaviourally equivalent (measured: removing it\n // leaves the whole suite green — nothing ran that could change liveness),\n // the same shape as `suspendable` gating the #1169 commit-gate.\n //\n // `isTransitioning()`, not `isActive()`: the question is \"is the machine\n // still inside a transition\", and it is strictly the better one here. It\n // catches the reported `dispose()` (DISPOSED) and `stop()` (IDLE), and for\n // free the factory that starts a NESTED navigation which runs to completion\n // — that leaves the FSM in READY, so `isActive()` would wave it through and\n // this commit would silently overwrite the nested one's. Measured: with\n // `isActive()` the nested case commits the outer state over the inner\n // result; with this, the inner result stands.\n //\n // ⚠ Interim form; absorbed when the commit is asked THROUGH the FSM right\n // before `setState` (RFC-10a §16.7 / §9.9, plan §10 phase 3). Two cells stay\n // open and are deliberately NOT patched here — both need a token this\n // function is not given: a nested navigation still IN FLIGHT (the FSM is in\n // ITS transition, so `isTransitioning()` is true for somebody else), and the\n // `replace()`-revalidation commit, which is a different commit path.\n if (cleared && !deps.isTransitioning()) {\n throw new RouterError(errorCodes.TRANSITION_CANCELLED);\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"],"mappings":"8EAUA,SAAS,EACP,EACA,EACA,EACA,EACA,EACgB,CAChB,OAAO,OAAO,CAAY,EAC1B,OAAO,OAAO,CAAU,EAQxB,IAAM,EAA8B,CAClC,MAAO,aACP,OAAQ,UACR,SATe,OAAO,OAAO,CAC7B,YAAa,EACb,UAAW,EACX,cACF,CAKS,CACT,EAkBA,OAhBI,GAAW,OAAS,IAAA,KACtB,EAAK,KAAO,EAAU,MAGpB,EAAK,SAAW,IAAA,KAClB,EAAK,OAAS,EAAK,QAGjB,EAAK,UAAY,IAAA,KACnB,EAAK,QAAU,EAAK,SAGlB,EAAK,aAAe,IAAA,KACtB,EAAK,WAAa,EAAK,YAGlB,OAAO,OAAO,CAAI,CAC3B,CAEA,SAAS,EAAY,CACnB,OAAQ,EACR,GAAG,GACoC,CACvC,OAAO,CACT,CAEA,SAAgB,EACd,EACA,EACO,CACP,GAAM,CAAE,UAAS,YAAW,OAAM,eAAc,aAAY,gBAC1D,EAEF,GACE,EAAQ,OAASA,EAAAA,UAAU,eAC3B,CAAC,EAAK,SAAS,EAAQ,IAAI,EAC3B,CACA,IAAM,EAAM,IAAIC,EAAAA,YAAYC,EAAAA,WAAW,gBAAiB,CACtD,UAAW,EAAQ,IACrB,CAAC,EAID,MAFA,EAAK,mBAAmB,EAAS,EAAW,CAAG,EAEzC,CACR,CAEA,GAAI,EAAW,CACb,IAAI,EAAU,GAEd,IAAK,IAAM,KAAQ,EACb,EAAW,SAAS,CAAI,GAAK,CAAC,EAAI,uBAAuB,IAAI,CAAI,IAIrE,EAAK,mBAAmB,CAAI,EAC5B,EAAU,IAmCZ,GAAI,GAAW,CAAC,EAAK,gBAAgB,EACnC,MAAM,IAAID,EAAAA,YAAYC,EAAAA,WAAW,oBAAoB,CAEzD,CAEA,EAA4C,WAAa,EACvD,EACA,EACA,EACA,EACA,CACF,EAEA,IAAM,EAAa,OAAO,OAAO,CAAO,EAExC,EAAK,SAAS,CAAU,EAExB,IAAM,EAAiB,EAAK,SAAW,IAAA,GAAY,EAAO,EAAY,CAAI,EAI1E,OAFA,EAAK,mBAAmB,EAAY,EAAW,CAAc,EAEtD,CACT"}