@real-router/core 0.84.0 → 0.85.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.
- package/dist/cjs/{Router-CUtHMhRV.js → Router-D6629wsY.js} +2 -2
- package/dist/cjs/{Router-CUtHMhRV.js.map → Router-D6629wsY.js.map} +1 -1
- package/dist/cjs/Router.d.ts.map +1 -1
- package/dist/cjs/api/getRoutesApi.d.ts.map +1 -1
- package/dist/cjs/api.js +1 -1
- package/dist/cjs/api.js.map +1 -1
- package/dist/cjs/buildParamMeta-0m11OIJS.js.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/internals.d.ts +7 -0
- package/dist/cjs/internals.d.ts.map +1 -1
- package/dist/esm/Router-hyjjNX3c.mjs +2 -0
- package/dist/esm/{Router-sWkNlsWW.mjs.map → Router-hyjjNX3c.mjs.map} +1 -1
- package/dist/esm/Router.d.mts.map +1 -1
- package/dist/esm/api/getRoutesApi.d.mts.map +1 -1
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/api.mjs.map +1 -1
- package/dist/esm/buildParamMeta-D8td47fL.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/internals.d.mts +7 -0
- package/dist/esm/internals.d.mts.map +1 -1
- package/package.json +2 -2
- package/dist/cjs/Router.js +0 -2
- package/dist/cjs/Router.js.map +0 -1
- package/dist/cjs/RouterError.js +0 -2
- package/dist/cjs/RouterError.js.map +0 -1
- package/dist/cjs/api/cloneRouter.js +0 -2
- package/dist/cjs/api/cloneRouter.js.map +0 -1
- package/dist/cjs/api/getDependenciesApi.js +0 -2
- package/dist/cjs/api/getDependenciesApi.js.map +0 -1
- package/dist/cjs/api/getLifecycleApi.js +0 -2
- package/dist/cjs/api/getLifecycleApi.js.map +0 -1
- package/dist/cjs/api/getPluginApi.js +0 -2
- package/dist/cjs/api/getPluginApi.js.map +0 -1
- package/dist/cjs/api/getRoutesApi.js +0 -2
- package/dist/cjs/api/getRoutesApi.js.map +0 -1
- package/dist/cjs/api/helpers.js +0 -2
- package/dist/cjs/api/helpers.js.map +0 -1
- package/dist/cjs/channels/defaults.js +0 -2
- package/dist/cjs/channels/defaults.js.map +0 -1
- package/dist/cjs/channels/guard.js +0 -2
- package/dist/cjs/channels/guard.js.map +0 -1
- package/dist/cjs/channels/modeGate.js +0 -2
- package/dist/cjs/channels/modeGate.js.map +0 -1
- package/dist/cjs/constants.js +0 -2
- package/dist/cjs/constants.js.map +0 -1
- package/dist/cjs/createRouter.js +0 -2
- package/dist/cjs/createRouter.js.map +0 -1
- package/dist/cjs/engine/builder/buildTree.js +0 -2
- package/dist/cjs/engine/builder/buildTree.js.map +0 -1
- package/dist/cjs/engine/builder/computeCaches.js +0 -2
- package/dist/cjs/engine/builder/computeCaches.js.map +0 -1
- package/dist/cjs/engine/builder/createRouteTree.js +0 -2
- package/dist/cjs/engine/builder/createRouteTree.js.map +0 -1
- package/dist/cjs/engine/createMatcher.js +0 -2
- package/dist/cjs/engine/createMatcher.js.map +0 -1
- package/dist/cjs/engine/operations/routeTreeToDefinitions.js +0 -2
- package/dist/cjs/engine/operations/routeTreeToDefinitions.js.map +0 -1
- package/dist/cjs/engine/path-matcher/SegmentMatcher.js +0 -2
- package/dist/cjs/engine/path-matcher/SegmentMatcher.js.map +0 -1
- package/dist/cjs/engine/path-matcher/buildParamMeta.js +0 -2
- package/dist/cjs/engine/path-matcher/buildParamMeta.js.map +0 -1
- package/dist/cjs/engine/path-matcher/encoding.js +0 -2
- package/dist/cjs/engine/path-matcher/encoding.js.map +0 -1
- package/dist/cjs/engine/path-matcher/parseSegment.js +0 -2
- package/dist/cjs/engine/path-matcher/parseSegment.js.map +0 -1
- package/dist/cjs/engine/path-matcher/pathUtils.js +0 -2
- package/dist/cjs/engine/path-matcher/pathUtils.js.map +0 -1
- package/dist/cjs/engine/path-matcher/percentEncoding.js +0 -2
- package/dist/cjs/engine/path-matcher/percentEncoding.js.map +0 -1
- package/dist/cjs/engine/path-matcher/registration/buildParts.js +0 -2
- package/dist/cjs/engine/path-matcher/registration/buildParts.js.map +0 -1
- package/dist/cjs/engine/path-matcher/registration/context.js +0 -2
- package/dist/cjs/engine/path-matcher/registration/context.js.map +0 -1
- package/dist/cjs/engine/path-matcher/registration/errors.js +0 -2
- package/dist/cjs/engine/path-matcher/registration/errors.js.map +0 -1
- package/dist/cjs/engine/path-matcher/registration/index.js +0 -2
- package/dist/cjs/engine/path-matcher/registration/index.js.map +0 -1
- package/dist/cjs/engine/path-matcher/registration/trie.js +0 -2
- package/dist/cjs/engine/path-matcher/registration/trie.js.map +0 -1
- package/dist/cjs/engine/path-matcher/registration/trieNodes.js +0 -2
- package/dist/cjs/engine/path-matcher/registration/trieNodes.js.map +0 -1
- package/dist/cjs/engine/search-params/decode.js +0 -2
- package/dist/cjs/engine/search-params/decode.js.map +0 -1
- package/dist/cjs/engine/search-params/encode.js +0 -2
- package/dist/cjs/engine/search-params/encode.js.map +0 -1
- package/dist/cjs/engine/search-params/searchParams.js +0 -2
- package/dist/cjs/engine/search-params/searchParams.js.map +0 -1
- package/dist/cjs/engine/search-params/strategies/array.js +0 -2
- package/dist/cjs/engine/search-params/strategies/array.js.map +0 -1
- package/dist/cjs/engine/search-params/strategies/boolean.js +0 -2
- package/dist/cjs/engine/search-params/strategies/boolean.js.map +0 -1
- package/dist/cjs/engine/search-params/strategies/index.js +0 -2
- package/dist/cjs/engine/search-params/strategies/index.js.map +0 -1
- package/dist/cjs/engine/search-params/strategies/null.js +0 -2
- package/dist/cjs/engine/search-params/strategies/null.js.map +0 -1
- package/dist/cjs/engine/search-params/strategies/number.js +0 -2
- package/dist/cjs/engine/search-params/strategies/number.js.map +0 -1
- package/dist/cjs/engine/search-params/utils.js +0 -2
- package/dist/cjs/engine/search-params/utils.js.map +0 -1
- package/dist/cjs/engine/validation/route-batch.js +0 -2
- package/dist/cjs/engine/validation/route-batch.js.map +0 -1
- package/dist/cjs/engine/validation/routes.js +0 -2
- package/dist/cjs/engine/validation/routes.js.map +0 -1
- package/dist/cjs/getNavigator.js +0 -2
- package/dist/cjs/getNavigator.js.map +0 -1
- package/dist/cjs/guards.js +0 -2
- package/dist/cjs/guards.js.map +0 -1
- package/dist/cjs/helpers.js +0 -2
- package/dist/cjs/helpers.js.map +0 -1
- package/dist/cjs/internals.js +0 -2
- package/dist/cjs/internals.js.map +0 -1
- package/dist/cjs/limits.js +0 -2
- package/dist/cjs/limits.js.map +0 -1
- package/dist/cjs/namespaces/DependenciesNamespace/dependenciesStore.js +0 -2
- package/dist/cjs/namespaces/DependenciesNamespace/dependenciesStore.js.map +0 -1
- package/dist/cjs/namespaces/EventBusNamespace/EventBusNamespace.js +0 -2
- package/dist/cjs/namespaces/EventBusNamespace/EventBusNamespace.js.map +0 -1
- package/dist/cjs/namespaces/NavigationNamespace/InFlightNavigation.js +0 -2
- package/dist/cjs/namespaces/NavigationNamespace/InFlightNavigation.js.map +0 -1
- package/dist/cjs/namespaces/NavigationNamespace/NavigationNamespace.js +0 -2
- package/dist/cjs/namespaces/NavigationNamespace/NavigationNamespace.js.map +0 -1
- package/dist/cjs/namespaces/NavigationNamespace/constants.js +0 -2
- package/dist/cjs/namespaces/NavigationNamespace/constants.js.map +0 -1
- package/dist/cjs/namespaces/NavigationNamespace/transition/completeTransition.js +0 -2
- package/dist/cjs/namespaces/NavigationNamespace/transition/completeTransition.js.map +0 -1
- package/dist/cjs/namespaces/NavigationNamespace/transition/errorHandling.js +0 -2
- package/dist/cjs/namespaces/NavigationNamespace/transition/errorHandling.js.map +0 -1
- package/dist/cjs/namespaces/NavigationNamespace/transition/executeNavigation.js +0 -2
- package/dist/cjs/namespaces/NavigationNamespace/transition/executeNavigation.js.map +0 -1
- package/dist/cjs/namespaces/NavigationNamespace/transition/guardPhase.js +0 -2
- package/dist/cjs/namespaces/NavigationNamespace/transition/guardPhase.js.map +0 -1
- package/dist/cjs/namespaces/NavigationNamespace/transition/navigateToNotFound.js +0 -2
- package/dist/cjs/namespaces/NavigationNamespace/transition/navigateToNotFound.js.map +0 -1
- package/dist/cjs/namespaces/OptionsNamespace/OptionsNamespace.js +0 -2
- package/dist/cjs/namespaces/OptionsNamespace/OptionsNamespace.js.map +0 -1
- package/dist/cjs/namespaces/OptionsNamespace/constants.js +0 -2
- package/dist/cjs/namespaces/OptionsNamespace/constants.js.map +0 -1
- package/dist/cjs/namespaces/OptionsNamespace/helpers.js +0 -2
- package/dist/cjs/namespaces/OptionsNamespace/helpers.js.map +0 -1
- package/dist/cjs/namespaces/OptionsNamespace/validators.js +0 -2
- package/dist/cjs/namespaces/OptionsNamespace/validators.js.map +0 -1
- package/dist/cjs/namespaces/PluginsNamespace/PluginsNamespace.js +0 -2
- package/dist/cjs/namespaces/PluginsNamespace/PluginsNamespace.js.map +0 -1
- package/dist/cjs/namespaces/PluginsNamespace/constants.js +0 -2
- package/dist/cjs/namespaces/PluginsNamespace/constants.js.map +0 -1
- package/dist/cjs/namespaces/PluginsNamespace/validators.js +0 -2
- package/dist/cjs/namespaces/PluginsNamespace/validators.js.map +0 -1
- package/dist/cjs/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.js +0 -2
- package/dist/cjs/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.js.map +0 -1
- package/dist/cjs/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.js +0 -2
- package/dist/cjs/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.js.map +0 -1
- package/dist/cjs/namespaces/RouterLifecycleNamespace/constants.js +0 -2
- package/dist/cjs/namespaces/RouterLifecycleNamespace/constants.js.map +0 -1
- package/dist/cjs/namespaces/RoutesNamespace/RoutesNamespace.js +0 -2
- package/dist/cjs/namespaces/RoutesNamespace/RoutesNamespace.js.map +0 -1
- package/dist/cjs/namespaces/RoutesNamespace/constants.js +0 -2
- package/dist/cjs/namespaces/RoutesNamespace/constants.js.map +0 -1
- package/dist/cjs/namespaces/RoutesNamespace/forwardChain.js +0 -2
- package/dist/cjs/namespaces/RoutesNamespace/forwardChain.js.map +0 -1
- package/dist/cjs/namespaces/RoutesNamespace/helpers.js +0 -2
- package/dist/cjs/namespaces/RoutesNamespace/helpers.js.map +0 -1
- package/dist/cjs/namespaces/RoutesNamespace/routeGuards.js +0 -2
- package/dist/cjs/namespaces/RoutesNamespace/routeGuards.js.map +0 -1
- package/dist/cjs/namespaces/RoutesNamespace/routesStore.js +0 -2
- package/dist/cjs/namespaces/RoutesNamespace/routesStore.js.map +0 -1
- package/dist/cjs/namespaces/StateNamespace/StateNamespace.js +0 -2
- package/dist/cjs/namespaces/StateNamespace/StateNamespace.js.map +0 -1
- package/dist/cjs/pipeline/buildURL.js +0 -2
- package/dist/cjs/pipeline/buildURL.js.map +0 -1
- package/dist/cjs/pipeline/canonicalize.js +0 -2
- package/dist/cjs/pipeline/canonicalize.js.map +0 -1
- package/dist/cjs/pipeline/materialize.js +0 -2
- package/dist/cjs/pipeline/materialize.js.map +0 -1
- package/dist/cjs/routerFSM.js +0 -2
- package/dist/cjs/routerFSM.js.map +0 -1
- package/dist/cjs/transitionPath.js +0 -2
- package/dist/cjs/transitionPath.js.map +0 -1
- package/dist/cjs/utils/event-emitter/EventEmitter.js +0 -2
- package/dist/cjs/utils/event-emitter/EventEmitter.js.map +0 -1
- package/dist/cjs/utils/fsm/fsm.js +0 -2
- package/dist/cjs/utils/fsm/fsm.js.map +0 -1
- package/dist/cjs/utils/logger/RouterLogger.js +0 -2
- package/dist/cjs/utils/logger/RouterLogger.js.map +0 -1
- package/dist/cjs/utils/logger/constants.js +0 -2
- package/dist/cjs/utils/logger/constants.js.map +0 -1
- package/dist/cjs/wiring/wireNamespaces.js +0 -2
- package/dist/cjs/wiring/wireNamespaces.js.map +0 -1
- package/dist/esm/Router-sWkNlsWW.mjs +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildParamMeta-D8td47fL.mjs","names":["LT","QUESTION","SLASH"],"sources":["../../src/channels/guard.ts","../../src/internals.ts","../../src/engine/path-matcher/parseSegment.ts","../../src/engine/path-matcher/buildParamMeta.ts"],"sourcesContent":["// packages/core/src/channels/guard.ts\n\nimport type { Params } from \"../types\";\n\n/**\n * THE predicate of the always-on channel guard: the first key the caller put in\n * the PATH bag while the route declares it as a QUERY param, or `undefined`\n * when the bag is channel-correct.\n *\n * A DETECTOR, not a normaliser — the key is never moved. Moving it is what\n * `separateChannels` (stage ②) used to do — a function that no longer exists.\n * Channel-correctness is the producer's contract now, not a repair the pipeline\n * performs behind everyone's back.\n *\n * Scans `queryNames` (a route's declared query names — small, cached) rather\n * than the bag, so there is no `Object.keys` allocation, and short-circuits on\n * a route with no query declarations, which is the common case.\n *\n * `undefined` is absence on both sides (#1550 / #1551), so an\n * `undefined`-valued key is NOT a mis-channel: it is the documented removal\n * marker `persistent-params` relies on, and it never reaches a built state\n * anyway. A name that also occupies a path slot (`/items/:id?id`) is absent\n * from `queryNames` by construction (#843 / #1549 carve-out), so the collision\n * form is legitimately path-owned and passes.\n *\n * @internal\n */\nexport function findMisChanneledKey(\n params: Params | undefined,\n queryNames: readonly string[],\n): string | undefined {\n if (queryNames.length === 0 || params === undefined) {\n return undefined;\n }\n\n for (const key of queryNames) {\n if (!Object.hasOwn(params, key)) {\n continue;\n }\n\n let value: unknown;\n\n try {\n value = params[key];\n } catch {\n // A DIAGNOSTIC must never become the thing that throws. The bag may be\n // backed by accessors (a Proxy, a getter, a framework's reactive object),\n // and reading one here happens EARLIER than any consumer would have read\n // it — so an accessor that throws would surface from the guard instead of\n // from the code that actually needed the value, moving the origin of an\n // existing failure. Treat it as \"nothing to report\" and let the real\n // consumer hit the same accessor exactly as it did before.\n return undefined;\n }\n\n if (value !== undefined) {\n return key;\n }\n }\n\n return undefined;\n}\n\n/**\n * THE centralized channel check — the single place a mis-channelled bag is\n * refused, wherever it came from.\n *\n * Replaces the repair `separateChannels` (stage ②, since deleted) used to\n * perform at the `forwardState` seam. A key the route declares with `?`, sitting in the PATH\n * bag, is a producer's mistake — the producer named the route, so it knows the\n * declaration — and the router now says so instead of quietly moving the field\n * into the other object. Moving it was invisible: the caller kept believing\n * their bag was the one that shipped, and two producers of the SAME intent\n * could disagree about which channel a key ended up in.\n *\n * `source` names WHOSE bag is wrong, which is the whole diagnostic value at a\n * seam: the caller's argument, a `forwardState` interceptor's return, or the\n * output of a route's own `decodeParams`. It takes a THUNK as well as a string\n * because the seam sits on the navigation hot path — a source that has to be\n * composed (naming the route a chain forwarded from) must not build its string\n * on every call just to discard it on the 99.99% of calls that pass.\n *\n * @internal\n */\nexport function assertChannelCorrect(\n method: string,\n routeName: string,\n params: Params | undefined,\n queryNames: readonly string[],\n source?: string | (() => string),\n remedy?: string,\n): void {\n const key = findMisChanneledKey(params, queryNames);\n\n if (key !== undefined) {\n throw new TypeError(\n `[router.${method}] ${misChanneledKeyMessage(\n routeName,\n key,\n typeof source === \"function\" ? source() : source,\n remedy,\n )}`,\n );\n }\n}\n\n/**\n * The guard's actionable message. One builder for every position, so the\n * wording a user sees does not depend on which door they came through — the\n * facade's `TypeError`, the seam's, the decoder's, and `navigateToState`'s\n * `RouterError(WRONG_CHANNEL)`, which needs the wording WITHOUT the throw and is\n * why this is a separate function from {@link assertChannelCorrect}.\n *\n * @internal\n */\nexport function misChanneledKeyMessage(\n routeName: string,\n key: string,\n source = \"the `params` argument\",\n remedy = \"Pass it in `search` instead\",\n): string {\n return `Route \"${routeName}\" declares \\`${key}\\` as a query param, but it was given in ${source} — the path channel. ${remedy}; the two channels are separate since RFC-4 M2 and the router never moves a key between them.`;\n}\n","import { assertChannelCorrect } from \"./channels\";\n\nimport type { RouteTree } from \"./engine\";\nimport type { DependenciesStore } from \"./namespaces\";\nimport type { RoutesStore } from \"./namespaces/RoutesNamespace\";\nimport type { RouteResolver } from \"./pipeline\";\nimport type { Router as RouterClass } from \"./Router\";\nimport type {\n AnyOptions,\n DefaultDependencies,\n EventName,\n LoggerConfig,\n NavigationOptions,\n Options,\n Params,\n Plugin,\n Router as RouterInterface,\n RouterLogger,\n RouteTreeState,\n SearchParams,\n SerializedRouterState,\n SimpleState,\n State,\n TreeChangedEvent,\n Unsubscribe,\n EventMethodMap,\n PluginFactory,\n} from \"./types\";\nimport type { RouterValidator } from \"./types/RouterValidator\";\n\nexport interface RouterInternals<\n D extends DefaultDependencies = DefaultDependencies,\n> {\n readonly makeState: <\n P extends Params = Params,\n S extends SearchParams = SearchParams,\n >(\n name: string,\n params?: P,\n search?: S,\n path?: string,\n ) => State<P, S>;\n\n /**\n * Per-segment param-source map for a route name (`{ segment: { param: \"url\" |\n * \"query\" } }`), read from the live matcher — the ownership channel for\n * `getTransitionPath` (RFC-4 M2 / #1548, replaced the removed per-State\n * `stateMetaStore` WeakMap). `undefined` when the name is not in the tree.\n */\n readonly getMetaForState: (\n name: string,\n ) => Record<string, Record<string, \"url\" | \"query\">> | undefined;\n\n /**\n * The route's DECLARED query-param names — the same registry the URL build\n * prints from (#1556), minus path slots. Feeds the always-on channel guard\n * (#1572); read here rather than re-derived, so classification cannot drift.\n */\n readonly getQueryParams: (name: string) => readonly string[];\n\n readonly forwardState: <\n P extends Params = Params,\n S extends SearchParams = SearchParams,\n >(\n routeName: string,\n routeParams: P,\n routeSearch?: S,\n ) => SimpleState<P, S>;\n\n readonly buildStateResolved: (\n resolvedName: string,\n resolvedParams: Params,\n ) => RouteTreeState | undefined;\n\n readonly matchPath: <P extends Params = Params>(\n path: string,\n options?: AnyOptions,\n ) => State<P> | undefined;\n\n readonly getOptions: () => Options<D>;\n\n readonly addEventListener: <E extends EventName>(\n eventName: E,\n cb: Plugin[EventMethodMap[E]],\n ) => Unsubscribe;\n\n /**\n * Route-tree mutation channel — internal access for the `getRoutesApi`\n * wrapper. A dedicated bridge is required because the public\n * `addEventListener<E extends EventName>` structurally rejects\n * `\"TREE_CHANGED\"` (it is not in the public `EventName` union), is strict on\n * duplicates, and exposes neither `emit` nor `listenerCount`.\n */\n readonly treeChanged: {\n readonly emit: (event: TreeChangedEvent) => void;\n readonly subscribe: (\n handler: (event: TreeChangedEvent) => void,\n ) => Unsubscribe;\n readonly listenerCount: () => number;\n /**\n * True while a `TREE_CHANGED` emit is on the stack — `getRoutesApi` reads it\n * to reject reentrant route-CRUD from a `subscribeChanges` handler (#1032).\n */\n readonly isEmitting: () => boolean;\n };\n\n readonly buildPath: (\n route: string,\n params?: Params,\n search?: SearchParams,\n ) => string;\n\n /**\n * The navigation pipeline's read-model, for entry points that live on this\n * plugin-facing surface rather than in a namespace. Resolved LAZILY: the port\n * is created during wiring, and `registerInternals` runs before that, so the\n * accessor is a closure rather than a value — the same shape the interceptable\n * methods above already use.\n */\n readonly port: () => RouteResolver;\n\n readonly emitTransitionError: (error: Error) => void;\n\n /**\n * Emits `TRANSITION_SUCCESS` directly (no FSM transition) — used by\n * `getRoutesApi().replace()` to notify `router.subscribe` listeners when a\n * structural replace revalidates the active state (#950). Mirrors the success\n * emission `completeTransition` / `navigateToNotFound` perform.\n */\n readonly emitTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n opts?: NavigationOptions,\n ) => void;\n\n /**\n * Commits the not-found (`UNKNOWN_ROUTE`) state for `path` and emits\n * `TRANSITION_SUCCESS` — the `NavigationNamespace.navigateToNotFound`\n * primitive. `replace()` uses it when a structural replace drops the active\n * route, so subscribers are notified instead of the state silently clearing\n * (#950).\n */\n readonly navigateToNotFound: (path: string) => State;\n\n readonly start: (path: string) => Promise<State>;\n\n /**\n * Plugin-only navigation entry point — delegates to\n * `NavigationNamespace.navigateToState` (`getPluginApi(router).navigateToState`).\n * Hidden from `Router`/`Navigator` to keep the userland surface minimal;\n * see `core-types/src/api.ts` for usage docs.\n */\n readonly navigateToState: (\n state: State,\n options?: NavigationOptions,\n ) => Promise<State>;\n\n /* eslint-disable @typescript-eslint/no-explicit-any -- heterogeneous map: stores different InterceptorFn<M> types under different keys */\n readonly interceptors: Map<\n string,\n ((next: (...args: any[]) => any, ...args: any[]) => any)[]\n >;\n /* eslint-enable @typescript-eslint/no-explicit-any */\n\n readonly setRootPath: (rootPath: string) => void;\n readonly getRootPath: () => string;\n\n readonly getTree: () => RouteTree;\n\n readonly isDisposed: () => boolean;\n\n validator: RouterValidator | null;\n\n // Per-router logger instance (built from `options.logger` in the Router\n // constructor). The facade reads it as `getInternals(this).logger`; namespaces\n // receive it via their deps at wiring; plugins reach it through\n // `getPluginApi(router).logger`. Replaces the former process-global singleton\n // from the standalone `@real-router/logger` package (now folded into\n // `utils/logger`), whose `configure()` leaked across routers (#724).\n readonly logger: RouterLogger;\n\n // Dependencies (issue #172)\n readonly dependenciesGetStore: () => DependenciesStore<D>;\n\n // Clone support (issue #173, consolidated #964). One accessor for the\n // source-side snapshot a clone carries over besides the route store, so a new\n // clone-relevant subsystem is wired in a single place instead of being spread\n // across separate methods.\n readonly getCloneState: () => {\n options: Options<D>;\n dependencies: Record<string, unknown>;\n pluginFactories: PluginFactory<D>[];\n // Resolved logger config of the base router, so a clone can build its OWN\n // logger inheriting the base's level/callback. Frozen `options` do NOT carry\n // `logger` (stripped in the constructor), so `options` above can't convey it;\n // cloneRouter merges a per-request override (traceId) over this snapshot.\n loggerConfig: LoggerConfig;\n };\n\n // Consolidated route data store (issue #174 Phase 2)\n readonly routeGetStore: () => RoutesStore<D>;\n\n // Cross-namespace state (issue #174)\n readonly getStateName: () => string | undefined;\n readonly isTransitioning: () => boolean;\n readonly clearState: () => void;\n readonly setState: (state: State) => void;\n readonly routerExtensions: { keys: string[] }[];\n readonly contextClaimRecords: Set<string>;\n\n /**\n * One-shot hydration scratchpad populated by `hydrateRouter` immediately\n * before delegating to `router.start(parsed.path)` and cleared in the\n * matching `finally`. SSR loader plugins read this slot directly via\n * `getInternals(router).hydrationState` to short-circuit their own loader\n * call when the server-resolved namespace value is already present in the\n * parsed state (#596). `null` outside of an active `hydrateRouter`\n * invocation.\n */\n hydrationState: SerializedRouterState | null;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- existential type: stores RouterInternals for all Dependencies types\nconst internals = new WeakMap<object, RouterInternals<any>>();\n\nexport function getInternals<D extends DefaultDependencies>(\n router: RouterInterface<D>,\n): RouterInternals<D> {\n const ctx = internals.get(router);\n\n if (!ctx) {\n throw new TypeError(\n \"[real-router] Invalid router instance — not found in internals registry\",\n );\n }\n\n return ctx as RouterInternals<D>;\n}\n\n/**\n * Channel guard, position P1 (#1572) — the caller's RAW `params` argument, at\n * the API boundary and BEFORE any interceptor runs, so what it reports is what\n * the CALLER wrote (a plugin's later injection is P2's population, not this one).\n *\n * THROWS. The warn-first step (#1572) announced the contract so every call site\n * could identify itself in the logs; this is the promotion it announced.\n *\n * A `TypeError`, synchronous, rather than a `RouterError` on a rejected promise:\n * this is an ARGUMENT-shape defect at the API boundary, caught before any\n * interceptor or transition exists — the same class as the `subscribe` /\n * `navigateToNotFound` / `start` guards beside it. Rejecting instead would let a\n * `.catch()` written for navigation failures swallow a programming error.\n *\n * P3 (`navigateToState`) keeps REJECTING — deliberately asymmetric, because it\n * takes a ready-made `State` from a popstate handler, where a new synchronous\n * throw would change an existing method's failure shape.\n *\n * The predicates (`buildPath` / `isActiveRoute` / `canNavigateTo`) are still NOT\n * instrumented: they run on every `<Link>` render, an answer there is read\n * immediately and corrupts nothing, and throwing inside a render in six adapters\n * is not a trade this guard is worth.\n *\n * ⚠ Not instrumented ≠ blind. `canNavigateTo` answers whether `navigate` WOULD\n * work, so it consults {@link findMisChanneledKey} directly and returns `false`\n * for a shape this function would have thrown on (#1576) — an answer, not a\n * throw, so the render-path trade above is untouched. `buildPath` /\n * `isActiveRoute` ask a different question and are unchanged.\n *\n * @internal\n */\nexport function throwOnMisChanneledKey<D extends DefaultDependencies>(\n ctx: RouterInternals<D>,\n method: string,\n routeName: string,\n params: Params | undefined,\n): void {\n assertChannelCorrect(\n method,\n routeName,\n params,\n ctx.getQueryParams(routeName),\n );\n}\n\nexport function registerInternals<D extends DefaultDependencies>(\n router: RouterClass<D>,\n ctx: RouterInternals<D>,\n): void {\n internals.set(router, ctx);\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument -- internal chain execution: type safety enforced at public API boundary (PluginApi.addInterceptor) */\nfunction executeInterceptorChain<T>(\n interceptors: ((next: (...args: any[]) => any, ...args: any[]) => any)[],\n original: (...args: any[]) => T,\n args: any[],\n): T {\n let chain = original as (...args: any[]) => any;\n\n for (const interceptor of interceptors) {\n const prev = chain;\n\n chain = (...chainArgs: any[]) => interceptor(prev, ...chainArgs);\n }\n\n return chain(...args) as T;\n}\n\n/**\n * Variadic interceptor wrapper — wraps a function of any arity, returning the\n * same callable type `T`. Use {@link createTernaryInterceptable} instead when\n * the wrapped method takes exactly three args and the caller needs the precise\n * `(a, b, c) => r` signature preserved (the variadic form widens args to\n * `any[]`).\n */\nexport function createInterceptable<T extends (...args: any[]) => any>(\n name: string,\n original: T,\n interceptors: Map<\n string,\n ((next: (...args: any[]) => any, ...args: any[]) => any)[]\n >,\n): T {\n return ((...args: any[]) => {\n const chain = interceptors.get(name);\n\n if (!chain || chain.length === 0) {\n return original(...args);\n }\n\n return executeInterceptorChain(chain, original, args);\n }) as T;\n}\n\n/**\n * Three-argument interceptor wrapper — preserves the exact\n * `(a: A, b: B, c: C) => R` signature that the variadic\n * {@link createInterceptable} widens to `any[]`. Backs both search-aware\n * interceptables — `buildPath(route, params, search)` and\n * `forwardState(name, params, search)` (RFC-4 M2 / #1548). Every first-party\n * plugin registers the full three-argument form; a shorter-arity interceptor\n * from a third party remains type-valid (TS allows fewer params, and `next(a,\n * b)` leaves the third arg `undefined`).\n */\nexport function createTernaryInterceptable<A, B, C, R>(\n name: string,\n original: (a: A, b: B, c: C) => R,\n interceptors: Map<\n string,\n ((next: (...args: any[]) => any, ...args: any[]) => any)[]\n >,\n): (a: A, b: B, c: C) => R {\n return (arg1: A, arg2: B, arg3: C) => {\n const chain = interceptors.get(name);\n\n if (!chain || chain.length === 0) {\n return original(arg1, arg2, arg3);\n }\n\n return executeInterceptorChain(chain, original, [arg1, arg2, arg3]);\n };\n}\n/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */\n","/**\n * Canonical route-segment tokenizer.\n *\n * The single owner of \"where does a name / marker / constraint end\" for ONE\n * path segment (post-`/`-split). It replaces the five name-boundary compositions\n * of `PARAM_NAME_PATTERN` currently spread across `buildParamMeta` (L1),\n * `registration` (L2 build + L3 trie), and `route-tree`'s validation gate (L4),\n * so those layers can never disagree on a boundary (the gate↔backstop drift\n * class — #858 / #1050 / #1150 / #1311 / #1149 / #1324).\n *\n * A single left-to-right `charCodeAt` scan produces either a token tuple or a\n * typed error. Grammar — **3 tokens only** (`static | :param | *splat`); the\n * grammar has no optional `:x?` or `<re>` constraint forms. Any `<`/`>` or a\n * post-name `?` in the path is a *registration error* carrying a replacement\n * recipe (`optional-removed` / `constraint-removed`), not a token:\n * 1. Leading `:`/`*` → param/splat; otherwise `static` (a marker glued *after* a\n * static prefix ⇒ `fused-marker`; any `<`/`>` (a former constraint) ⇒\n * `constraint-removed`; a trailing `?` on a marker-less segment ⇒ `name-less`\n * — the modifier has no param name, #1241 / `/faq?`).\n * 2. name = any char except `<`/`?` (no `/` remains inside a segment); a name\n * ending in a bare `:`/`*` ⇒ `trailing-marker` (#1324). A *mid* marker stays\n * a name char — `:a:b` → name `a:b`, preserved.\n * 3. empty name ⇒ `name-less` (#858).\n * 4. a `<` after the name (a former `<re>` constraint) ⇒ `constraint-removed`.\n * 5. a post-name `?` (a former optional modifier, on `:param` or `*splat`) ⇒\n * `optional-removed`.\n *\n * @module parseSegment\n */\n\n/* eslint-disable unicorn/prefer-code-point, unicorn/prefer-includes-over-repeated-comparisons, sonarjs/cognitive-complexity -- charCodeAt code-unit scan + a single inlined branchy pass are this RFC's char-scan perf basis (§9); the same deliberate choices as registration/trie.ts hasNonAsciiSegment (#1285) and SegmentMatcher's inlined #traverseFrom. A `[LT,GT,QUESTION].includes(code)` boundary check would allocate an array literal per scanned char. Markers compared are ASCII (`:` `*` `<` `>` `?`, < 0x80). */\n\n/** A successfully tokenized segment (3-token grammar: `static | :param | *splat`). */\nexport type SegmentTokens =\n | { readonly kind: \"static\"; readonly text: string }\n | { readonly kind: \"param\"; readonly name: string }\n | { readonly kind: \"splat\"; readonly name: string };\n\n/** Grammar-shape rejections, each mirroring a registration guard. */\nexport type SegmentErrorCode =\n | \"name-less\" // #858 — a marker with no name\n | \"trailing-marker\" // #1324 — a param name ending in a bare `:`/`*`\n | \"fused-marker\" // #1050 — a marker glued after a static prefix\n | \"optional-removed\" // M1 — a `:x?`/`*x?` optional modifier (removed; two sibling routes)\n | \"constraint-removed\"; // M1 — a `<re>` constraint or stray `<`/`>` (removed; validate in a guard)\n\nexport interface SegmentError {\n readonly error: SegmentErrorCode;\n}\n\nconst COLON = 58; // :\nconst STAR = 42; // *\nconst LT = 60; // <\nconst GT = 62; // >\nconst QUESTION = 63; // ?\nconst SLASH = 47; // /\n\nconst isMarker = (code: number): boolean => code === COLON || code === STAR;\n\n/**\n * Splits a path into its `/`-delimited segments. A plain `/`-split (M1): the\n * 3-token grammar has no `<...>` constraint whose body could legally contain a\n * `/`, so no constraint-awareness is needed — a stray `<`/`>` is a\n * `constraint-removed` error, caught per segment by `parseSegment`. This is the\n * **segmentation** half of the path-grammar unification: `parseSegment` owns the\n * per-segment grammar, `splitPathSegments` owns where a segment begins and ends.\n *\n * @param path - a route path (query already stripped by the caller)\n * @returns the segments in order, including empty leading/trailing/`//` segments\n * (the caller skips empties, matching the current behaviour)\n */\nexport function splitPathSegments(path: string): string[] {\n const segments: string[] = [];\n let start = 0;\n\n for (let i = 0; i < path.length; i += 1) {\n if (path.charCodeAt(i) !== SLASH) {\n continue;\n }\n\n segments.push(path.slice(start, i));\n start = i + 1;\n }\n\n segments.push(path.slice(start));\n\n return segments;\n}\n\n/**\n * Tokenizes a single path segment (already split on `/`).\n *\n * @param segment - one `/`-delimited segment of a route path\n * @returns the segment's tokens, or a typed grammar error\n */\nexport function parseSegment(segment: string): SegmentTokens | SegmentError {\n const length = segment.length;\n\n if (length === 0) {\n return { kind: \"static\", text: \"\" };\n }\n\n // ---- static segment (no leading marker) -------------------------------\n if (!isMarker(segment.charCodeAt(0))) {\n for (let i = 0; i < length; i += 1) {\n const code = segment.charCodeAt(i);\n\n // A `<`/`>` (a former `<re>` constraint or a stray delimiter) is no longer\n // grammar — M1 removed constraints. Reject with the constraint recipe.\n if (code === LT || code === GT) {\n return { error: \"constraint-removed\" };\n }\n\n // A marker glued after a static prefix is extracted as a param by build/meta\n // but compiled as a static literal by the trie (#1050) — reject it as fused.\n // A marker ENDING the segment (a static ending in `:`/`*` — `/a:`, `/a*`, F2)\n // is NOT fused: caught by `i + 1 < length` being false. Every other following\n // char is fused — including a `?` (`a:?`): that shape never reaches the\n // tokenizer through a real path (a `?` after a bare marker is not a valid\n // `:name?` form, so the query mask strips it before `/`-segmentation), so a\n // direct call correctly reports fused-marker. (`a<`/`a>` already returned\n // `constraint-removed` above, so no `<`-follows exception is needed here.)\n if (isMarker(code) && i + 1 < length) {\n return { error: \"fused-marker\" };\n }\n }\n\n // A trailing `?` is a former optional modifier; on a marker-less segment (no\n // param name) it is a modifier-with-no-name — name-less (#858/#1241, `/faq?`),\n // NOT `optional-removed` (there is no param to route to two siblings). The\n // backstop rejects it by the SAME rule: its `endsWith(\"?\")` fork routes the\n // segment to `extractParamName` → this tokenizer. Owning the `?` here (not\n // only in the marker branch) is what lets the gate and backstop agree on it —\n // otherwise the gate reads `faq?` as a valid static (#1324 §4).\n if (segment.charCodeAt(length - 1) === QUESTION) {\n return { error: \"name-less\" };\n }\n\n return { kind: \"static\", text: segment };\n }\n\n const splat = segment.charCodeAt(0) === STAR;\n\n // ---- name: up to the first `<`/`>` (former constraint delimiter, reserved —\n // В1.3) or `?` (former optional). A segment holds no `/`. -----------------\n let cursor = 1;\n\n while (cursor < length) {\n const code = segment.charCodeAt(cursor);\n\n if (code === LT || code === GT || code === QUESTION) {\n break;\n }\n\n cursor += 1;\n }\n\n const name = segment.slice(1, cursor);\n\n if (name.length === 0) {\n return { error: \"name-less\" }; // #858\n }\n\n if (isMarker(name.charCodeAt(name.length - 1))) {\n return { error: \"trailing-marker\" }; // #1324\n }\n\n // ---- former constraint / optional modifiers (removed in M1) ------------\n // The name scan stops at the first `<`/`>` or `?`. Either is a form removed\n // in M1: a `<re>` constraint (also a stray `<`/`>` — В1.3), or a `:x?`/`*x?`\n // optional. Only `?` is the optional; `<`/`>` are the constraint recipe.\n // Reject with the matching replacement recipe rather than tokenize it.\n if (cursor < length) {\n return segment.charCodeAt(cursor) === QUESTION\n ? { error: \"optional-removed\" }\n : { error: \"constraint-removed\" }; // LT or GT\n }\n\n return splat ? { kind: \"splat\", name } : { kind: \"param\", name };\n}\n\n/**\n * Returns the first per-segment grammar error in a path, or `undefined` if every\n * segment tokenizes cleanly.\n *\n * The **validation-facing** entry over the tokenizer: `route-tree`'s\n * `validateRoutePath` calls this instead of re-running its own split+parse loop,\n * so the gate and the matcher's own grammar cannot drift (#1324) and the loop\n * stays single-sourced here — the tokenizer primitives (`parseSegment`,\n * `splitPathSegments`) need not leak into the package's public surface. An empty\n * segment tokenizes as `static` (never an error), so leading/trailing/`//`\n * empties are skipped naturally.\n *\n * @param path - a route path (query already stripped by the caller)\n * @returns the first `SegmentErrorCode` (scanned left to right), or `undefined`\n */\nexport function findSegmentGrammarError(\n path: string,\n): SegmentErrorCode | undefined {\n for (const segment of splitPathSegments(path)) {\n const token = parseSegment(segment);\n\n if (\"error\" in token) {\n return token.error;\n }\n }\n\n return undefined;\n}\n\n/** A removed-form (M1) match, describing the offending segment and — for an\n * optional — the two sibling paths that replace it (path without the optional\n * segment + path with the param made required). The route-tree gate uses this to\n * build a route-contextual replacement recipe; the matcher backstop uses only the\n * error code (a shorter, path-free recipe). */\nexport type RemovedForm =\n | {\n readonly code: \"optional-removed\";\n readonly segment: string;\n readonly withoutSegment: string;\n readonly requiredForm: string;\n }\n | { readonly code: \"constraint-removed\"; readonly segment: string };\n\n/**\n * The rich (route-tree gate) view over the tokenizer for a removed form: finds\n * the first `optional-removed` / `constraint-removed` segment and, for an\n * optional, computes its two replacement sibling paths from the ACTUAL path\n * (dropping the segment → without-form; dropping the trailing `?` → required\n * form). Returns `undefined` if no removed form is present (the gate then uses\n * `findSegmentGrammarError` for a surviving grammar rejection).\n *\n * @param path - a route path (query already stripped by the caller)\n */\nexport function describeRemovedForm(path: string): RemovedForm | undefined {\n const segments = splitPathSegments(path);\n\n for (let i = 0; i < segments.length; i += 1) {\n const token = parseSegment(segments[i]);\n\n if (!(\"error\" in token)) {\n continue;\n }\n\n // First error wins (mirrors `findSegmentGrammarError`): describe it ONLY if\n // it is a removed form, else return undefined so the caller falls to the\n // surviving-code message — this keeps the gate's reason in lockstep with the\n // matcher backstop's first-error verdict.\n if (token.error === \"optional-removed\") {\n const segment = segments[i];\n const required = [...segments];\n\n // Drop the `?` optional modifier AND everything after it (the tokenizer\n // stopped the name at the first `?`, so it is the modifier). Using the `?`\n // index — not a blind `slice(0, -1)` — keeps the required sibling VALID for\n // a reverse/compound form whose `?` is not the last char: `:b?<x>` → `:b`\n // (not `:b?<x`), `:id??` → `:id` (not `:id?`). #1516\n required[i] = segment.slice(0, segment.indexOf(\"?\"));\n\n return {\n code: \"optional-removed\",\n segment,\n withoutSegment: segments.filter((_, j) => j !== i).join(\"/\"),\n requiredForm: required.join(\"/\"),\n };\n }\n\n return token.error === \"constraint-removed\"\n ? { code: \"constraint-removed\", segment: segments[i] }\n : undefined;\n }\n\n return undefined;\n}\n","/**\n * Route Parameter Metadata Extraction.\n *\n * Extracts parameter metadata from route path patterns without requiring\n * a full path-parser instance. Replaces parser.urlParams/queryParams/spatParams.\n *\n * @module buildParamMeta\n */\n\nimport { parseSegment, splitPathSegments } from \"./parseSegment\";\n\nimport type { ParamMeta } from \"./types\";\n\n/**\n * A query-param NAME may not contain `<`/`>` (#1242 §5.1) — a constraint\n * delimiter leaked into the query via a reverse-order modifier typo (`/a/:b?<c>`\n * parses the `?` as the query start, making `<c>` the query name). Consumed by\n * the route-tree gate and the `registerTree` backstop; relocated here from the\n * deleted `constraint-grammar.ts` when M1 removed constraints (query-param name\n * validation is a query concern, and this module owns query extraction).\n */\nexport const INVALID_QUERY_NAME_RGX = /[<>]/u;\n\nconst QUESTION = 0x3f; // ?\nconst SLASH = 0x2f; // /\nconst LT = 0x3c; // <\n\n/**\n * Locates the query separator `?` in a route path — the FIRST `?` whose tail is\n * non-empty and does not begin with `/`, `?`, or `<` (M1 §3.3). The 3-token\n * grammar leaves `?` a single role (there is no optional modifier and no\n * constraint body to hide one), so no length-preserving mask is needed. The three\n * excluded tails keep a REMOVED form in the path part, where `parseSegment`\n * rejects it with a recipe instead of mis-reading it as a query declaration:\n * - end-of-string (`/:id?`) and `/` (`/:id?/edit`) → a bare `:x?` optional;\n * - `?` (`/:id??tab`) → the leading `?` is the optional, the later `?` the query;\n * - `<` (`/a/:b?<x>`) → a reverse-order `:b?<x>` (optional then a former constraint).\n *\n * @param path - a route path\n * @returns the index of the query separator, or -1 if there is none\n */\nfunction findQuerySeparator(path: string): number {\n for (let i = 0; i < path.length; i += 1) {\n if (path.codePointAt(i) !== QUESTION) {\n continue;\n }\n\n // `next` is the code point after the `?`, or the `-1` sentinel at end-of-string.\n // The `-1` sentinel is the SOLE end-of-string guard — the former separate\n // `next !== undefined` conjunct was dead (the ternary bounds the index, so\n // `codePointAt` never returns `undefined`; the `!` is a type assertion, not a\n // runtime branch, so it keeps the scan at 100% coverage). Mirrors `#scanPath`.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ternary-bounded in-range index; codePointAt is defined\n const next = i + 1 < path.length ? path.codePointAt(i + 1)! : -1;\n\n if (next !== -1 && next !== SLASH && next !== QUESTION && next !== LT) {\n return i;\n }\n }\n\n return -1;\n}\n\n/**\n * Builds parameter metadata from a route path pattern.\n *\n * Extracts URL parameters, query parameters, and splat parameters\n * from the path pattern string.\n *\n * @param path - Route path pattern (e.g., \"/users/:id/posts/:postId?q\")\n * @returns Parameter metadata object\n *\n * @example\n * ```typescript\n * buildParamMeta(\"/users/:id\")\n * // → { urlParams: [\"id\"], queryParams: [], spatParams: [], paramTypeMap: { id: \"url\" } }\n *\n * buildParamMeta(\"/search?q&page\")\n * // → { urlParams: [], queryParams: [\"q\", \"page\"], spatParams: [],\n * // paramTypeMap: { q: \"query\", page: \"query\" } }\n *\n * buildParamMeta(\"/files/*path\")\n * // → { urlParams: [\"path\"], queryParams: [], spatParams: [\"path\"],\n * // paramTypeMap: { path: \"url\" } }\n * ```\n */\n// Shared frozen sentinels for the common no-params case — avoid a fresh empty\n// array/object per route (#1009). ParamMeta fields are Readonly*; match/build\n// only read them, and computeCaches' Object.freeze on the arrays/object is a\n// no-op on an already-frozen shared instance.\nconst EMPTY_PARAM_NAMES: readonly string[] = Object.freeze([]);\nconst EMPTY_PARAM_TYPE_MAP: Readonly<Record<string, \"url\" | \"query\">> =\n Object.freeze({});\n\n// Whole-meta shared sentinel for the fully-static case: every collection is a\n// #1009 sentinel AND pathPattern degenerates to the input path itself (no query\n// to strip), so the wrapper carries zero per-route information. The RETAINING\n// caller (route-tree's computeCaches) swaps a matching fresh result for this\n// instance — buildParamMeta itself keeps returning fresh objects so the\n// validation gate can read the real pathPattern of arbitrary input paths.\n// `pathPattern` is \"\" here; the one stored-meta reader (`registerNode`) falls\n// back to `node.path` on identity match.\nexport const EMPTY_PARAM_META: ParamMeta = Object.freeze({\n urlParams: EMPTY_PARAM_NAMES,\n queryParams: EMPTY_PARAM_NAMES,\n spatParams: EMPTY_PARAM_NAMES,\n paramTypeMap: EMPTY_PARAM_TYPE_MAP,\n pathPattern: \"\",\n});\n\n/**\n * Extracts URL/splat params from a path's segments into the given accumulators\n * via the canonical `parseSegment` tokenizer. Split out of `buildParamMeta` so\n * the builder stays under the cognitive-complexity budget. A malformed segment\n * (token errors) or a `static` segment contributes nothing — a malformed route is\n * rejected downstream before it compiles, so its meta is moot.\n */\nfunction collectUrlParams(\n path: string,\n urlParams: string[],\n spatParams: string[],\n paramTypeMap: Record<string, \"url\" | \"query\">,\n): void {\n for (const segment of splitPathSegments(path)) {\n if (segment.length === 0) {\n continue;\n }\n\n const token = parseSegment(segment);\n\n if (\"error\" in token || token.kind === \"static\") {\n continue;\n }\n\n urlParams.push(token.name);\n paramTypeMap[token.name] = \"url\";\n\n if (token.kind === \"splat\") {\n spatParams.push(token.name);\n }\n }\n}\n\nexport function buildParamMeta(path: string): ParamMeta {\n const urlParams: string[] = [];\n const queryParams: string[] = [];\n const spatParams: string[] = [];\n const paramTypeMap: Record<string, \"url\" | \"query\"> = {};\n\n // Locate the real query separator (M1 §3.3: first `?` whose tail is not a\n // former optional/reverse form).\n const separator = findQuerySeparator(path);\n\n if (separator !== -1) {\n const queryString = path.slice(separator + 1);\n const params = queryString.split(\"&\");\n\n for (const param of params) {\n const paramName = param.trim();\n\n if (paramName.length > 0) {\n queryParams.push(paramName);\n paramTypeMap[paramName] = \"query\";\n }\n }\n\n path = path.slice(0, separator);\n }\n\n collectUrlParams(path, urlParams, spatParams, paramTypeMap);\n\n return shareEmptyCollections(\n urlParams,\n queryParams,\n spatParams,\n paramTypeMap,\n path,\n );\n}\n\n// #1009: swap each freshly-built empty collection for a shared frozen sentinel\n// — factored out of buildParamMeta so the hot builder stays under the cognitive-\n// complexity budget. match/build only read these (Readonly*), and computeCaches'\n// Object.freeze is a no-op on an already-frozen shared instance.\nfunction shareEmptyCollections(\n urlParams: string[],\n queryParams: string[],\n spatParams: string[],\n paramTypeMap: Record<string, \"url\" | \"query\">,\n pathPattern: string,\n): ParamMeta {\n return {\n urlParams: urlParams.length === 0 ? EMPTY_PARAM_NAMES : urlParams,\n queryParams: queryParams.length === 0 ? EMPTY_PARAM_NAMES : queryParams,\n spatParams: spatParams.length === 0 ? EMPTY_PARAM_NAMES : spatParams,\n paramTypeMap:\n urlParams.length === 0 && queryParams.length === 0\n ? EMPTY_PARAM_TYPE_MAP\n : paramTypeMap,\n pathPattern,\n };\n}\n"],"mappings":"AA2BA,SAAgB,EACd,EACA,EACoB,CAChB,KAAW,SAAW,GAAK,IAAW,IAAA,GAI1C,IAAK,IAAM,KAAO,EAAY,CAC5B,GAAI,CAAC,OAAO,OAAO,EAAQ,CAAG,EAC5B,SAGF,IAAI,EAEJ,GAAI,CACF,EAAQ,EAAO,EACjB,MAAQ,CAQN,MACF,CAEA,GAAI,IAAU,IAAA,GACZ,OAAO,CAEX,CAGF,CAuBA,SAAgB,EACd,EACA,EACA,EACA,EACA,EACA,EACM,CACN,IAAM,EAAM,EAAoB,EAAQ,CAAU,EAElD,GAAI,IAAQ,IAAA,GACV,MAAU,UACR,WAAW,EAAO,IAAI,EACpB,EACA,EACA,OAAO,GAAW,WAAa,EAAO,EAAI,EAC1C,CACF,GACF,CAEJ,CAWA,SAAgB,EACd,EACA,EACA,EAAS,wBACT,EAAS,8BACD,CACR,MAAO,UAAU,EAAU,eAAe,EAAI,2CAA2C,EAAO,uBAAuB,EAAO,8FAChI,CCqGA,MAAM,EAAY,IAAI,QAEtB,SAAgB,EACd,EACoB,CACpB,IAAM,EAAM,EAAU,IAAI,CAAM,EAEhC,GAAI,CAAC,EACH,MAAU,UACR,yEACF,EAGF,OAAO,CACT,CAiCA,SAAgB,EACd,EACA,EACA,EACA,EACM,CACN,EACE,EACA,EACA,EACA,EAAI,eAAe,CAAS,CAC9B,CACF,CAEA,SAAgB,EACd,EACA,EACM,CACN,EAAU,IAAI,EAAQ,CAAG,CAC3B,CAGA,SAAS,EACP,EACA,EACA,EACG,CACH,IAAI,EAAQ,EAEZ,IAAK,IAAM,KAAe,EAAc,CACtC,IAAM,EAAO,EAEb,GAAS,GAAG,IAAqB,EAAY,EAAM,GAAG,CAAS,CACjE,CAEA,OAAO,EAAM,GAAG,CAAI,CACtB,CASA,SAAgB,EACd,EACA,EACA,EAIG,CACH,QAAS,GAAG,IAAgB,CAC1B,IAAM,EAAQ,EAAa,IAAI,CAAI,EAMnC,MAJI,CAAC,GAAS,EAAM,SAAW,EACtB,EAAS,GAAG,CAAI,EAGlB,EAAwB,EAAO,EAAU,CAAI,CACtD,EACF,CAYA,SAAgB,EACd,EACA,EACA,EAIyB,CACzB,OAAQ,EAAS,EAAS,IAAY,CACpC,IAAM,EAAQ,EAAa,IAAI,CAAI,EAMnC,MAJI,CAAC,GAAS,EAAM,SAAW,EACtB,EAAS,EAAM,EAAM,CAAI,EAG3B,EAAwB,EAAO,EAAU,CAAC,EAAM,EAAM,CAAI,CAAC,CACpE,CACF,CCvTA,MAOM,EAAY,GAA0B,IAAS,IAAS,IAAS,GAcvE,SAAgB,EAAkB,EAAwB,CACxD,IAAM,EAAqB,CAAC,EACxB,EAAQ,EAEZ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,GAAK,EAChC,EAAK,WAAW,CAAC,IAAME,KAI3B,EAAS,KAAK,EAAK,MAAM,EAAO,CAAC,CAAC,EAClC,EAAQ,EAAI,GAKd,OAFA,EAAS,KAAK,EAAK,MAAM,CAAK,CAAC,EAExB,CACT,CAQA,SAAgB,EAAa,EAA+C,CAC1E,IAAM,EAAS,EAAQ,OAEvB,GAAI,IAAW,EACb,MAAO,CAAE,KAAM,SAAU,KAAM,EAAG,EAIpC,GAAI,CAAC,EAAS,EAAQ,WAAW,CAAC,CAAC,EAAG,CACpC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,GAAK,EAAG,CAClC,IAAM,EAAO,EAAQ,WAAW,CAAC,EAIjC,GAAI,IAASF,IAAM,IAAS,GAC1B,MAAO,CAAE,MAAO,oBAAqB,EAYvC,GAAI,EAAS,CAAI,GAAK,EAAI,EAAI,EAC5B,MAAO,CAAE,MAAO,cAAe,CAEnC,CAaA,OAJI,EAAQ,WAAW,EAAS,CAAC,IAAMC,GAC9B,CAAE,MAAO,WAAY,EAGvB,CAAE,KAAM,SAAU,KAAM,CAAQ,CACzC,CAEA,IAAM,EAAQ,EAAQ,WAAW,CAAC,IAAM,GAIpC,EAAS,EAEb,KAAO,EAAS,GAAQ,CACtB,IAAM,EAAO,EAAQ,WAAW,CAAM,EAEtC,GAAI,IAASD,IAAM,IAAS,IAAM,IAASC,GACzC,MAGF,GAAU,CACZ,CAEA,IAAM,EAAO,EAAQ,MAAM,EAAG,CAAM,EAqBpC,OAnBI,EAAK,SAAW,EACX,CAAE,MAAO,WAAY,EAG1B,EAAS,EAAK,WAAW,EAAK,OAAS,CAAC,CAAC,EACpC,CAAE,MAAO,iBAAkB,EAQhC,EAAS,EACJ,EAAQ,WAAW,CAAM,IAAMA,GAClC,CAAE,MAAO,kBAAmB,EAC5B,CAAE,MAAO,oBAAqB,EAG7B,EAAQ,CAAE,KAAM,QAAS,MAAK,EAAI,CAAE,KAAM,QAAS,MAAK,CACjE,CAiBA,SAAgB,EACd,EAC8B,CAC9B,IAAK,IAAM,KAAW,EAAkB,CAAI,EAAG,CAC7C,IAAM,EAAQ,EAAa,CAAO,EAElC,GAAI,UAAW,EACb,OAAO,EAAM,KAEjB,CAGF,CA0BA,SAAgB,EAAoB,EAAuC,CACzE,IAAM,EAAW,EAAkB,CAAI,EAEvC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,GAAK,EAAG,CAC3C,IAAM,EAAQ,EAAa,EAAS,EAAE,EAEhC,aAAW,EAQjB,IAAI,EAAM,QAAU,mBAAoB,CACtC,IAAM,EAAU,EAAS,GACnB,EAAW,CAAC,GAAG,CAAQ,EAS7B,MAFA,GAAS,GAAK,EAAQ,MAAM,EAAG,EAAQ,QAAQ,GAAG,CAAC,EAE5C,CACL,KAAM,mBACN,UACA,eAAgB,EAAS,QAAQ,EAAG,IAAM,IAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAC3D,aAAc,EAAS,KAAK,GAAG,CACjC,CACF,CAEA,OAAO,EAAM,QAAU,qBACnB,CAAE,KAAM,qBAAsB,QAAS,EAAS,EAAG,EACnD,IAAA,EAJJ,CAKF,CAGF,CC5PA,MAAa,EAAyB,QAoBtC,SAAS,EAAmB,EAAsB,CAChD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,GAAK,EAAG,CACvC,GAAI,EAAK,YAAY,CAAC,IAAM,GAC1B,SASF,IAAM,EAAO,EAAI,EAAI,EAAK,OAAS,EAAK,YAAY,EAAI,CAAC,EAAK,GAE9D,GAAI,IAAS,IAAM,IAAS,IAAS,IAAS,IAAY,IAAS,GACjE,OAAO,CAEX,CAEA,MAAO,EACT,CA6BA,MAAM,EAAuC,OAAO,OAAO,CAAC,CAAC,EACvD,EACJ,OAAO,OAAO,CAAC,CAAC,EAUL,EAA8B,OAAO,OAAO,CACvD,UAAW,EACX,YAAa,EACb,WAAY,EACZ,aAAc,EACd,YAAa,EACf,CAAC,EASD,SAAS,EACP,EACA,EACA,EACA,EACM,CACN,IAAK,IAAM,KAAW,EAAkB,CAAI,EAAG,CAC7C,GAAI,EAAQ,SAAW,EACrB,SAGF,IAAM,EAAQ,EAAa,CAAO,EAE9B,UAAW,GAAS,EAAM,OAAS,WAIvC,EAAU,KAAK,EAAM,IAAI,EACzB,EAAa,EAAM,MAAQ,MAEvB,EAAM,OAAS,SACjB,EAAW,KAAK,EAAM,IAAI,EAE9B,CACF,CAEA,SAAgB,EAAe,EAAyB,CACtD,IAAM,EAAsB,CAAC,EACvB,EAAwB,CAAC,EACzB,EAAuB,CAAC,EACxB,EAAgD,CAAC,EAIjD,EAAY,EAAmB,CAAI,EAEzC,GAAI,IAAc,GAAI,CAEpB,IAAM,EADc,EAAK,MAAM,EAAY,CAClB,CAAC,CAAC,MAAM,GAAG,EAEpC,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAY,EAAM,KAAK,EAEzB,EAAU,OAAS,IACrB,EAAY,KAAK,CAAS,EAC1B,EAAa,GAAa,QAE9B,CAEA,EAAO,EAAK,MAAM,EAAG,CAAS,CAChC,CAIA,OAFA,EAAiB,EAAM,EAAW,EAAY,CAAY,EAEnD,EACL,EACA,EACA,EACA,EACA,CACF,CACF,CAMA,SAAS,EACP,EACA,EACA,EACA,EACA,EACW,CACX,MAAO,CACL,UAAW,EAAU,SAAW,EAAI,EAAoB,EACxD,YAAa,EAAY,SAAW,EAAI,EAAoB,EAC5D,WAAY,EAAW,SAAW,EAAI,EAAoB,EAC1D,aACE,EAAU,SAAW,GAAK,EAAY,SAAW,EAC7C,EACA,EACN,aACF,CACF"}
|
|
1
|
+
{"version":3,"file":"buildParamMeta-D8td47fL.mjs","names":["LT","QUESTION","SLASH"],"sources":["../../src/channels/guard.ts","../../src/internals.ts","../../src/engine/path-matcher/parseSegment.ts","../../src/engine/path-matcher/buildParamMeta.ts"],"sourcesContent":["// packages/core/src/channels/guard.ts\n\nimport type { Params } from \"../types\";\n\n/**\n * THE predicate of the always-on channel guard: the first key the caller put in\n * the PATH bag while the route declares it as a QUERY param, or `undefined`\n * when the bag is channel-correct.\n *\n * A DETECTOR, not a normaliser — the key is never moved. Moving it is what\n * `separateChannels` (stage ②) used to do — a function that no longer exists.\n * Channel-correctness is the producer's contract now, not a repair the pipeline\n * performs behind everyone's back.\n *\n * Scans `queryNames` (a route's declared query names — small, cached) rather\n * than the bag, so there is no `Object.keys` allocation, and short-circuits on\n * a route with no query declarations, which is the common case.\n *\n * `undefined` is absence on both sides (#1550 / #1551), so an\n * `undefined`-valued key is NOT a mis-channel: it is the documented removal\n * marker `persistent-params` relies on, and it never reaches a built state\n * anyway. A name that also occupies a path slot (`/items/:id?id`) is absent\n * from `queryNames` by construction (#843 / #1549 carve-out), so the collision\n * form is legitimately path-owned and passes.\n *\n * @internal\n */\nexport function findMisChanneledKey(\n params: Params | undefined,\n queryNames: readonly string[],\n): string | undefined {\n if (queryNames.length === 0 || params === undefined) {\n return undefined;\n }\n\n for (const key of queryNames) {\n if (!Object.hasOwn(params, key)) {\n continue;\n }\n\n let value: unknown;\n\n try {\n value = params[key];\n } catch {\n // A DIAGNOSTIC must never become the thing that throws. The bag may be\n // backed by accessors (a Proxy, a getter, a framework's reactive object),\n // and reading one here happens EARLIER than any consumer would have read\n // it — so an accessor that throws would surface from the guard instead of\n // from the code that actually needed the value, moving the origin of an\n // existing failure. Treat it as \"nothing to report\" and let the real\n // consumer hit the same accessor exactly as it did before.\n return undefined;\n }\n\n if (value !== undefined) {\n return key;\n }\n }\n\n return undefined;\n}\n\n/**\n * THE centralized channel check — the single place a mis-channelled bag is\n * refused, wherever it came from.\n *\n * Replaces the repair `separateChannels` (stage ②, since deleted) used to\n * perform at the `forwardState` seam. A key the route declares with `?`, sitting in the PATH\n * bag, is a producer's mistake — the producer named the route, so it knows the\n * declaration — and the router now says so instead of quietly moving the field\n * into the other object. Moving it was invisible: the caller kept believing\n * their bag was the one that shipped, and two producers of the SAME intent\n * could disagree about which channel a key ended up in.\n *\n * `source` names WHOSE bag is wrong, which is the whole diagnostic value at a\n * seam: the caller's argument, a `forwardState` interceptor's return, or the\n * output of a route's own `decodeParams`. It takes a THUNK as well as a string\n * because the seam sits on the navigation hot path — a source that has to be\n * composed (naming the route a chain forwarded from) must not build its string\n * on every call just to discard it on the 99.99% of calls that pass.\n *\n * @internal\n */\nexport function assertChannelCorrect(\n method: string,\n routeName: string,\n params: Params | undefined,\n queryNames: readonly string[],\n source?: string | (() => string),\n remedy?: string,\n): void {\n const key = findMisChanneledKey(params, queryNames);\n\n if (key !== undefined) {\n throw new TypeError(\n `[router.${method}] ${misChanneledKeyMessage(\n routeName,\n key,\n typeof source === \"function\" ? source() : source,\n remedy,\n )}`,\n );\n }\n}\n\n/**\n * The guard's actionable message. One builder for every position, so the\n * wording a user sees does not depend on which door they came through — the\n * facade's `TypeError`, the seam's, the decoder's, and `navigateToState`'s\n * `RouterError(WRONG_CHANNEL)`, which needs the wording WITHOUT the throw and is\n * why this is a separate function from {@link assertChannelCorrect}.\n *\n * @internal\n */\nexport function misChanneledKeyMessage(\n routeName: string,\n key: string,\n source = \"the `params` argument\",\n remedy = \"Pass it in `search` instead\",\n): string {\n return `Route \"${routeName}\" declares \\`${key}\\` as a query param, but it was given in ${source} — the path channel. ${remedy}; the two channels are separate since RFC-4 M2 and the router never moves a key between them.`;\n}\n","import { assertChannelCorrect } from \"./channels\";\n\nimport type { RouteTree } from \"./engine\";\nimport type { DependenciesStore } from \"./namespaces\";\nimport type { RoutesStore } from \"./namespaces/RoutesNamespace\";\nimport type { RouteResolver } from \"./pipeline\";\nimport type { Router as RouterClass } from \"./Router\";\nimport type {\n AnyOptions,\n DefaultDependencies,\n EventName,\n LoggerConfig,\n NavigationOptions,\n Options,\n Params,\n Plugin,\n Router as RouterInterface,\n RouterLogger,\n RouteTreeState,\n SearchParams,\n SerializedRouterState,\n SimpleState,\n State,\n TreeChangedEvent,\n Unsubscribe,\n EventMethodMap,\n PluginFactory,\n} from \"./types\";\nimport type { RouterValidator } from \"./types/RouterValidator\";\n\nexport interface RouterInternals<\n D extends DefaultDependencies = DefaultDependencies,\n> {\n readonly makeState: <\n P extends Params = Params,\n S extends SearchParams = SearchParams,\n >(\n name: string,\n params?: P,\n search?: S,\n path?: string,\n ) => State<P, S>;\n\n /**\n * Per-segment param-source map for a route name (`{ segment: { param: \"url\" |\n * \"query\" } }`), read from the live matcher — the ownership channel for\n * `getTransitionPath` (RFC-4 M2 / #1548, replaced the removed per-State\n * `stateMetaStore` WeakMap). `undefined` when the name is not in the tree.\n */\n readonly getMetaForState: (\n name: string,\n ) => Record<string, Record<string, \"url\" | \"query\">> | undefined;\n\n /**\n * The route's DECLARED query-param names — the same registry the URL build\n * prints from (#1556), minus path slots. Feeds the always-on channel guard\n * (#1572); read here rather than re-derived, so classification cannot drift.\n */\n readonly getQueryParams: (name: string) => readonly string[];\n\n readonly forwardState: <\n P extends Params = Params,\n S extends SearchParams = SearchParams,\n >(\n routeName: string,\n routeParams: P,\n routeSearch?: S,\n ) => SimpleState<P, S>;\n\n readonly buildStateResolved: (\n resolvedName: string,\n resolvedParams: Params,\n ) => RouteTreeState | undefined;\n\n readonly matchPath: <P extends Params = Params>(\n path: string,\n options?: AnyOptions,\n ) => State<P> | undefined;\n\n readonly getOptions: () => Options<D>;\n\n readonly addEventListener: <E extends EventName>(\n eventName: E,\n cb: Plugin[EventMethodMap[E]],\n ) => Unsubscribe;\n\n /**\n * Route-tree mutation channel — internal access for the `getRoutesApi`\n * wrapper. A dedicated bridge is required because the public\n * `addEventListener<E extends EventName>` structurally rejects\n * `\"TREE_CHANGED\"` (it is not in the public `EventName` union), is strict on\n * duplicates, and exposes neither `emit` nor `listenerCount`.\n */\n readonly treeChanged: {\n readonly emit: (event: TreeChangedEvent) => void;\n readonly subscribe: (\n handler: (event: TreeChangedEvent) => void,\n ) => Unsubscribe;\n readonly listenerCount: () => number;\n /**\n * True while a `TREE_CHANGED` emit is on the stack — `getRoutesApi` reads it\n * to reject reentrant route-CRUD from a `subscribeChanges` handler (#1032).\n */\n readonly isEmitting: () => boolean;\n };\n\n readonly buildPath: (\n route: string,\n params?: Params,\n search?: SearchParams,\n ) => string;\n\n /**\n * The navigation pipeline's read-model, for entry points that live on this\n * plugin-facing surface rather than in a namespace. Resolved LAZILY: the port\n * is created during wiring, and `registerInternals` runs before that, so the\n * accessor is a closure rather than a value — the same shape the interceptable\n * methods above already use.\n */\n readonly port: () => RouteResolver;\n\n readonly emitTransitionError: (error: Error) => void;\n\n /**\n * Emits `TRANSITION_SUCCESS` directly (no FSM transition) — used by\n * `getRoutesApi().replace()` to notify `router.subscribe` listeners when a\n * structural replace revalidates the active state (#950). Mirrors the success\n * emission `completeTransition` / `navigateToNotFound` perform.\n */\n readonly emitTransitionSuccess: (\n toState: State,\n fromState: State | undefined,\n opts?: NavigationOptions,\n ) => void;\n\n /**\n * Commits the not-found (`UNKNOWN_ROUTE`) state for `path` and emits\n * `TRANSITION_SUCCESS` — the `NavigationNamespace.navigateToNotFound`\n * primitive. `replace()` uses it when a structural replace drops the active\n * route, so subscribers are notified instead of the state silently clearing\n * (#950).\n */\n readonly navigateToNotFound: (path: string) => State;\n\n readonly start: (path: string) => Promise<State>;\n\n /**\n * Plugin-only navigation entry point — delegates to\n * `NavigationNamespace.navigateToState` (`getPluginApi(router).navigateToState`).\n * Hidden from `Router`/`Navigator` to keep the userland surface minimal;\n * see `core-types/src/api.ts` for usage docs.\n */\n readonly navigateToState: (\n state: State,\n options?: NavigationOptions,\n ) => Promise<State>;\n\n /* eslint-disable @typescript-eslint/no-explicit-any -- heterogeneous map: stores different InterceptorFn<M> types under different keys */\n readonly interceptors: Map<\n string,\n ((next: (...args: any[]) => any, ...args: any[]) => any)[]\n >;\n /* eslint-enable @typescript-eslint/no-explicit-any */\n\n readonly setRootPath: (rootPath: string) => void;\n readonly getRootPath: () => string;\n\n readonly getTree: () => RouteTree;\n\n readonly isDisposed: () => boolean;\n\n validator: RouterValidator | null;\n\n // Per-router logger instance (built from `options.logger` in the Router\n // constructor). The facade reads it as `getInternals(this).logger`; namespaces\n // receive it via their deps at wiring; plugins reach it through\n // `getPluginApi(router).logger`. Replaces the former process-global singleton\n // from the standalone `@real-router/logger` package (now folded into\n // `utils/logger`), whose `configure()` leaked across routers (#724).\n readonly logger: RouterLogger;\n\n // Dependencies (issue #172)\n readonly dependenciesGetStore: () => DependenciesStore<D>;\n\n // Clone support (issue #173, consolidated #964). One accessor for the\n // source-side snapshot a clone carries over besides the route store, so a new\n // clone-relevant subsystem is wired in a single place instead of being spread\n // across separate methods.\n readonly getCloneState: () => {\n options: Options<D>;\n dependencies: Record<string, unknown>;\n pluginFactories: PluginFactory<D>[];\n // Resolved logger config of the base router, so a clone can build its OWN\n // logger inheriting the base's level/callback. Frozen `options` do NOT carry\n // `logger` (stripped in the constructor), so `options` above can't convey it;\n // cloneRouter merges a per-request override (traceId) over this snapshot.\n loggerConfig: LoggerConfig;\n };\n\n // Consolidated route data store (issue #174 Phase 2)\n readonly routeGetStore: () => RoutesStore<D>;\n\n // Cross-namespace state (issue #174)\n readonly getStateName: () => string | undefined;\n readonly isTransitioning: () => boolean;\n /**\n * Is the router still live (FSM neither IDLE nor DISPOSED)? Asked by\n * `replace()`'s revalidation commit, which runs application code\n * (`clearDefinitionGuards` recompiles a surviving external factory) between\n * its entry `throwIfDisposed()` and the commit itself (#1627).\n */\n readonly isActive: () => boolean;\n readonly clearState: () => void;\n readonly setState: (state: State) => void;\n readonly routerExtensions: { keys: string[] }[];\n readonly contextClaimRecords: Set<string>;\n\n /**\n * One-shot hydration scratchpad populated by `hydrateRouter` immediately\n * before delegating to `router.start(parsed.path)` and cleared in the\n * matching `finally`. SSR loader plugins read this slot directly via\n * `getInternals(router).hydrationState` to short-circuit their own loader\n * call when the server-resolved namespace value is already present in the\n * parsed state (#596). `null` outside of an active `hydrateRouter`\n * invocation.\n */\n hydrationState: SerializedRouterState | null;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- existential type: stores RouterInternals for all Dependencies types\nconst internals = new WeakMap<object, RouterInternals<any>>();\n\nexport function getInternals<D extends DefaultDependencies>(\n router: RouterInterface<D>,\n): RouterInternals<D> {\n const ctx = internals.get(router);\n\n if (!ctx) {\n throw new TypeError(\n \"[real-router] Invalid router instance — not found in internals registry\",\n );\n }\n\n return ctx as RouterInternals<D>;\n}\n\n/**\n * Channel guard, position P1 (#1572) — the caller's RAW `params` argument, at\n * the API boundary and BEFORE any interceptor runs, so what it reports is what\n * the CALLER wrote (a plugin's later injection is P2's population, not this one).\n *\n * THROWS. The warn-first step (#1572) announced the contract so every call site\n * could identify itself in the logs; this is the promotion it announced.\n *\n * A `TypeError`, synchronous, rather than a `RouterError` on a rejected promise:\n * this is an ARGUMENT-shape defect at the API boundary, caught before any\n * interceptor or transition exists — the same class as the `subscribe` /\n * `navigateToNotFound` / `start` guards beside it. Rejecting instead would let a\n * `.catch()` written for navigation failures swallow a programming error.\n *\n * P3 (`navigateToState`) keeps REJECTING — deliberately asymmetric, because it\n * takes a ready-made `State` from a popstate handler, where a new synchronous\n * throw would change an existing method's failure shape.\n *\n * The predicates (`buildPath` / `isActiveRoute` / `canNavigateTo`) are still NOT\n * instrumented: they run on every `<Link>` render, an answer there is read\n * immediately and corrupts nothing, and throwing inside a render in six adapters\n * is not a trade this guard is worth.\n *\n * ⚠ Not instrumented ≠ blind. `canNavigateTo` answers whether `navigate` WOULD\n * work, so it consults {@link findMisChanneledKey} directly and returns `false`\n * for a shape this function would have thrown on (#1576) — an answer, not a\n * throw, so the render-path trade above is untouched. `buildPath` /\n * `isActiveRoute` ask a different question and are unchanged.\n *\n * @internal\n */\nexport function throwOnMisChanneledKey<D extends DefaultDependencies>(\n ctx: RouterInternals<D>,\n method: string,\n routeName: string,\n params: Params | undefined,\n): void {\n assertChannelCorrect(\n method,\n routeName,\n params,\n ctx.getQueryParams(routeName),\n );\n}\n\nexport function registerInternals<D extends DefaultDependencies>(\n router: RouterClass<D>,\n ctx: RouterInternals<D>,\n): void {\n internals.set(router, ctx);\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument -- internal chain execution: type safety enforced at public API boundary (PluginApi.addInterceptor) */\nfunction executeInterceptorChain<T>(\n interceptors: ((next: (...args: any[]) => any, ...args: any[]) => any)[],\n original: (...args: any[]) => T,\n args: any[],\n): T {\n let chain = original as (...args: any[]) => any;\n\n for (const interceptor of interceptors) {\n const prev = chain;\n\n chain = (...chainArgs: any[]) => interceptor(prev, ...chainArgs);\n }\n\n return chain(...args) as T;\n}\n\n/**\n * Variadic interceptor wrapper — wraps a function of any arity, returning the\n * same callable type `T`. Use {@link createTernaryInterceptable} instead when\n * the wrapped method takes exactly three args and the caller needs the precise\n * `(a, b, c) => r` signature preserved (the variadic form widens args to\n * `any[]`).\n */\nexport function createInterceptable<T extends (...args: any[]) => any>(\n name: string,\n original: T,\n interceptors: Map<\n string,\n ((next: (...args: any[]) => any, ...args: any[]) => any)[]\n >,\n): T {\n return ((...args: any[]) => {\n const chain = interceptors.get(name);\n\n if (!chain || chain.length === 0) {\n return original(...args);\n }\n\n return executeInterceptorChain(chain, original, args);\n }) as T;\n}\n\n/**\n * Three-argument interceptor wrapper — preserves the exact\n * `(a: A, b: B, c: C) => R` signature that the variadic\n * {@link createInterceptable} widens to `any[]`. Backs both search-aware\n * interceptables — `buildPath(route, params, search)` and\n * `forwardState(name, params, search)` (RFC-4 M2 / #1548). Every first-party\n * plugin registers the full three-argument form; a shorter-arity interceptor\n * from a third party remains type-valid (TS allows fewer params, and `next(a,\n * b)` leaves the third arg `undefined`).\n */\nexport function createTernaryInterceptable<A, B, C, R>(\n name: string,\n original: (a: A, b: B, c: C) => R,\n interceptors: Map<\n string,\n ((next: (...args: any[]) => any, ...args: any[]) => any)[]\n >,\n): (a: A, b: B, c: C) => R {\n return (arg1: A, arg2: B, arg3: C) => {\n const chain = interceptors.get(name);\n\n if (!chain || chain.length === 0) {\n return original(arg1, arg2, arg3);\n }\n\n return executeInterceptorChain(chain, original, [arg1, arg2, arg3]);\n };\n}\n/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */\n","/**\n * Canonical route-segment tokenizer.\n *\n * The single owner of \"where does a name / marker / constraint end\" for ONE\n * path segment (post-`/`-split). It replaces the five name-boundary compositions\n * of `PARAM_NAME_PATTERN` currently spread across `buildParamMeta` (L1),\n * `registration` (L2 build + L3 trie), and `route-tree`'s validation gate (L4),\n * so those layers can never disagree on a boundary (the gate↔backstop drift\n * class — #858 / #1050 / #1150 / #1311 / #1149 / #1324).\n *\n * A single left-to-right `charCodeAt` scan produces either a token tuple or a\n * typed error. Grammar — **3 tokens only** (`static | :param | *splat`); the\n * grammar has no optional `:x?` or `<re>` constraint forms. Any `<`/`>` or a\n * post-name `?` in the path is a *registration error* carrying a replacement\n * recipe (`optional-removed` / `constraint-removed`), not a token:\n * 1. Leading `:`/`*` → param/splat; otherwise `static` (a marker glued *after* a\n * static prefix ⇒ `fused-marker`; any `<`/`>` (a former constraint) ⇒\n * `constraint-removed`; a trailing `?` on a marker-less segment ⇒ `name-less`\n * — the modifier has no param name, #1241 / `/faq?`).\n * 2. name = any char except `<`/`?` (no `/` remains inside a segment); a name\n * ending in a bare `:`/`*` ⇒ `trailing-marker` (#1324). A *mid* marker stays\n * a name char — `:a:b` → name `a:b`, preserved.\n * 3. empty name ⇒ `name-less` (#858).\n * 4. a `<` after the name (a former `<re>` constraint) ⇒ `constraint-removed`.\n * 5. a post-name `?` (a former optional modifier, on `:param` or `*splat`) ⇒\n * `optional-removed`.\n *\n * @module parseSegment\n */\n\n/* eslint-disable unicorn/prefer-code-point, unicorn/prefer-includes-over-repeated-comparisons, sonarjs/cognitive-complexity -- charCodeAt code-unit scan + a single inlined branchy pass are this RFC's char-scan perf basis (§9); the same deliberate choices as registration/trie.ts hasNonAsciiSegment (#1285) and SegmentMatcher's inlined #traverseFrom. A `[LT,GT,QUESTION].includes(code)` boundary check would allocate an array literal per scanned char. Markers compared are ASCII (`:` `*` `<` `>` `?`, < 0x80). */\n\n/** A successfully tokenized segment (3-token grammar: `static | :param | *splat`). */\nexport type SegmentTokens =\n | { readonly kind: \"static\"; readonly text: string }\n | { readonly kind: \"param\"; readonly name: string }\n | { readonly kind: \"splat\"; readonly name: string };\n\n/** Grammar-shape rejections, each mirroring a registration guard. */\nexport type SegmentErrorCode =\n | \"name-less\" // #858 — a marker with no name\n | \"trailing-marker\" // #1324 — a param name ending in a bare `:`/`*`\n | \"fused-marker\" // #1050 — a marker glued after a static prefix\n | \"optional-removed\" // M1 — a `:x?`/`*x?` optional modifier (removed; two sibling routes)\n | \"constraint-removed\"; // M1 — a `<re>` constraint or stray `<`/`>` (removed; validate in a guard)\n\nexport interface SegmentError {\n readonly error: SegmentErrorCode;\n}\n\nconst COLON = 58; // :\nconst STAR = 42; // *\nconst LT = 60; // <\nconst GT = 62; // >\nconst QUESTION = 63; // ?\nconst SLASH = 47; // /\n\nconst isMarker = (code: number): boolean => code === COLON || code === STAR;\n\n/**\n * Splits a path into its `/`-delimited segments. A plain `/`-split (M1): the\n * 3-token grammar has no `<...>` constraint whose body could legally contain a\n * `/`, so no constraint-awareness is needed — a stray `<`/`>` is a\n * `constraint-removed` error, caught per segment by `parseSegment`. This is the\n * **segmentation** half of the path-grammar unification: `parseSegment` owns the\n * per-segment grammar, `splitPathSegments` owns where a segment begins and ends.\n *\n * @param path - a route path (query already stripped by the caller)\n * @returns the segments in order, including empty leading/trailing/`//` segments\n * (the caller skips empties, matching the current behaviour)\n */\nexport function splitPathSegments(path: string): string[] {\n const segments: string[] = [];\n let start = 0;\n\n for (let i = 0; i < path.length; i += 1) {\n if (path.charCodeAt(i) !== SLASH) {\n continue;\n }\n\n segments.push(path.slice(start, i));\n start = i + 1;\n }\n\n segments.push(path.slice(start));\n\n return segments;\n}\n\n/**\n * Tokenizes a single path segment (already split on `/`).\n *\n * @param segment - one `/`-delimited segment of a route path\n * @returns the segment's tokens, or a typed grammar error\n */\nexport function parseSegment(segment: string): SegmentTokens | SegmentError {\n const length = segment.length;\n\n if (length === 0) {\n return { kind: \"static\", text: \"\" };\n }\n\n // ---- static segment (no leading marker) -------------------------------\n if (!isMarker(segment.charCodeAt(0))) {\n for (let i = 0; i < length; i += 1) {\n const code = segment.charCodeAt(i);\n\n // A `<`/`>` (a former `<re>` constraint or a stray delimiter) is no longer\n // grammar — M1 removed constraints. Reject with the constraint recipe.\n if (code === LT || code === GT) {\n return { error: \"constraint-removed\" };\n }\n\n // A marker glued after a static prefix is extracted as a param by build/meta\n // but compiled as a static literal by the trie (#1050) — reject it as fused.\n // A marker ENDING the segment (a static ending in `:`/`*` — `/a:`, `/a*`, F2)\n // is NOT fused: caught by `i + 1 < length` being false. Every other following\n // char is fused — including a `?` (`a:?`): that shape never reaches the\n // tokenizer through a real path (a `?` after a bare marker is not a valid\n // `:name?` form, so the query mask strips it before `/`-segmentation), so a\n // direct call correctly reports fused-marker. (`a<`/`a>` already returned\n // `constraint-removed` above, so no `<`-follows exception is needed here.)\n if (isMarker(code) && i + 1 < length) {\n return { error: \"fused-marker\" };\n }\n }\n\n // A trailing `?` is a former optional modifier; on a marker-less segment (no\n // param name) it is a modifier-with-no-name — name-less (#858/#1241, `/faq?`),\n // NOT `optional-removed` (there is no param to route to two siblings). The\n // backstop rejects it by the SAME rule: its `endsWith(\"?\")` fork routes the\n // segment to `extractParamName` → this tokenizer. Owning the `?` here (not\n // only in the marker branch) is what lets the gate and backstop agree on it —\n // otherwise the gate reads `faq?` as a valid static (#1324 §4).\n if (segment.charCodeAt(length - 1) === QUESTION) {\n return { error: \"name-less\" };\n }\n\n return { kind: \"static\", text: segment };\n }\n\n const splat = segment.charCodeAt(0) === STAR;\n\n // ---- name: up to the first `<`/`>` (former constraint delimiter, reserved —\n // В1.3) or `?` (former optional). A segment holds no `/`. -----------------\n let cursor = 1;\n\n while (cursor < length) {\n const code = segment.charCodeAt(cursor);\n\n if (code === LT || code === GT || code === QUESTION) {\n break;\n }\n\n cursor += 1;\n }\n\n const name = segment.slice(1, cursor);\n\n if (name.length === 0) {\n return { error: \"name-less\" }; // #858\n }\n\n if (isMarker(name.charCodeAt(name.length - 1))) {\n return { error: \"trailing-marker\" }; // #1324\n }\n\n // ---- former constraint / optional modifiers (removed in M1) ------------\n // The name scan stops at the first `<`/`>` or `?`. Either is a form removed\n // in M1: a `<re>` constraint (also a stray `<`/`>` — В1.3), or a `:x?`/`*x?`\n // optional. Only `?` is the optional; `<`/`>` are the constraint recipe.\n // Reject with the matching replacement recipe rather than tokenize it.\n if (cursor < length) {\n return segment.charCodeAt(cursor) === QUESTION\n ? { error: \"optional-removed\" }\n : { error: \"constraint-removed\" }; // LT or GT\n }\n\n return splat ? { kind: \"splat\", name } : { kind: \"param\", name };\n}\n\n/**\n * Returns the first per-segment grammar error in a path, or `undefined` if every\n * segment tokenizes cleanly.\n *\n * The **validation-facing** entry over the tokenizer: `route-tree`'s\n * `validateRoutePath` calls this instead of re-running its own split+parse loop,\n * so the gate and the matcher's own grammar cannot drift (#1324) and the loop\n * stays single-sourced here — the tokenizer primitives (`parseSegment`,\n * `splitPathSegments`) need not leak into the package's public surface. An empty\n * segment tokenizes as `static` (never an error), so leading/trailing/`//`\n * empties are skipped naturally.\n *\n * @param path - a route path (query already stripped by the caller)\n * @returns the first `SegmentErrorCode` (scanned left to right), or `undefined`\n */\nexport function findSegmentGrammarError(\n path: string,\n): SegmentErrorCode | undefined {\n for (const segment of splitPathSegments(path)) {\n const token = parseSegment(segment);\n\n if (\"error\" in token) {\n return token.error;\n }\n }\n\n return undefined;\n}\n\n/** A removed-form (M1) match, describing the offending segment and — for an\n * optional — the two sibling paths that replace it (path without the optional\n * segment + path with the param made required). The route-tree gate uses this to\n * build a route-contextual replacement recipe; the matcher backstop uses only the\n * error code (a shorter, path-free recipe). */\nexport type RemovedForm =\n | {\n readonly code: \"optional-removed\";\n readonly segment: string;\n readonly withoutSegment: string;\n readonly requiredForm: string;\n }\n | { readonly code: \"constraint-removed\"; readonly segment: string };\n\n/**\n * The rich (route-tree gate) view over the tokenizer for a removed form: finds\n * the first `optional-removed` / `constraint-removed` segment and, for an\n * optional, computes its two replacement sibling paths from the ACTUAL path\n * (dropping the segment → without-form; dropping the trailing `?` → required\n * form). Returns `undefined` if no removed form is present (the gate then uses\n * `findSegmentGrammarError` for a surviving grammar rejection).\n *\n * @param path - a route path (query already stripped by the caller)\n */\nexport function describeRemovedForm(path: string): RemovedForm | undefined {\n const segments = splitPathSegments(path);\n\n for (let i = 0; i < segments.length; i += 1) {\n const token = parseSegment(segments[i]);\n\n if (!(\"error\" in token)) {\n continue;\n }\n\n // First error wins (mirrors `findSegmentGrammarError`): describe it ONLY if\n // it is a removed form, else return undefined so the caller falls to the\n // surviving-code message — this keeps the gate's reason in lockstep with the\n // matcher backstop's first-error verdict.\n if (token.error === \"optional-removed\") {\n const segment = segments[i];\n const required = [...segments];\n\n // Drop the `?` optional modifier AND everything after it (the tokenizer\n // stopped the name at the first `?`, so it is the modifier). Using the `?`\n // index — not a blind `slice(0, -1)` — keeps the required sibling VALID for\n // a reverse/compound form whose `?` is not the last char: `:b?<x>` → `:b`\n // (not `:b?<x`), `:id??` → `:id` (not `:id?`). #1516\n required[i] = segment.slice(0, segment.indexOf(\"?\"));\n\n return {\n code: \"optional-removed\",\n segment,\n withoutSegment: segments.filter((_, j) => j !== i).join(\"/\"),\n requiredForm: required.join(\"/\"),\n };\n }\n\n return token.error === \"constraint-removed\"\n ? { code: \"constraint-removed\", segment: segments[i] }\n : undefined;\n }\n\n return undefined;\n}\n","/**\n * Route Parameter Metadata Extraction.\n *\n * Extracts parameter metadata from route path patterns without requiring\n * a full path-parser instance. Replaces parser.urlParams/queryParams/spatParams.\n *\n * @module buildParamMeta\n */\n\nimport { parseSegment, splitPathSegments } from \"./parseSegment\";\n\nimport type { ParamMeta } from \"./types\";\n\n/**\n * A query-param NAME may not contain `<`/`>` (#1242 §5.1) — a constraint\n * delimiter leaked into the query via a reverse-order modifier typo (`/a/:b?<c>`\n * parses the `?` as the query start, making `<c>` the query name). Consumed by\n * the route-tree gate and the `registerTree` backstop; relocated here from the\n * deleted `constraint-grammar.ts` when M1 removed constraints (query-param name\n * validation is a query concern, and this module owns query extraction).\n */\nexport const INVALID_QUERY_NAME_RGX = /[<>]/u;\n\nconst QUESTION = 0x3f; // ?\nconst SLASH = 0x2f; // /\nconst LT = 0x3c; // <\n\n/**\n * Locates the query separator `?` in a route path — the FIRST `?` whose tail is\n * non-empty and does not begin with `/`, `?`, or `<` (M1 §3.3). The 3-token\n * grammar leaves `?` a single role (there is no optional modifier and no\n * constraint body to hide one), so no length-preserving mask is needed. The three\n * excluded tails keep a REMOVED form in the path part, where `parseSegment`\n * rejects it with a recipe instead of mis-reading it as a query declaration:\n * - end-of-string (`/:id?`) and `/` (`/:id?/edit`) → a bare `:x?` optional;\n * - `?` (`/:id??tab`) → the leading `?` is the optional, the later `?` the query;\n * - `<` (`/a/:b?<x>`) → a reverse-order `:b?<x>` (optional then a former constraint).\n *\n * @param path - a route path\n * @returns the index of the query separator, or -1 if there is none\n */\nfunction findQuerySeparator(path: string): number {\n for (let i = 0; i < path.length; i += 1) {\n if (path.codePointAt(i) !== QUESTION) {\n continue;\n }\n\n // `next` is the code point after the `?`, or the `-1` sentinel at end-of-string.\n // The `-1` sentinel is the SOLE end-of-string guard — the former separate\n // `next !== undefined` conjunct was dead (the ternary bounds the index, so\n // `codePointAt` never returns `undefined`; the `!` is a type assertion, not a\n // runtime branch, so it keeps the scan at 100% coverage). Mirrors `#scanPath`.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ternary-bounded in-range index; codePointAt is defined\n const next = i + 1 < path.length ? path.codePointAt(i + 1)! : -1;\n\n if (next !== -1 && next !== SLASH && next !== QUESTION && next !== LT) {\n return i;\n }\n }\n\n return -1;\n}\n\n/**\n * Builds parameter metadata from a route path pattern.\n *\n * Extracts URL parameters, query parameters, and splat parameters\n * from the path pattern string.\n *\n * @param path - Route path pattern (e.g., \"/users/:id/posts/:postId?q\")\n * @returns Parameter metadata object\n *\n * @example\n * ```typescript\n * buildParamMeta(\"/users/:id\")\n * // → { urlParams: [\"id\"], queryParams: [], spatParams: [], paramTypeMap: { id: \"url\" } }\n *\n * buildParamMeta(\"/search?q&page\")\n * // → { urlParams: [], queryParams: [\"q\", \"page\"], spatParams: [],\n * // paramTypeMap: { q: \"query\", page: \"query\" } }\n *\n * buildParamMeta(\"/files/*path\")\n * // → { urlParams: [\"path\"], queryParams: [], spatParams: [\"path\"],\n * // paramTypeMap: { path: \"url\" } }\n * ```\n */\n// Shared frozen sentinels for the common no-params case — avoid a fresh empty\n// array/object per route (#1009). ParamMeta fields are Readonly*; match/build\n// only read them, and computeCaches' Object.freeze on the arrays/object is a\n// no-op on an already-frozen shared instance.\nconst EMPTY_PARAM_NAMES: readonly string[] = Object.freeze([]);\nconst EMPTY_PARAM_TYPE_MAP: Readonly<Record<string, \"url\" | \"query\">> =\n Object.freeze({});\n\n// Whole-meta shared sentinel for the fully-static case: every collection is a\n// #1009 sentinel AND pathPattern degenerates to the input path itself (no query\n// to strip), so the wrapper carries zero per-route information. The RETAINING\n// caller (route-tree's computeCaches) swaps a matching fresh result for this\n// instance — buildParamMeta itself keeps returning fresh objects so the\n// validation gate can read the real pathPattern of arbitrary input paths.\n// `pathPattern` is \"\" here; the one stored-meta reader (`registerNode`) falls\n// back to `node.path` on identity match.\nexport const EMPTY_PARAM_META: ParamMeta = Object.freeze({\n urlParams: EMPTY_PARAM_NAMES,\n queryParams: EMPTY_PARAM_NAMES,\n spatParams: EMPTY_PARAM_NAMES,\n paramTypeMap: EMPTY_PARAM_TYPE_MAP,\n pathPattern: \"\",\n});\n\n/**\n * Extracts URL/splat params from a path's segments into the given accumulators\n * via the canonical `parseSegment` tokenizer. Split out of `buildParamMeta` so\n * the builder stays under the cognitive-complexity budget. A malformed segment\n * (token errors) or a `static` segment contributes nothing — a malformed route is\n * rejected downstream before it compiles, so its meta is moot.\n */\nfunction collectUrlParams(\n path: string,\n urlParams: string[],\n spatParams: string[],\n paramTypeMap: Record<string, \"url\" | \"query\">,\n): void {\n for (const segment of splitPathSegments(path)) {\n if (segment.length === 0) {\n continue;\n }\n\n const token = parseSegment(segment);\n\n if (\"error\" in token || token.kind === \"static\") {\n continue;\n }\n\n urlParams.push(token.name);\n paramTypeMap[token.name] = \"url\";\n\n if (token.kind === \"splat\") {\n spatParams.push(token.name);\n }\n }\n}\n\nexport function buildParamMeta(path: string): ParamMeta {\n const urlParams: string[] = [];\n const queryParams: string[] = [];\n const spatParams: string[] = [];\n const paramTypeMap: Record<string, \"url\" | \"query\"> = {};\n\n // Locate the real query separator (M1 §3.3: first `?` whose tail is not a\n // former optional/reverse form).\n const separator = findQuerySeparator(path);\n\n if (separator !== -1) {\n const queryString = path.slice(separator + 1);\n const params = queryString.split(\"&\");\n\n for (const param of params) {\n const paramName = param.trim();\n\n if (paramName.length > 0) {\n queryParams.push(paramName);\n paramTypeMap[paramName] = \"query\";\n }\n }\n\n path = path.slice(0, separator);\n }\n\n collectUrlParams(path, urlParams, spatParams, paramTypeMap);\n\n return shareEmptyCollections(\n urlParams,\n queryParams,\n spatParams,\n paramTypeMap,\n path,\n );\n}\n\n// #1009: swap each freshly-built empty collection for a shared frozen sentinel\n// — factored out of buildParamMeta so the hot builder stays under the cognitive-\n// complexity budget. match/build only read these (Readonly*), and computeCaches'\n// Object.freeze is a no-op on an already-frozen shared instance.\nfunction shareEmptyCollections(\n urlParams: string[],\n queryParams: string[],\n spatParams: string[],\n paramTypeMap: Record<string, \"url\" | \"query\">,\n pathPattern: string,\n): ParamMeta {\n return {\n urlParams: urlParams.length === 0 ? EMPTY_PARAM_NAMES : urlParams,\n queryParams: queryParams.length === 0 ? EMPTY_PARAM_NAMES : queryParams,\n spatParams: spatParams.length === 0 ? EMPTY_PARAM_NAMES : spatParams,\n paramTypeMap:\n urlParams.length === 0 && queryParams.length === 0\n ? EMPTY_PARAM_TYPE_MAP\n : paramTypeMap,\n pathPattern,\n };\n}\n"],"mappings":"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"}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{A as e,D as t,O as n,g as r,k as i,n as a,t as o}from"./Router-
|
|
1
|
+
import{A as e,D as t,O as n,g as r,k as i,n as a,t as o}from"./Router-hyjjNX3c.mjs";const s=(e=[],t={},n={})=>new o(e,t,n),c=new WeakMap,l=e=>{let t=c.get(e);return t||(t=Object.freeze({navigate:e.navigate,getState:e.getState,isActiveRoute:e.isActiveRoute,canNavigateTo:e.canNavigateTo,subscribe:e.subscribe,subscribeLeave:e.subscribeLeave,isLeaveApproved:e.isLeaveApproved}),c.set(e,t)),t};export{o as Router,a as RouterError,t as UNKNOWN_ROUTE,n as constants,s as createRouter,i as errorCodes,e as events,l as getNavigator,r as resolveForwardChain};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/esm/internals.d.mts
CHANGED
|
@@ -97,6 +97,13 @@ interface RouterInternals<D extends DefaultDependencies = DefaultDependencies> {
|
|
|
97
97
|
readonly routeGetStore: () => RoutesStore<D>;
|
|
98
98
|
readonly getStateName: () => string | undefined;
|
|
99
99
|
readonly isTransitioning: () => boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Is the router still live (FSM neither IDLE nor DISPOSED)? Asked by
|
|
102
|
+
* `replace()`'s revalidation commit, which runs application code
|
|
103
|
+
* (`clearDefinitionGuards` recompiles a surviving external factory) between
|
|
104
|
+
* its entry `throwIfDisposed()` and the commit itself (#1627).
|
|
105
|
+
*/
|
|
106
|
+
readonly isActive: () => boolean;
|
|
100
107
|
readonly clearState: () => void;
|
|
101
108
|
readonly setState: (state: State) => void;
|
|
102
109
|
readonly routerExtensions: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internals.d.mts","names":[],"sources":["../../src/internals.ts"],"mappings":";;;;;;;;;;;;UA8BiB,gBACf,UAAU,sBAAsB;WAEvB,YACP,UAAU,SAAS,QACnB,UAAU,eAAe,cAEzB,cACA,SAAS,GACT,SAAS,GACT,kBACG,MAAM,GAAG;;;;;;;WAQL,kBACP,iBACG,eAAe;;;;;;WAOX,iBAAiB;WAEjB,eACP,UAAU,SAAS,QACnB,UAAU,eAAe,cAEzB,mBACA,aAAa,GACb,cAAc,MACX,YAAY,GAAG;WAEX,qBACP,sBACA,gBAAgB,WACb;WAEI,YAAY,UAAU,SAAS,QACtC,cACA,UAAU,eACP,MAAM;WAEF,kBAAkB,QAAQ;WAE1B,mBAAmB,UAAU,WACpC,WAAW,GACX,IAAI,OAAO,eAAe,QACvB;;;;;;;;WASI;aACE,OAAO,OAAO;aACd,YACP,UAAU,OAAO,8BACd;aACI;;;;;aAKA;;WAGF,YACP,eACA,SAAS,QACT,SAAS;;;;;;;;WAUF,YAAY;WAEZ,sBAAsB,OAAO;;;;;;;WAQ7B,wBACP,SAAS,OACT,WAAW,mBACX,OAAO;;;;;;;;WAUA,qBAAqB,iBAAiB;WAEtC,QAAQ,iBAAiB,QAAQ;;;;;;;WAQjC,kBACP,OAAO,OACP,UAAU,sBACP,QAAQ;WAGJ,cAAc,cAEnB,UAAU,wBAAwB;WAI7B,cAAc;WACd;WAEA,eAAe;WAEf;EAET,WAAW;WAQF,QAAQ;WAGR,4BAA4B,kBAAkB;WAM9C;IACP,SAAS,QAAQ;IACjB,cAAc;IACd,iBAAiB,cAAc;IAK/B,cAAc;;WAIP,qBAAqB,YAAY;WAGjC;WACA;WACA;WACA,WAAW,OAAO;WAClB;IAAoB;;WACpB,qBAAqB;;;;;;;;;;EAW9B,gBAAgB;;iBAMF,aAAa,UAAU,qBACrC,QAAQ,OAAgB,KACvB,gBAAgB"}
|
|
1
|
+
{"version":3,"file":"internals.d.mts","names":[],"sources":["../../src/internals.ts"],"mappings":";;;;;;;;;;;;UA8BiB,gBACf,UAAU,sBAAsB;WAEvB,YACP,UAAU,SAAS,QACnB,UAAU,eAAe,cAEzB,cACA,SAAS,GACT,SAAS,GACT,kBACG,MAAM,GAAG;;;;;;;WAQL,kBACP,iBACG,eAAe;;;;;;WAOX,iBAAiB;WAEjB,eACP,UAAU,SAAS,QACnB,UAAU,eAAe,cAEzB,mBACA,aAAa,GACb,cAAc,MACX,YAAY,GAAG;WAEX,qBACP,sBACA,gBAAgB,WACb;WAEI,YAAY,UAAU,SAAS,QACtC,cACA,UAAU,eACP,MAAM;WAEF,kBAAkB,QAAQ;WAE1B,mBAAmB,UAAU,WACpC,WAAW,GACX,IAAI,OAAO,eAAe,QACvB;;;;;;;;WASI;aACE,OAAO,OAAO;aACd,YACP,UAAU,OAAO,8BACd;aACI;;;;;aAKA;;WAGF,YACP,eACA,SAAS,QACT,SAAS;;;;;;;;WAUF,YAAY;WAEZ,sBAAsB,OAAO;;;;;;;WAQ7B,wBACP,SAAS,OACT,WAAW,mBACX,OAAO;;;;;;;;WAUA,qBAAqB,iBAAiB;WAEtC,QAAQ,iBAAiB,QAAQ;;;;;;;WAQjC,kBACP,OAAO,OACP,UAAU,sBACP,QAAQ;WAGJ,cAAc,cAEnB,UAAU,wBAAwB;WAI7B,cAAc;WACd;WAEA,eAAe;WAEf;EAET,WAAW;WAQF,QAAQ;WAGR,4BAA4B,kBAAkB;WAM9C;IACP,SAAS,QAAQ;IACjB,cAAc;IACd,iBAAiB,cAAc;IAK/B,cAAc;;WAIP,qBAAqB,YAAY;WAGjC;WACA;;;;;;;WAOA;WACA;WACA,WAAW,OAAO;WAClB;IAAoB;;WACpB,qBAAqB;;;;;;;;;;EAW9B,gBAAgB;;iBAMF,aAAa,UAAU,qBACrC,QAAQ,OAAgB,KACvB,gBAAgB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@real-router/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.85.1",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "A simple, powerful, view-agnostic, modular and extensible router",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -99,6 +99,6 @@
|
|
|
99
99
|
"type-check": "tsc --noEmit",
|
|
100
100
|
"lint": "eslint --cache --ext .ts src/ tests/ --max-warnings 0",
|
|
101
101
|
"lint:fix": "eslint --cache --ext .ts src/ tests/ --fix --max-warnings 0",
|
|
102
|
-
"bundle": "
|
|
102
|
+
"bundle": "RR_DTS_PASS=1 tsdown --config-loader unrun && tsdown --config-loader unrun && node scripts/check-dts-augment-targets.mjs"
|
|
103
103
|
}
|
|
104
104
|
}
|
package/dist/cjs/Router.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
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
|
-
//# sourceMappingURL=Router.js.map
|
package/dist/cjs/Router.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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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"}
|
package/dist/cjs/RouterError.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("./constants.js"),t=new Set(Object.values(e.errorCodes)),n=new Set([`code`,`segment`,`path`]),r=new Set([`setCode`,`setErrorInstance`,`setAdditionalFields`,`hasField`,`getField`,`toJSON`]);var i=class extends Error{segment;path;code;constructor(e,{message:t,segment:i,path:a,...o}={}){super(t??e),this.name=`RouterError`,this.code=e,this.segment=i,this.path=a;for(let[e,t]of Object.entries(o)){if(n.has(e))throw TypeError(`[RouterError] Cannot set reserved property "${e}"`);r.has(e)||(this[e]=t)}}setCode(e){this.code=e,t.has(this.message)&&(this.message=e)}setErrorInstance(e){if(!e)throw TypeError(`[RouterError.setErrorInstance] err parameter is required and must be an Error instance`);this.message=e.message,this.cause=e.cause,this.stack=e.stack??``}setAdditionalFields(e){for(let[t,i]of Object.entries(e)){if(n.has(t))throw TypeError(`[RouterError.setAdditionalFields] Cannot set reserved property "${t}"`);r.has(t)||(this[t]=i)}}hasField(e){return e in this}getField(e){return this[e]}toJSON(){let e={code:this.code,message:this.message};this.segment!==void 0&&(e.segment=this.segment),this.path!==void 0&&(e.path=this.path);let t=new Set([`code`,`message`,`segment`,`path`,`stack`,`name`]);for(let n in this)Object.hasOwn(this,n)&&!t.has(n)&&(e[n]=this[n]);return e}};exports.RouterError=i;
|
|
2
|
-
//# sourceMappingURL=RouterError.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RouterError.js","names":["errorCodes"],"sources":["../../src/RouterError.ts"],"sourcesContent":["// packages/core/src/RouterError.ts\n\nimport { errorCodes } from \"./constants\";\n\n// Pre-compute Set of error code values for O(1) lookup in setCode()\n// This avoids creating array and doing linear search on every setCode() call\nconst errorCodeValues = new Set(Object.values(errorCodes));\n\n// Reserved built-in properties - throw error if user tries to set these\nconst reservedProperties = new Set([\"code\", \"segment\", \"path\"]);\n\n// Reserved method names - silently ignore attempts to overwrite these\nconst reservedMethods = new Set([\n \"setCode\",\n \"setErrorInstance\",\n \"setAdditionalFields\",\n \"hasField\",\n \"getField\",\n \"toJSON\",\n]);\n\nexport class RouterError extends Error {\n [key: string]: unknown;\n\n // Using public properties to ensure structural compatibility\n // with RouterError interface in core-types\n readonly segment: string | undefined;\n readonly path: string | undefined;\n\n // Note: code appears to be writable but setCode() should be used\n // to properly update both code and message together\n code: string;\n\n /**\n * Creates a new RouterError instance.\n *\n * The options object accepts built-in fields (message, segment, path)\n * and any additional custom fields, which will all be attached to the error instance.\n *\n * @param code - The error code (e.g., \"ROUTE_NOT_FOUND\", \"CANNOT_ACTIVATE\")\n * @param options - Optional configuration object\n * @param options.message - Custom error message (defaults to code if not provided)\n * @param options.segment - The route segment where the error occurred\n * @param options.path - The full path where the error occurred\n *\n * @example\n * ```typescript\n * // Basic error\n * const err1 = new RouterError(\"ROUTE_NOT_FOUND\");\n *\n * // Error with custom message\n * const err2 = new RouterError(\"ERR\", { message: \"Something went wrong\" });\n *\n * // Error with context and custom fields\n * const err3 = new RouterError(\"CANNOT_ACTIVATE\", {\n * message: \"Insufficient permissions\",\n * segment: \"admin\",\n * path: \"/admin/users\",\n * userId: \"123\" // custom field\n * });\n * ```\n */\n constructor(\n code: string,\n {\n message,\n segment,\n path,\n ...rest\n }: {\n [key: string]: unknown;\n message?: string | undefined;\n segment?: string | undefined;\n path?: string | undefined;\n } = {},\n ) {\n super(message ?? code);\n\n // Subclasses don't auto-set `name`; without this `error.name` inherits\n // \"Error\", breaking `error.name === \"RouterError\"` checks at catch sites that\n // can't `instanceof` across bundle boundaries.\n this.name = \"RouterError\";\n\n this.code = code;\n this.segment = segment;\n this.path = path;\n\n // Assign custom fields, checking reserved properties and filtering out reserved method names\n // Issue #39: Throw for reserved properties to match setAdditionalFields behavior\n for (const [key, value] of Object.entries(rest)) {\n if (reservedProperties.has(key)) {\n throw new TypeError(\n `[RouterError] Cannot set reserved property \"${key}\"`,\n );\n }\n\n if (!reservedMethods.has(key)) {\n this[key] = value;\n }\n }\n }\n\n /**\n * Updates the error code and conditionally updates the message.\n *\n * If the current message is one of the standard error code values\n * (e.g., \"ROUTE_NOT_FOUND\", \"SAME_STATES\"), it will be replaced with the new code.\n * This allows keeping error messages in sync with codes when using standard error codes.\n *\n * If the message is custom (not a standard error code), it will be preserved.\n *\n * @param newCode - The new error code to set\n *\n * @example\n * // Message follows code (standard error code as message)\n * const err = new RouterError(\"ROUTE_NOT_FOUND\", { message: \"ROUTE_NOT_FOUND\" });\n * err.setCode(\"CUSTOM_ERROR\"); // message becomes \"CUSTOM_ERROR\"\n *\n * @example\n * // Custom message is preserved\n * const err = new RouterError(\"ERR\", { message: \"Custom error message\" });\n * err.setCode(\"NEW_CODE\"); // message stays \"Custom error message\"\n */\n setCode(newCode: string): void {\n this.code = newCode;\n\n // Only update message if it's a standard error code value (not a custom message)\n if (errorCodeValues.has(this.message)) {\n this.message = newCode;\n }\n }\n\n /**\n * Copies properties from another Error instance to this RouterError.\n *\n * This method updates the message, cause, and stack trace from the provided error.\n * Useful for wrapping native errors while preserving error context.\n *\n * @param err - The Error instance to copy properties from\n * @throws {TypeError} If err is null or undefined\n *\n * @example\n * ```typescript\n * const routerErr = new RouterError(\"TRANSITION_ERR\");\n * try {\n * // some operation that might fail\n * } catch (nativeErr) {\n * routerErr.setErrorInstance(nativeErr);\n * throw routerErr;\n * }\n * ```\n */\n setErrorInstance(err: Error): void {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!err) {\n throw new TypeError(\n \"[RouterError.setErrorInstance] err parameter is required and must be an Error instance\",\n );\n }\n\n this.message = err.message;\n this.cause = err.cause;\n this.stack = err.stack ?? \"\";\n }\n\n /**\n * Adds custom fields to the error object.\n *\n * This method allows attaching arbitrary data to the error for debugging or logging purposes.\n * All fields become accessible as properties on the error instance and are included in JSON serialization.\n *\n * Reserved method names (setCode, setErrorInstance, setAdditionalFields, hasField, getField, toJSON)\n * are automatically filtered out to prevent accidental overwriting of class methods.\n *\n * @param fields - Object containing custom fields to add to the error\n *\n * @example\n * ```typescript\n * const err = new RouterError(\"CANNOT_ACTIVATE\");\n * err.setAdditionalFields({\n * userId: \"123\",\n * attemptedRoute: \"/admin\",\n * reason: \"insufficient permissions\"\n * });\n *\n * console.log(err.userId); // \"123\"\n * console.log(JSON.stringify(err)); // includes all custom fields\n * ```\n */\n setAdditionalFields(fields: Record<string, unknown>): void {\n // Assign fields, throwing for reserved properties, silently ignoring methods\n for (const [key, value] of Object.entries(fields)) {\n if (reservedProperties.has(key)) {\n throw new TypeError(\n `[RouterError.setAdditionalFields] Cannot set reserved property \"${key}\"`,\n );\n }\n\n if (!reservedMethods.has(key)) {\n this[key] = value;\n }\n }\n }\n\n /**\n * Checks if a custom field exists on the error object.\n *\n * This method checks for both custom fields added via setAdditionalFields()\n * and built-in fields (code, message, segment, etc.).\n *\n * @param key - The field name to check\n * @returns `true` if the field exists, `false` otherwise\n *\n * @example\n * ```typescript\n * const err = new RouterError(\"ERR\", { segment: \"users\" });\n * err.setAdditionalFields({ userId: \"123\" });\n *\n * err.hasField(\"userId\"); // true\n * err.hasField(\"segment\"); // true\n * err.hasField(\"unknown\"); // false\n * ```\n */\n hasField(key: string): boolean {\n return key in this;\n }\n\n /**\n * Retrieves a custom field value from the error object.\n *\n * This method can access both custom fields and built-in fields.\n * Returns `undefined` if the field doesn't exist.\n *\n * @param key - The field name to retrieve\n * @returns The field value, or `undefined` if it doesn't exist\n *\n * @example\n * ```typescript\n * const err = new RouterError(\"ERR\");\n * err.setAdditionalFields({ userId: \"123\", role: \"admin\" });\n *\n * err.getField(\"userId\"); // \"123\"\n * err.getField(\"role\"); // \"admin\"\n * err.getField(\"code\"); // \"ERR\" (built-in field)\n * err.getField(\"unknown\"); // undefined\n * ```\n */\n getField(key: string): unknown {\n return this[key];\n }\n\n /**\n * Serializes the error to a JSON-compatible object.\n *\n * This method is automatically called by JSON.stringify() and includes:\n * - Built-in fields: code, message, segment (if set), path (if set)\n * - All custom fields added via setAdditionalFields() or constructor\n * - Excludes: stack trace (for security/cleanliness)\n *\n * @returns A plain object representation of the error, suitable for JSON serialization\n *\n * @example\n * ```typescript\n * const err = new RouterError(\"ROUTE_NOT_FOUND\", {\n * message: \"Route not found\",\n * path: \"/admin/users/123\"\n * });\n * err.setAdditionalFields({ userId: \"123\" });\n *\n * JSON.stringify(err);\n * // {\n * // \"code\": \"ROUTE_NOT_FOUND\",\n * // \"message\": \"Route not found\",\n * // \"path\": \"/admin/users/123\",\n * // \"userId\": \"123\"\n * // }\n * ```\n */\n toJSON(): Record<string, unknown> {\n const result: Record<string, unknown> = {\n code: this.code,\n message: this.message,\n };\n\n if (this.segment !== undefined) {\n result.segment = this.segment;\n }\n if (this.path !== undefined) {\n result.path = this.path;\n }\n\n // add all public fields\n // Using Set.has() for O(1) lookup instead of Array.includes() O(n)\n // Overall complexity: O(n) instead of O(n*m)\n const excludeKeys = new Set([\n \"code\",\n \"message\",\n \"segment\",\n \"path\",\n \"stack\",\n // `name` is now an own enumerable prop (constructor sets it to\n // \"RouterError\"); it's class metadata, not a custom field — keep it out of\n // the serialized output (preserves toJSON shape).\n \"name\",\n ]);\n\n for (const key in this) {\n if (Object.hasOwn(this, key) && !excludeKeys.has(key)) {\n result[key] = this[key];\n }\n }\n\n return result;\n }\n}\n"],"mappings":"kCAMM,EAAkB,IAAI,IAAI,OAAO,OAAOA,EAAAA,UAAU,CAAC,EAGnD,EAAqB,IAAI,IAAI,CAAC,OAAQ,UAAW,MAAM,CAAC,EAGxD,EAAkB,IAAI,IAAI,CAC9B,UACA,mBACA,sBACA,WACA,WACA,QACF,CAAC,EAED,IAAa,EAAb,cAAiC,KAAM,CAKrC,QACA,KAIA,KA+BA,YACE,EACA,CACE,UACA,UACA,OACA,GAAG,GAMD,CAAC,EACL,CACA,MAAM,GAAW,CAAI,EAKrB,KAAK,KAAO,cAEZ,KAAK,KAAO,EACZ,KAAK,QAAU,EACf,KAAK,KAAO,EAIZ,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAI,EAAG,CAC/C,GAAI,EAAmB,IAAI,CAAG,EAC5B,MAAU,UACR,+CAA+C,EAAI,EACrD,EAGG,EAAgB,IAAI,CAAG,IAC1B,KAAK,GAAO,EAEhB,CACF,CAuBA,QAAQ,EAAuB,CAC7B,KAAK,KAAO,EAGR,EAAgB,IAAI,KAAK,OAAO,IAClC,KAAK,QAAU,EAEnB,CAsBA,iBAAiB,EAAkB,CAEjC,GAAI,CAAC,EACH,MAAU,UACR,wFACF,EAGF,KAAK,QAAU,EAAI,QACnB,KAAK,MAAQ,EAAI,MACjB,KAAK,MAAQ,EAAI,OAAS,EAC5B,CA0BA,oBAAoB,EAAuC,CAEzD,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAM,EAAG,CACjD,GAAI,EAAmB,IAAI,CAAG,EAC5B,MAAU,UACR,mEAAmE,EAAI,EACzE,EAGG,EAAgB,IAAI,CAAG,IAC1B,KAAK,GAAO,EAEhB,CACF,CAqBA,SAAS,EAAsB,CAC7B,OAAO,KAAO,IAChB,CAsBA,SAAS,EAAsB,CAC7B,OAAO,KAAK,EACd,CA6BA,QAAkC,CAChC,IAAM,EAAkC,CACtC,KAAM,KAAK,KACX,QAAS,KAAK,OAChB,EAEI,KAAK,UAAY,IAAA,KACnB,EAAO,QAAU,KAAK,SAEpB,KAAK,OAAS,IAAA,KAChB,EAAO,KAAO,KAAK,MAMrB,IAAM,EAAc,IAAI,IAAI,CAC1B,OACA,UACA,UACA,OACA,QAIA,MACF,CAAC,EAED,IAAK,IAAM,KAAO,KACZ,OAAO,OAAO,KAAM,CAAG,GAAK,CAAC,EAAY,IAAI,CAAG,IAClD,EAAO,GAAO,KAAK,IAIvB,OAAO,CACT,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../constants.js"),t=require("../internals.js"),n=require("../engine/operations/routeTreeToDefinitions.js"),r=require("../namespaces/RoutesNamespace/helpers.js"),i=require("../RouterError.js"),a=require("../Router.js"),o=require("./getLifecycleApi.js");function s(s,c,l){let u=t.getInternals(s);if(u.isDisposed())throw new i.RouterError(e.errorCodes.ROUTER_DISPOSED);u.validator?.dependencies.validateCloneArgs(c);let d=u.routeGetStore(),f=n.routeTreeToDefinitions(d.tree),p=d.config,m=d.resolvedForwardMap,h=d.routeCustomFields,{options:g,dependencies:_,pluginFactories:v,loggerConfig:y}=u.getCloneState(),{definition:b,external:x}=d.lifecycleNamespace.getFactoriesByOrigin(),S={..._,...c},C=l?.logger?{...y,...l.logger}:y,w=new a.Router(f,{...g,logger:C},S),T=t.getInternals(w),E=T.routeGetStore(),D=E.lifecycleNamespace;r.assignConfigEntries(E.config,p),Object.assign(E.resolvedForwardMap,m),Object.assign(E.routeCustomFields,h),d.rootPath!==``&&T.setRootPath(d.rootPath);let[O,k]=b,[A,j]=x;for(let[e,t]of Object.entries(O))D.addCanDeactivate(e,t,!0);for(let[e,t]of Object.entries(k))D.addCanActivate(e,t,!0);let M=o.getLifecycleApi(w);for(let[e,t]of Object.entries(A))M.addDeactivateGuard(e,t);for(let[e,t]of Object.entries(j))M.addActivateGuard(e,t);let N=new Set(T.getCloneState().pluginFactories),P=v.filter(e=>!N.has(e));return P.length>0&&w.usePlugin(...P),w}exports.cloneRouter=s;
|
|
2
|
-
//# sourceMappingURL=cloneRouter.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cloneRouter.js","names":["getInternals","RouterError","errorCodes","routeTreeToDefinitions","RouterClass","getLifecycleApi"],"sources":["../../../src/api/cloneRouter.ts"],"sourcesContent":["import { errorCodes } from \"../constants\";\nimport { routeTreeToDefinitions } from \"../engine\";\nimport { getInternals } from \"../internals\";\nimport { getLifecycleApi } from \"./getLifecycleApi\";\nimport { assignConfigEntries } from \"../namespaces/RoutesNamespace/helpers\";\nimport { Router as RouterClass } from \"../Router\";\nimport { RouterError } from \"../RouterError\";\n\nimport type {\n DefaultDependencies,\n LoggerConfig,\n Router,\n Route,\n} from \"../types\";\n\n/**\n * Per-clone overrides beyond dependencies.\n */\nexport interface CloneOptions {\n /**\n * Per-clone logger config override, merged **over** the base router's resolved\n * logger config. Primary use: per-request `traceId` in SSR — a fresh\n * `callback` closed over the request id, while `level` inherits the base.\n * Omitted keys inherit the base (level / callback / callbackIgnoresLevel).\n *\n * Override is by **config**, not a logger instance: `RouterLogger` is\n * core-internal (only its `{ log, warn, error }` interface is public), so\n * nothing outside core constructs one — configuration is the whole surface.\n */\n logger?: Partial<LoggerConfig>;\n}\n\n/**\n * Build an independent router instance that shares the route tree, options,\n * lifecycle guards, and plugin factories of `router`. The primary use case\n * is **SSR multi-tenancy** — one base router per process, one clone per\n * request.\n *\n * @param router - Source router (must not be disposed).\n * @param dependencies - Optional per-clone overrides merged on top of the\n * base router's dependencies. Always **fresh per call** in the documented\n * SSR pattern: pass per-request state here, never store it in the base.\n *\n * @remarks\n *\n * **Dependency merge — shallow by design.** `base.dependencies` are spread\n * into the clone via `{ ...sourceDeps, ...dependencies }`. Top-level keys\n * are new objects, but **values are shared by reference**: a `Map`, `Set`,\n * class instance, function, or nested plain object stored in\n * `base.dependencies` is the **same instance** in every clone. Mutations\n * in one clone are visible in the base and in every sibling clone.\n *\n * This is intentional. `structuredClone` of dep values is **not** applied\n * because it would:\n * - strip class prototypes (`new DbClient()` → plain object, methods lost)\n * - reject functions and symbols (`DataCloneError`)\n * - fragment singleton pools (one connection pool per request — pool\n * semantics destroyed)\n * - reject circular references\n *\n * **SSR rule of thumb.** Place values in `base.dependencies` according to\n * their lifecycle:\n *\n * - **Singletons / shared services** → `base.dependencies`. Examples: DB\n * client, connection pool, logger, config, feature-flag client. Process-\n * wide pooling depends on sharing these by reference.\n * - **Per-request state** → the `dependencies` override parameter (or\n * `createRequestScope`'s `deps` argument). Examples: `currentUser`,\n * `traceId`, `sessionId`, `abortSignal`. The override is applied last,\n * so it wins over base keys; pass a fresh object per call.\n *\n * Cross-request data leaks are **only possible** when per-request mutable\n * state is incorrectly placed in `base.dependencies`. The override slot is\n * the safe channel.\n *\n * @example\n * ```typescript\n * // Server boot — singletons only\n * const base = createRouter(routes, options, {\n * db: new DbClient(dbUrl),\n * logger,\n * });\n *\n * // Per request — fresh override per call\n * const clone = cloneRouter(base, {\n * currentUser,\n * traceId,\n * });\n * // clone.deps.db === base.deps.db ✓ shared pool (intentional)\n * // clone.deps.currentUser ✓ unique per request\n * ```\n *\n * @see createRequestScope — `@real-router/ssr-utils` SSR helper that\n * wraps this function and injects `abortSignal` automatically.\n */\nexport function cloneRouter<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n router: Router<Dependencies>,\n dependencies?: Dependencies,\n opts?: CloneOptions,\n): RouterClass<Dependencies> {\n const ctx = getInternals(router);\n\n if (ctx.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n ctx.validator?.dependencies.validateCloneArgs(dependencies);\n\n // Get source store directly\n const sourceStore = ctx.routeGetStore();\n const routes = routeTreeToDefinitions(sourceStore.tree);\n const routeConfig = sourceStore.config;\n const resolvedForwardMap = sourceStore.resolvedForwardMap;\n const routeCustomFields = sourceStore.routeCustomFields;\n\n const {\n options,\n dependencies: sourceDeps,\n pluginFactories,\n loggerConfig,\n } = ctx.getCloneState();\n // Origin-aware factory snapshot — definition guards are re-registered with\n // `isFromDefinition=true` on the clone so `replace()` can still strip them\n // via `clearDefinitionGuards()`. External guards take the public lifecycle\n // API path so they survive `replace()` symmetric with the base.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const sourceLifecycleNamespace = sourceStore.lifecycleNamespace!;\n const { definition: definitionFactories, external: externalFactories } =\n sourceLifecycleNamespace.getFactoriesByOrigin();\n\n const mergedDeps = {\n ...sourceDeps,\n ...dependencies,\n } as Dependencies;\n\n // The clone builds its OWN logger (isolation, #724) but INHERITS the base's\n // resolved config — frozen options don't carry `logger`, so without this the\n // clone would fall back to the default logger and lose the base's\n // callback/level (an M1 regression the singleton used to mask). A per-request\n // `opts.logger` override (e.g. a traceId-bound callback) merges on top.\n const clonedLoggerConfig: Partial<LoggerConfig> = opts?.logger\n ? { ...loggerConfig, ...opts.logger }\n : loggerConfig;\n\n const newRouter = new RouterClass<Dependencies>(\n routes as Route<Dependencies>[],\n { ...options, logger: clonedLoggerConfig },\n mergedDeps,\n );\n\n const newCtx = getInternals(newRouter);\n const newStore = newCtx.routeGetStore();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const newLifecycleNamespace = newStore.lifecycleNamespace!;\n\n // Copy the source config + store-level maps BEFORE re-registering guards\n // (#1331 review): the definition-guard factories re-executed below must\n // observe the fully-built clone (encoders/decoders/defaultParams/custom\n // fields), mirroring the constructor where flushPendingGuards runs after the\n // store is complete. The five RouteConfig sub-maps go through a single\n // enumeration so a newly added config field is carried over automatically\n // (#965); resolvedForwardMap and routeCustomFields are store-level (not part\n // of RouteConfig) and stay explicit.\n assignConfigEntries(newStore.config, routeConfig);\n Object.assign(newStore.resolvedForwardMap, resolvedForwardMap);\n Object.assign(newStore.routeCustomFields, routeCustomFields);\n\n // #1175: carry the source rootPath. It lives in the store (not options/config),\n // and neither routeTreeToDefinitions nor getCloneState include it — so a clone\n // of a base configured with `setRootPath(\"/app\")` would otherwise build/match\n // under \"\" and 404 every request of a sub-path SSR deployment. setRootPath\n // rebuilds the tree in place with the just-copied config; the rebuild is only\n // paid when a rootPath is actually set, and it runs before the definition-guard\n // factories below so they observe the fully-built clone (rootPath included).\n if (sourceStore.rootPath !== \"\") {\n newCtx.setRootPath(sourceStore.rootPath);\n }\n\n const [definitionDeactivate, definitionActivate] = definitionFactories;\n const [externalDeactivate, externalActivate] = externalFactories;\n\n for (const [name, handler] of Object.entries(definitionDeactivate)) {\n newLifecycleNamespace.addCanDeactivate(name, handler, true);\n }\n\n for (const [name, handler] of Object.entries(definitionActivate)) {\n newLifecycleNamespace.addCanActivate(name, handler, true);\n }\n\n const lifecycle = getLifecycleApi(newRouter);\n\n for (const [name, handler] of Object.entries(externalDeactivate)) {\n lifecycle.addDeactivateGuard(name, handler);\n }\n\n for (const [name, handler] of Object.entries(externalActivate)) {\n lifecycle.addActivateGuard(name, handler);\n }\n\n // Plugin replay runs last and skips factories that a (contract-violating)\n // definition-guard factory already registered on the clone during the\n // re-compilation above — without the filter every clone would double-apply\n // such a plugin: once via the factory, once via this replay (#1331 review).\n const alreadyRegistered = new Set(newCtx.getCloneState().pluginFactories);\n const pluginsToReplay = pluginFactories.filter(\n (factory) => !alreadyRegistered.has(factory),\n );\n\n // Stryker disable next-line EqualityOperator: equivalent — `>= 0` is always true, but `usePlugin(...[])` with an empty spread is a no-op, so entering the block on an empty list behaves identically to skipping it. (ConditionalExpression stays live: `→false` skips a real plugin list and is killable.)\n if (pluginsToReplay.length > 0) {\n newRouter.usePlugin(...pluginsToReplay);\n }\n\n return newRouter;\n}\n"],"mappings":"6QA+FA,SAAgB,EAGd,EACA,EACA,EAC2B,CAC3B,IAAM,EAAMA,EAAAA,aAAa,CAAM,EAE/B,GAAI,EAAI,WAAW,EACjB,MAAM,IAAIC,EAAAA,YAAYC,EAAAA,WAAW,eAAe,EAGlD,EAAI,WAAW,aAAa,kBAAkB,CAAY,EAG1D,IAAM,EAAc,EAAI,cAAc,EAChC,EAASC,EAAAA,uBAAuB,EAAY,IAAI,EAChD,EAAc,EAAY,OAC1B,EAAqB,EAAY,mBACjC,EAAoB,EAAY,kBAEhC,CACJ,UACA,aAAc,EACd,kBACA,gBACE,EAAI,cAAc,EAOhB,CAAE,WAAY,EAAqB,SAAU,GADlB,EAAY,mBAElB,qBAAqB,EAE1C,EAAa,CACjB,GAAG,EACH,GAAG,CACL,EAOM,EAA4C,GAAM,OACpD,CAAE,GAAG,EAAc,GAAG,EAAK,MAAO,EAClC,EAEE,EAAY,IAAIC,EAAAA,OACpB,EACA,CAAE,GAAG,EAAS,OAAQ,CAAmB,EACzC,CACF,EAEM,EAASJ,EAAAA,aAAa,CAAS,EAC/B,EAAW,EAAO,cAAc,EAEhC,EAAwB,EAAS,mBAUvC,EAAA,oBAAoB,EAAS,OAAQ,CAAW,EAChD,OAAO,OAAO,EAAS,mBAAoB,CAAkB,EAC7D,OAAO,OAAO,EAAS,kBAAmB,CAAiB,EASvD,EAAY,WAAa,IAC3B,EAAO,YAAY,EAAY,QAAQ,EAGzC,GAAM,CAAC,EAAsB,GAAsB,EAC7C,CAAC,EAAoB,GAAoB,EAE/C,IAAK,GAAM,CAAC,EAAM,KAAY,OAAO,QAAQ,CAAoB,EAC/D,EAAsB,iBAAiB,EAAM,EAAS,EAAI,EAG5D,IAAK,GAAM,CAAC,EAAM,KAAY,OAAO,QAAQ,CAAkB,EAC7D,EAAsB,eAAe,EAAM,EAAS,EAAI,EAG1D,IAAM,EAAYK,EAAAA,gBAAgB,CAAS,EAE3C,IAAK,GAAM,CAAC,EAAM,KAAY,OAAO,QAAQ,CAAkB,EAC7D,EAAU,mBAAmB,EAAM,CAAO,EAG5C,IAAK,GAAM,CAAC,EAAM,KAAY,OAAO,QAAQ,CAAgB,EAC3D,EAAU,iBAAiB,EAAM,CAAO,EAO1C,IAAM,EAAoB,IAAI,IAAI,EAAO,cAAc,CAAC,CAAC,eAAe,EAClE,EAAkB,EAAgB,OACrC,GAAY,CAAC,EAAkB,IAAI,CAAO,CAC7C,EAOA,OAJI,EAAgB,OAAS,GAC3B,EAAU,UAAU,GAAG,CAAe,EAGjC,CACT"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../internals.js"),t=require("./helpers.js");function n(e,t,n,r){if(n!==void 0){if(!Object.hasOwn(e.dependencies,t))r?.dependencies.validateDependencyCount(e,`setDependency`);else{let i=e.dependencies[t];i!==n&&!(Number.isNaN(i)&&Number.isNaN(n))&&r?.dependencies.warnOverwrite(t,`setDependency`)}e.dependencies[t]=n}}function r(e,t,n){let r=[];for(let i in t)t[i]!==void 0&&(Object.hasOwn(e.dependencies,i)?r.push(i):n?.dependencies.validateDependencyCount(e,`setDependencies`),e.dependencies[i]=t[i]);r.length>0&&n?.dependencies.warnBatchOverwrite(r,`setDependencies`)}function i(i){let a=e.getInternals(i);return{get:e=>{a.validator?.dependencies.validateDependencyName(e,`getDependency`);let t=a.dependenciesGetStore(),n=t.dependencies[e];return a.validator?.dependencies.validateDependencyExists(e,t),n},getAll:()=>({...a.dependenciesGetStore().dependencies}),set:(e,r)=>{t.throwIfDisposed(a.isDisposed),a.validator?.dependencies.validateSetDependencyArgs(e,r,`setDependency`),n(a.dependenciesGetStore(),e,r,a.validator)},setAll:e=>{t.throwIfDisposed(a.isDisposed);let n=a.dependenciesGetStore();a.validator?.dependencies.validateDependenciesObject(e,`setDependencies`),r(n,e,a.validator)},remove:e=>{t.throwIfDisposed(a.isDisposed),a.validator?.dependencies.validateDependencyName(e,`removeDependency`);let n=a.dependenciesGetStore();Object.hasOwn(n.dependencies,e)||a.validator?.dependencies.warnRemoveNonExistent(e),delete n.dependencies[e]},reset:()=>{t.throwIfDisposed(a.isDisposed);let e=a.dependenciesGetStore();e.dependencies=Object.create(null)},has:e=>(a.validator?.dependencies.validateDependencyName(e,`hasDependency`),Object.hasOwn(a.dependenciesGetStore().dependencies,e))}}exports.getDependenciesApi=i;
|
|
2
|
-
//# sourceMappingURL=getDependenciesApi.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getDependenciesApi.js","names":["getInternals"],"sources":["../../../src/api/getDependenciesApi.ts"],"sourcesContent":["import { throwIfDisposed } from \"./helpers\";\nimport { getInternals } from \"../internals\";\n\nimport type { DependenciesApi } from \"./types\";\nimport type { DependenciesStore } from \"../namespaces\";\nimport type { DefaultDependencies, Router } from \"../types\";\nimport type { RouterValidator } from \"../types/RouterValidator\";\n\n// =============================================================================\n// Module-private CRUD functions\n// =============================================================================\n\nfunction setDependency(\n store: DependenciesStore,\n dependencyName: string,\n dependencyValue: unknown,\n validator?: RouterValidator | null,\n): void {\n // undefined = \"don't set\" (feature for conditional setting)\n if (dependencyValue === undefined) {\n return;\n }\n\n const isNewKey = !Object.hasOwn(store.dependencies, dependencyName);\n\n if (isNewKey) {\n // Only check limit when adding new keys (overwrites don't increase count)\n validator?.dependencies.validateDependencyCount(store, \"setDependency\");\n } else {\n const oldValue = (store.dependencies as Record<string, unknown>)[\n dependencyName\n ];\n const isChanging = oldValue !== dependencyValue;\n // Special case for NaN idempotency (NaN !== NaN is always true)\n const bothAreNaN = Number.isNaN(oldValue) && Number.isNaN(dependencyValue);\n\n if (isChanging && !bothAreNaN) {\n validator?.dependencies.warnOverwrite(dependencyName, \"setDependency\");\n }\n }\n\n (store.dependencies as Record<string, unknown>)[dependencyName] =\n dependencyValue;\n}\n\nfunction setMultipleDependencies(\n store: DependenciesStore,\n deps: Record<string, unknown>,\n validator?: RouterValidator | null,\n): void {\n const overwrittenKeys: string[] = [];\n\n for (const key in deps) {\n if (deps[key] === undefined) {\n continue;\n }\n\n if (Object.hasOwn(store.dependencies, key)) {\n overwrittenKeys.push(key);\n } else {\n validator?.dependencies.validateDependencyCount(store, \"setDependencies\");\n }\n\n (store.dependencies as Record<string, unknown>)[key] = deps[key];\n }\n\n if (overwrittenKeys.length > 0) {\n validator?.dependencies.warnBatchOverwrite(\n overwrittenKeys,\n \"setDependencies\",\n );\n }\n}\n\n// =============================================================================\n// Public API factory\n// =============================================================================\n\nexport function getDependenciesApi<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(router: Router<Dependencies>): DependenciesApi<Dependencies> {\n const ctx = getInternals(router);\n\n return {\n get: (name) => {\n ctx.validator?.dependencies.validateDependencyName(name, \"getDependency\");\n\n const store = ctx.dependenciesGetStore();\n const value = (store.dependencies as Record<string, unknown>)[\n name as string\n ];\n\n ctx.validator?.dependencies.validateDependencyExists(\n name as string,\n store,\n );\n\n return value as Dependencies[typeof name];\n },\n getAll: () => ({ ...ctx.dependenciesGetStore().dependencies }),\n set: (name, value) => {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.dependencies.validateSetDependencyArgs(\n name,\n value,\n \"setDependency\",\n );\n\n setDependency(ctx.dependenciesGetStore(), name, value, ctx.validator);\n },\n setAll: (deps) => {\n throwIfDisposed(ctx.isDisposed);\n\n const store = ctx.dependenciesGetStore();\n\n ctx.validator?.dependencies.validateDependenciesObject(\n deps,\n \"setDependencies\",\n );\n\n setMultipleDependencies(\n store,\n deps as Record<string, unknown>,\n ctx.validator,\n );\n },\n remove: (name) => {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.dependencies.validateDependencyName(\n name,\n \"removeDependency\",\n );\n\n const store = ctx.dependenciesGetStore();\n\n if (!Object.hasOwn(store.dependencies, name)) {\n ctx.validator?.dependencies.warnRemoveNonExistent(name);\n }\n\n delete (store.dependencies as Record<string, unknown>)[name as string];\n },\n reset: () => {\n throwIfDisposed(ctx.isDisposed);\n const store = ctx.dependenciesGetStore();\n\n store.dependencies = Object.create(null) as Partial<Dependencies>;\n },\n has: (name) => {\n ctx.validator?.dependencies.validateDependencyName(name, \"hasDependency\");\n\n return Object.hasOwn(ctx.dependenciesGetStore().dependencies, name);\n },\n };\n}\n"],"mappings":"6DAYA,SAAS,EACP,EACA,EACA,EACA,EACM,CAEF,OAAoB,IAAA,GAMxB,IAAI,CAFc,OAAO,OAAO,EAAM,aAAc,CAAc,EAIhE,GAAW,aAAa,wBAAwB,EAAO,eAAe,MACjE,CACL,IAAM,EAAY,EAAM,aACtB,GAEiB,IAAa,GAId,EAFC,OAAO,MAAM,CAAQ,GAAK,OAAO,MAAM,CAAe,IAGvE,GAAW,aAAa,cAAc,EAAgB,eAAe,CAEzE,CAEA,EAAO,aAAyC,GAC9C,CAHF,CAIF,CAEA,SAAS,EACP,EACA,EACA,EACM,CACN,IAAM,EAA4B,CAAC,EAEnC,IAAK,IAAM,KAAO,EACZ,EAAK,KAAS,IAAA,KAId,OAAO,OAAO,EAAM,aAAc,CAAG,EACvC,EAAgB,KAAK,CAAG,EAExB,GAAW,aAAa,wBAAwB,EAAO,iBAAiB,EAG1E,EAAO,aAAyC,GAAO,EAAK,IAG1D,EAAgB,OAAS,GAC3B,GAAW,aAAa,mBACtB,EACA,iBACF,CAEJ,CAMA,SAAgB,EAEd,EAA6D,CAC7D,IAAM,EAAMA,EAAAA,aAAa,CAAM,EAE/B,MAAO,CACL,IAAM,GAAS,CACb,EAAI,WAAW,aAAa,uBAAuB,EAAM,eAAe,EAExE,IAAM,EAAQ,EAAI,qBAAqB,EACjC,EAAS,EAAM,aACnB,GAQF,OALA,EAAI,WAAW,aAAa,yBAC1B,EACA,CACF,EAEO,CACT,EACA,YAAe,CAAE,GAAG,EAAI,qBAAqB,CAAC,CAAC,YAAa,GAC5D,KAAM,EAAM,IAAU,CACpB,EAAA,gBAAgB,EAAI,UAAU,EAE9B,EAAI,WAAW,aAAa,0BAC1B,EACA,EACA,eACF,EAEA,EAAc,EAAI,qBAAqB,EAAG,EAAM,EAAO,EAAI,SAAS,CACtE,EACA,OAAS,GAAS,CAChB,EAAA,gBAAgB,EAAI,UAAU,EAE9B,IAAM,EAAQ,EAAI,qBAAqB,EAEvC,EAAI,WAAW,aAAa,2BAC1B,EACA,iBACF,EAEA,EACE,EACA,EACA,EAAI,SACN,CACF,EACA,OAAS,GAAS,CAChB,EAAA,gBAAgB,EAAI,UAAU,EAE9B,EAAI,WAAW,aAAa,uBAC1B,EACA,kBACF,EAEA,IAAM,EAAQ,EAAI,qBAAqB,EAElC,OAAO,OAAO,EAAM,aAAc,CAAI,GACzC,EAAI,WAAW,aAAa,sBAAsB,CAAI,EAGxD,OAAQ,EAAM,aAAyC,EACzD,EACA,UAAa,CACX,EAAA,gBAAgB,EAAI,UAAU,EAC9B,IAAM,EAAQ,EAAI,qBAAqB,EAEvC,EAAM,aAAe,OAAO,OAAO,IAAI,CACzC,EACA,IAAM,IACJ,EAAI,WAAW,aAAa,uBAAuB,EAAM,eAAe,EAEjE,OAAO,OAAO,EAAI,qBAAqB,CAAC,CAAC,aAAc,CAAI,EAEtE,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../internals.js"),t=require("./helpers.js");function n(n){let r=e.getInternals(n),i=r.routeGetStore().lifecycleNamespace;return{addActivateGuard(e,n){t.throwIfDisposed(r.isDisposed),r.validator?.routes.validateRouteName(e,`addActivateGuard`),r.validator?.lifecycle.validateHandler(n,`addActivateGuard`),i.addCanActivate(e,n)},addDeactivateGuard(e,n){t.throwIfDisposed(r.isDisposed),r.validator?.routes.validateRouteName(e,`addDeactivateGuard`),r.validator?.lifecycle.validateHandler(n,`addDeactivateGuard`),i.addCanDeactivate(e,n)},removeActivateGuard(e){t.throwIfDisposed(r.isDisposed),r.validator?.routes.validateRouteName(e,`removeActivateGuard`),i.clearCanActivate(e,`external`)},removeDeactivateGuard(e){t.throwIfDisposed(r.isDisposed),r.validator?.routes.validateRouteName(e,`removeDeactivateGuard`),i.clearCanDeactivate(e,`external`)}}}exports.getLifecycleApi=n;
|
|
2
|
-
//# sourceMappingURL=getLifecycleApi.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getLifecycleApi.js","names":["getInternals"],"sources":["../../../src/api/getLifecycleApi.ts"],"sourcesContent":["import { throwIfDisposed } from \"./helpers\";\nimport { getInternals } from \"../internals\";\n\nimport type { LifecycleApi } from \"./types\";\nimport type { DefaultDependencies, Router } from \"../types\";\n\nexport function getLifecycleApi<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(router: Router<Dependencies>): LifecycleApi<Dependencies> {\n const ctx = getInternals(router);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const lifecycleNamespace = ctx.routeGetStore().lifecycleNamespace!;\n\n return {\n addActivateGuard(name, handler) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"addActivateGuard\");\n ctx.validator?.lifecycle.validateHandler(handler, \"addActivateGuard\");\n\n // Handler-limit enforcement lives at the namespace registration choke point\n // (RouteLifecycleNamespace.#registerHandler) so all paths are bounded\n // uniformly — see #961.\n lifecycleNamespace.addCanActivate(name, handler);\n },\n\n addDeactivateGuard(name, handler) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"addDeactivateGuard\");\n ctx.validator?.lifecycle.validateHandler(handler, \"addDeactivateGuard\");\n\n lifecycleNamespace.addCanDeactivate(name, handler);\n },\n\n removeActivateGuard(name) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"removeActivateGuard\");\n\n // Inverse of addActivateGuard (external): clears only the external guard;\n // a route-config (definition) canActivate survives (#1171).\n lifecycleNamespace.clearCanActivate(name, \"external\");\n },\n\n removeDeactivateGuard(name) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"removeDeactivateGuard\");\n\n // Inverse of addDeactivateGuard (external): clears only the external guard;\n // a route-config (definition) canDeactivate survives (#1171).\n lifecycleNamespace.clearCanDeactivate(name, \"external\");\n },\n };\n}\n"],"mappings":"6DAMA,SAAgB,EAEd,EAA0D,CAC1D,IAAM,EAAMA,EAAAA,aAAa,CAAM,EAEzB,EAAqB,EAAI,cAAc,CAAC,CAAC,mBAE/C,MAAO,CACL,iBAAiB,EAAM,EAAS,CAC9B,EAAA,gBAAgB,EAAI,UAAU,EAE9B,EAAI,WAAW,OAAO,kBAAkB,EAAM,kBAAkB,EAChE,EAAI,WAAW,UAAU,gBAAgB,EAAS,kBAAkB,EAKpE,EAAmB,eAAe,EAAM,CAAO,CACjD,EAEA,mBAAmB,EAAM,EAAS,CAChC,EAAA,gBAAgB,EAAI,UAAU,EAE9B,EAAI,WAAW,OAAO,kBAAkB,EAAM,oBAAoB,EAClE,EAAI,WAAW,UAAU,gBAAgB,EAAS,oBAAoB,EAEtE,EAAmB,iBAAiB,EAAM,CAAO,CACnD,EAEA,oBAAoB,EAAM,CACxB,EAAA,gBAAgB,EAAI,UAAU,EAE9B,EAAI,WAAW,OAAO,kBAAkB,EAAM,qBAAqB,EAInE,EAAmB,iBAAiB,EAAM,UAAU,CACtD,EAEA,sBAAsB,EAAM,CAC1B,EAAA,gBAAgB,EAAI,UAAU,EAE9B,EAAI,WAAW,OAAO,kBAAkB,EAAM,uBAAuB,EAIrE,EAAmB,mBAAmB,EAAM,UAAU,CACxD,CACF,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../constants.js"),t=require("../internals.js"),n=require("../pipeline/canonicalize.js"),r=require("../pipeline/buildURL.js"),i=require("../pipeline/materialize.js"),a=require("../RouterError.js"),o=require("./helpers.js"),s=new WeakMap;function c(c){let l=s.get(c);if(l)return l;let u=t.getInternals(c),d={makeState:(e,n,r,i)=>(t.throwOnMisChanneledKey(u,`makeState`,e,n),u.validator?.state.validateMakeStateArgs(e,n,i),u.makeState(e,n,r,i)),forwardState:(e,t,n)=>(u.validator?.routes.validateStateBuilderArgs(e,t,`forwardState`),u.forwardState(e,t,n)),matchPath:e=>(u.validator?.routes.validateMatchPathArgs(e),u.matchPath(e,u.getOptions())),navigateToState:(e,t)=>(o.throwIfDisposed(u.isDisposed),u.validator?.navigation.validateNavigateToStateArgs(e),t!==void 0&&u.validator?.navigation.validateNavigationOptions(t,`navigateToState`),u.navigateToState(e,t)),setRootPath:e=>{o.throwIfDisposed(u.isDisposed),u.validator?.routes.validateSetRootPathArgs(e),u.setRootPath(e)},getRootPath:u.getRootPath,addEventListener:(e,t)=>(o.throwIfDisposed(u.isDisposed),u.validator?.eventBus.validateListenerArgs(e,t),u.addEventListener(e,t)),buildNavigationState:(e,a={},o={})=>{t.throwOnMisChanneledKey(u,`buildNavigationState`,e,a),u.validator?.routes.validateStateBuilderArgs(e,a,`buildNavigationState`);let s=n.canonicalize(u.port(),e,a,o,{diagnoseUndeclared:!0});if(u.buildStateResolved(s.name,s.path))return i.materialize(s,{path:r.buildURL(s,u.port())})},getOptions:u.getOptions,getTree:u.getTree,addInterceptor:(e,t)=>{o.throwIfDisposed(u.isDisposed),u.validator?.plugins.validateAddInterceptorArgs(e,t);let n=u.interceptors.get(e);n||(n=[],u.interceptors.set(e,n)),n.push(t);let r=!1;return()=>{r||(r=!0,n.splice(n.indexOf(t),1))}},getRouteConfig:e=>{let t=u.routeGetStore();if(t.matcher.hasRoute(e))return t.routeCustomFields[e]},extendRouter:t=>{o.throwIfDisposed(u.isDisposed);let n=Object.keys(t);for(let t of n)if(t in c)throw new a.RouterError(e.errorCodes.PLUGIN_CONFLICT,{message:`Cannot extend router: property "${t}" already exists`});for(let e of n)c[e]=t[e];let r={keys:n};u.routerExtensions.push(r);let i=!1;return()=>{if(i)return;i=!0;for(let e of r.keys)delete c[e];let e=u.routerExtensions.indexOf(r);e!==-1&&u.routerExtensions.splice(e,1)}},emitTransitionError:e=>{o.throwIfDisposed(u.isDisposed),u.emitTransitionError(e)},claimContextNamespace:t=>{if(o.throwIfDisposed(u.isDisposed),typeof t!=`string`||t===``)throw TypeError(`[claimContextNamespace] namespace must be a non-empty string, got ${typeof t==`string`?`an empty string`:typeof t}`);if(u.contextClaimRecords.has(t))throw new a.RouterError(e.errorCodes.CONTEXT_NAMESPACE_ALREADY_CLAIMED,{message:`Cannot claim context namespace: "${t}" is already claimed by another plugin`});return u.contextClaimRecords.add(t),{write(e,n){t===`__proto__`?Object.defineProperty(e.context,t,{value:n,writable:!0,enumerable:!0,configurable:!0}):e.context[t]=n},release(){u.contextClaimRecords.delete(t)}}}};return s.set(c,d),d}exports.getPluginApi=c;
|
|
2
|
-
//# sourceMappingURL=getPluginApi.js.map
|