@real-router/core 0.80.0 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -45
- package/dist/cjs/Router-BIYDZRgF.js +2 -0
- package/dist/cjs/Router-BIYDZRgF.js.map +1 -0
- package/dist/cjs/Router.js +1 -1
- package/dist/cjs/Router.js.map +1 -1
- package/dist/cjs/buildParamMeta-DniluAIV.js.map +1 -1
- package/dist/cjs/engine/builder/buildTree.js +1 -1
- package/dist/cjs/engine/builder/buildTree.js.map +1 -1
- package/dist/cjs/engine/path-matcher/registration/trieNodes.js.map +1 -1
- package/dist/cjs/internals.js.map +1 -1
- package/dist/cjs/namespaces/EventBusNamespace/EventBusNamespace.js.map +1 -1
- package/dist/cjs/routerFSM.js +1 -1
- package/dist/cjs/routerFSM.js.map +1 -1
- package/dist/cjs/types/router.d.ts +1 -1
- package/dist/cjs/utils/event-emitter/EventEmitter.js.map +1 -0
- package/dist/cjs/utils/fsm/fsm.js.map +1 -0
- package/dist/cjs/utils/logger/RouterLogger.js.map +1 -0
- package/dist/cjs/utils/logger/constants.js.map +1 -0
- package/dist/esm/Router-BIui2eew.mjs +2 -0
- package/dist/esm/Router-BIui2eew.mjs.map +1 -0
- package/dist/esm/api.mjs +1 -1
- package/dist/esm/buildParamMeta-bOLhLF9h.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/types/router.d.mts +1 -1
- package/package.json +1 -1
- package/dist/cjs/Router-DGUed2F4.js +0 -2
- package/dist/cjs/Router-DGUed2F4.js.map +0 -1
- package/dist/cjs/foundation/event-emitter/EventEmitter.js.map +0 -1
- package/dist/cjs/foundation/fsm/fsm.js.map +0 -1
- package/dist/cjs/foundation/logger/RouterLogger.js.map +0 -1
- package/dist/cjs/foundation/logger/constants.js.map +0 -1
- package/dist/esm/Router-Bk6PfSE8.mjs +0 -2
- package/dist/esm/Router-Bk6PfSE8.mjs.map +0 -1
- /package/dist/cjs/{foundation → utils}/event-emitter/EventEmitter.js +0 -0
- /package/dist/cjs/{foundation → utils}/fsm/fsm.js +0 -0
- /package/dist/cjs/{foundation → utils}/logger/RouterLogger.js +0 -0
- /package/dist/cjs/{foundation → utils}/logger/constants.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventBusNamespace.js","names":["RouterError","errorCodes","#fsm","#emitter","#abortController","#leaveListeners","#currentToState","#setupFSMActions","events","#dispatchDepth","routerEvents","#pendingToState","#pendingFromState","#pendingError","#pendingCancelReason","routerStates","#checkListenerThreshold","#getValidator","#emitPendingError"],"sources":["../../../../src/namespaces/EventBusNamespace/EventBusNamespace.ts"],"sourcesContent":["// packages/core/src/namespaces/EventBusNamespace/EventBusNamespace.ts\n\nimport { errorCodes, events } from \"../../constants\";\nimport { RouterError } from \"../../RouterError\";\nimport { routerEvents, routerStates } from \"../../routerFSM\";\n\nimport type { EventBusOptions } from \"./types\";\nimport type { EventEmitter } from \"../../foundation/event-emitter\";\nimport type { FSM } from \"../../foundation/fsm\";\nimport type { RouterEvent, RouterPayloads, RouterState } from \"../../routerFSM\";\nimport type {\n EventName,\n LeaveFn,\n LeaveState,\n NavigationOptions,\n Plugin,\n State,\n SubscribeFn,\n TreeChangedEvent,\n Unsubscribe,\n EventMethodMap,\n} from \"../../types\";\nimport type { RouterEventMap } from \"../../types/internal\";\nimport type { RouterValidator } from \"../../types/RouterValidator\";\n\n/**\n * Internal-only event key for route-tree mutations. Lives on the same\n * `EventEmitter` as the 7 transition events but never enters the public\n * `EventName` union — reachable only through\n * `getRoutesApi(router).subscribeChanges()`.\n */\nconst TREE_CHANGED = \"TREE_CHANGED\";\n\nfunction ensureError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value));\n}\n\nfunction settleLeavePromises(\n promises: Promise<void>[],\n firstSyncError: unknown,\n signal: AbortSignal,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n // #1197 — canonicalize the abort outcome: reject with a\n // `RouterError(TRANSITION_CANCELLED)` carrying the external reason, so the\n // no-guards leave path classifies identically to the guard path (no raw\n // reject, no spurious TRANSITION_ERROR). Internal sources\n // (supersede/stop/dispose) already abort with such a RouterError — thread it\n // through unchanged so the #943 reason is preserved.\n const onAbort = (): void => {\n const reason: unknown = signal.reason;\n\n reject(\n reason instanceof RouterError &&\n reason.code === errorCodes.TRANSITION_CANCELLED\n ? reason\n : new RouterError(errorCodes.TRANSITION_CANCELLED, { reason }),\n );\n };\n\n if (signal.aborted) {\n onAbort();\n\n return;\n }\n\n // Stryker disable next-line ObjectLiteral,BooleanLiteral: equivalent — `{ once: true }` is redundant: onAbort fires at most once (a signal aborts once) and the success path explicitly removeEventListener's it, so dropping `once` is unobservable. StringLiteral sibling stays live (the \"abort\" event name is killed).\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n void Promise.allSettled(promises).then((results) => {\n // Stryker disable next-line StringLiteral: equivalent — this cleanup name is redundant: onAbort is registered with `{ once: true }` and the per-navigation signal is discarded unaborted on success, so failing to remove the listener leaks nothing observable.\n signal.removeEventListener(\"abort\", onAbort);\n\n // Stryker disable next-line BlockStatement: equivalent — emptying the post-allSettled abort-race early-return falls through to resolve()/reject(), but the abort handler already settled the promise, so the extra settle is a no-op. CE sibling stays live (→true hangs the pipeline = killed via timeout).\n if (signal.aborted) {\n // Race lost to abort — the abort handler already rejected; do nothing\n return;\n }\n\n if (firstSyncError !== undefined) {\n reject(ensureError(firstSyncError));\n\n return;\n }\n\n const rejected = results.find(\n (result): result is PromiseRejectedResult =>\n result.status === \"rejected\",\n );\n\n if (rejected !== undefined) {\n reject(ensureError(rejected.reason));\n\n return;\n }\n\n resolve();\n });\n });\n}\n\nexport class EventBusNamespace {\n readonly #fsm: FSM<RouterState, RouterEvent, null, RouterPayloads>;\n readonly #emitter: EventEmitter<RouterEventMap>;\n // Effect of the FSM CANCEL action: aborts the in-flight navigation's\n // controller. Wired to NavigationNamespace.\n readonly #abortController: (reason?: unknown) => void;\n // Lazy accessor for the opt-in RouterValidator (wired by wireNamespaces).\n // Returns `null` until validation-plugin is registered — so the proactive\n // listener-count threshold (#1188) costs the no-plugin path nothing.\n #getValidator: (() => RouterValidator | null) | undefined;\n readonly #leaveListeners: LeaveFn[] = [];\n\n // Depth of the synchronous transition-dispatch window — elevated while a\n // transition event is being emitted (`emitTransition*`) or a `subscribeLeave`\n // listener batch runs. `isProcessing()` reads it so the navigation facade can\n // reject a synchronous reentrant navigate() from inside a transition listener\n // with REENTRANT_NAVIGATION (RFC navigation-cancellation-unification §4). A\n // counter (not a boolean) tolerates legitimately-nested transition emits. The\n // ceiling-bounded \"allow reentrant navigate, throw RecursionDepthError at\n // `maxEventDepth`\" behaviour (#935/#945) is gone: such a navigate now throws\n // REENTRANT_NAVIGATION at depth 1, before it can recurse. (The emitter's old\n // `maxEventDepth` depth-bound is gone too — re-entrant emits are coalesced to a\n // no-op at the emitter, #1033 — so no event can re-enter its own dispatch.)\n #dispatchDepth = 0;\n\n #currentToState: State | undefined;\n #pendingToState: State | undefined;\n #pendingFromState: State | undefined;\n #pendingError: unknown;\n // Abort reason for the pending CANCEL — read by handleCancel, set by sendCancel.\n #pendingCancelReason: unknown;\n\n constructor(options: EventBusOptions) {\n this.#fsm = options.routerFSM;\n this.#emitter = options.emitter;\n this.#abortController = options.abortController;\n this.#currentToState = undefined;\n this.#setupFSMActions();\n }\n\n static validateSubscribeListener(listener: unknown): void {\n if (typeof listener !== \"function\") {\n throw new TypeError(\n \"[router.subscribe] Expected a function. \" +\n \"For Observable pattern use observable(router) from @real-router/rx\",\n );\n }\n }\n\n /**\n * Validates the `subscribeLeave` listener. Unlike\n * {@link validateSubscribeListener}, the error carries **no**\n * `@real-router/rx` hint — rx exposes the Observable pattern for *success*\n * transitions (`observable(router)`, `state$`, `events$`), not for leave\n * events, so steering leave-listener misuse toward rx would mislead. The\n * asymmetry is intentional (mirrored in `core/CLAUDE.md`).\n */\n static validateSubscribeLeaveListener(listener: unknown): void {\n if (typeof listener !== \"function\") {\n throw new TypeError(\"[router.subscribeLeave] Expected a function\");\n }\n }\n\n emitRouterStart(): void {\n this.#emitter.emit(events.ROUTER_START);\n }\n\n emitRouterStop(): void {\n this.#emitter.emit(events.ROUTER_STOP);\n }\n\n emitTransitionStart(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_START, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionSuccess(\n toState: State,\n fromState?: State,\n opts?: NavigationOptions,\n ): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_SUCCESS, toState, fromState, opts);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionError(\n toState?: State,\n fromState?: State,\n error?: RouterError,\n ): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_ERROR, toState, fromState, error);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionCancel(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_CANCEL, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionLeaveApprove(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_LEAVE_APPROVE, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n /**\n * True while a transition event is being dispatched synchronously — an\n * `emitTransition*` call or a `subscribeLeave` listener batch is on the stack.\n * The navigation facade reads this to reject a synchronous reentrant\n * navigate() from inside a transition listener (RFC §4).\n */\n isProcessing(): boolean {\n return this.#dispatchDepth > 0;\n }\n\n /**\n * Emits the internal `TREE_CHANGED` event after a structural route-tree\n * mutation. Reuses the shared `EventEmitter` — so re-entrancy coalescing\n * (#1033) and per-listener error isolation (`onListenerError`) apply\n * automatically.\n */\n emitTreeChanged(event: TreeChangedEvent): void {\n this.#emitter.emit(TREE_CHANGED, event);\n }\n\n /**\n * True while a `TREE_CHANGED` event is being dispatched synchronously.\n * Delegates to the emitter's own in-flight tracking (#1034) — `getRoutesApi`\n * reads this to reject reentrant route-CRUD from a `subscribeChanges` handler\n * (#1032).\n */\n isEmittingTreeChanged(): boolean {\n return this.#emitter.isDispatching(TREE_CHANGED);\n }\n\n /**\n * Subscribes to `TREE_CHANGED`. **Lenient** duplicate semantics (mirrors\n * {@link subscribe}): each call wraps the handler in a fresh closure, so N\n * registrations of the same reference produce N independent subscriptions.\n */\n subscribeTreeChanged(\n handler: (event: TreeChangedEvent) => void,\n ): Unsubscribe {\n // Same disposed-state enforcement as subscribe()/subscribeLeave() (#946),\n // completing the guard across all three subscription primitives — extended\n // here to the internal route-tree channel (#982). A `subscribeChanges`\n // reference bound before dispose() (`const s = routes.subscribeChanges\n // .bind(routes)`) reaches this method via the getRoutesApi delegate, which\n // — unlike its add/remove/update siblings — does not itself check\n // isDisposed(). Without this guard, `emitter.on` would re-register a\n // TREE_CHANGED listener that can never fire (clearAll already ran, the FSM\n // is DISPOSED, the route tree is torn down, no future emit) — a silent\n // no-op, the internal-channel counterpart of the #946 hazard.\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n return this.#emitter.on(TREE_CHANGED, (event: TreeChangedEvent) => {\n handler(event);\n });\n }\n\n /** Number of active `TREE_CHANGED` listeners (drives conditional emit). */\n treeChangedListenerCount(): number {\n return this.#emitter.listenerCount(TREE_CHANGED);\n }\n\n sendStart(): void {\n this.#fsm.send(routerEvents.START);\n }\n\n sendStop(): void {\n this.#fsm.send(routerEvents.STOP);\n }\n\n sendDispose(): void {\n this.#fsm.send(routerEvents.DISPOSE);\n }\n\n sendStarted(): void {\n this.#fsm.send(routerEvents.STARTED);\n }\n\n sendNavigate(toState: State, fromState?: State): void {\n this.#currentToState = toState;\n // Table-driven: the FSM action emits TRANSITION_START (#1169 D-full). A\n // NAVIGATE that the table rejects is a no-op — the FSM never leaves an\n // invalid state and no event fires.\n this.#fsm.send(routerEvents.NAVIGATE, { toState, fromState });\n }\n\n sendComplete(\n state: State,\n fromState?: State,\n opts: NavigationOptions = {},\n ): void {\n // Table-driven: the FSM action emits TRANSITION_SUCCESS (#1169 D-full).\n // COMPLETE from IDLE/DISPOSED (a listener stopped/disposed mid-transition)\n // is a table no-op — no resurrection, no phantom success emit.\n this.#fsm.send(routerEvents.COMPLETE, { toState: state, fromState, opts });\n\n // Nav committed — clear so a later stop()/dispose() cannot cancel a finished\n // navigation. Unconditional now that synchronous reentrant navigate is banned\n // (RFC §4): nothing can replace #currentToState during the emit above, so the\n // #308 reentrant-preserve guard is no longer needed.\n this.#currentToState = undefined;\n }\n\n sendLeaveApprove(toState: State, fromState?: State): void {\n // Table-driven: the FSM action emits TRANSITION_LEAVE_APPROVE (#1169 D-full).\n // LEAVE_APPROVE from IDLE/DISPOSED is a table no-op — no resurrection.\n this.#fsm.send(routerEvents.LEAVE_APPROVE, { toState, fromState });\n }\n\n sendFail(toState?: State, fromState?: State, error?: unknown): void {\n this.#pendingToState = toState;\n this.#pendingFromState = fromState;\n this.#pendingError = error;\n this.#fsm.send(routerEvents.FAIL);\n\n // Nav failed — clear (unconditional; synchronous reentrant navigate is\n // banned (RFC §4), so nothing replaces #currentToState during the emit).\n this.#currentToState = undefined;\n }\n\n /**\n * Surfaces a `TRANSITION_ERROR` for callers that do **not** know — or do not\n * control — the current FSM state: the plugin-facing `emitTransitionError`\n * primitive (`getPluginApi`), the dispose chain, and validator / same-state\n * rejections. It is the state-agnostic counterpart to {@link sendFail}.\n *\n * **What \"Safe\" means here.** The error event is never *dropped*, whatever the\n * FSM state — it does **not** mean the method catches every error. Errors\n * thrown *inside* a `TRANSITION_ERROR` listener are isolated by the\n * `EventEmitter`'s per-listener `onListenerError` sink, not by this method.\n *\n * **Why it branches on its own FSM state.** When the FSM is settled in `READY`\n * (no transition in flight) it routes through the FSM `FAIL` action via\n * {@link sendFail}, so the error rides the normal FSM-driven emit. Otherwise —\n * the router may be starting, mid-transition, or torn down — it emits\n * `TRANSITION_ERROR` directly: a fire-and-forget error report from an unknown\n * state must not drive a second FSM transition that could collide with an\n * in-flight one. Both branches guarantee the event reaches subscribers.\n */\n sendFailSafe(toState?: State, fromState?: State, error?: unknown): void {\n if (this.isReady()) {\n this.sendFail(toState, fromState, error);\n } else {\n this.emitTransitionError(toState, fromState, error as RouterError);\n }\n }\n\n sendCancel(toState: State, fromState?: State, reason?: unknown): void {\n this.#pendingToState = toState;\n this.#pendingFromState = fromState;\n this.#pendingCancelReason = reason;\n this.#fsm.send(routerEvents.CANCEL);\n\n // Nav cancelled — clear (unconditional; synchronous reentrant navigate is\n // banned (RFC §4), so nothing replaces #currentToState during the emit).\n this.#currentToState = undefined;\n }\n\n canBeginTransition(): boolean {\n return this.#fsm.canSend(routerEvents.NAVIGATE);\n }\n\n canStart(): boolean {\n return this.#fsm.canSend(routerEvents.START);\n }\n\n canCancel(): boolean {\n return this.#fsm.canSend(routerEvents.CANCEL);\n }\n\n isActive(): boolean {\n const fsmState = this.#fsm.getState();\n\n return fsmState !== routerStates.IDLE && fsmState !== routerStates.DISPOSED;\n }\n\n isDisposed(): boolean {\n return this.#fsm.getState() === routerStates.DISPOSED;\n }\n\n isTransitioning(): boolean {\n const state = this.#fsm.getState();\n\n return (\n state === routerStates.TRANSITION_STARTED ||\n state === routerStates.LEAVE_APPROVED\n );\n }\n\n isLeaveApproved(): boolean {\n return this.#fsm.getState() === routerStates.LEAVE_APPROVED;\n }\n\n isReady(): boolean {\n return this.#fsm.getState() === routerStates.READY;\n }\n\n isStarting(): boolean {\n return this.#fsm.getState() === routerStates.STARTING;\n }\n\n isIdle(): boolean {\n return this.#fsm.getState() === routerStates.IDLE;\n }\n\n /**\n * Plugin-author API for subscribing to internal router events.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — strict (throws).** Passing the same\n * callback reference twice for the same event throws\n * `Error(\"Duplicate listener for ...\")` from the underlying `EventEmitter`.\n * This is loud-on-misuse by design: plugin code is expected to register\n * each callback once. The contract differs from {@link subscribe} /\n * {@link subscribeLeave}, which are end-user surfaces and silently accept\n * duplicates.\n */\n addEventListener<E extends EventName>(\n eventName: E,\n cb: Plugin[EventMethodMap[E]],\n ): Unsubscribe {\n this.#checkListenerThreshold(eventName, \"addEventListener\");\n\n return this.#emitter.on(\n eventName,\n cb as (...args: RouterEventMap[typeof eventName]) => void,\n );\n }\n\n /**\n * End-user / UI-binding API for subscribing to successful transitions.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — independent.** Each call wraps\n * `listener` in a fresh closure and registers it as a distinct internal\n * slot. `router.subscribe(fn)` twice produces **two** active subscriptions;\n * `fn` fires twice per `TRANSITION_SUCCESS`. The returned `Unsubscribe` is\n * paired with its specific call — invoking it removes exactly that\n * registration.\n *\n * This contract differs from {@link addEventListener} (plugin API, throws\n * on duplicate). End-user code that wants idempotent registration must\n * gate itself, e.g. `if (!unsub) unsub = router.subscribe(fn);`.\n */\n subscribe(listener: SubscribeFn): Unsubscribe {\n // Enforce the disposed state HERE, not only on the facade. A reference\n // bound before dispose() (`const s = router.subscribe.bind(router)`)\n // bypasses the facade's #markDisposed swap and reaches this method\n // directly. Without this guard, `emitter.on` would silently re-register a\n // listener that can never fire (clearAll already ran, FSM is DISPOSED, no\n // future emit) — a silent no-op / stuck-UI hazard (#946).\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n this.#checkListenerThreshold(events.TRANSITION_SUCCESS, \"subscribe\");\n\n // `subscribe` is fire-and-forget; the listener's return value is ignored at\n // the type level (`SubscribeFn` is `=> void`). A void-typed async listener\n // still returns a rejecting Promise at runtime, so return it to the emitter,\n // whose central isolation routes the rejection to the same `onListenerError`\n // sink a sync throw flows through — instead of leaking an `unhandledRejection`\n // (fatal under `--unhandled-rejections=strict`, the Node 22+ default). This\n // is #944, now folded into the emitter's central #1412 async isolation — the\n // former per-site `.catch` here is redundant (symmetric with `subscribeLeave`,\n // which isolates via `Promise.allSettled`).\n return this.#emitter.on(\n events.TRANSITION_SUCCESS,\n (toState: State, fromState?: State) => {\n // Return the listener's runtime value to the emitter so its central\n // isolation catches an async listener's rejection (#944/#1412). Read the\n // void-typed result into `unknown` first — a returned `void` expression\n // is forbidden, but returning the read `unknown` is fine.\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -- read the void-typed listener's runtime value so it can be returned for central async isolation (#944/#1412)\n const result: unknown = listener({\n route: toState,\n previousRoute: fromState,\n });\n\n return result;\n },\n );\n }\n\n /**\n * End-user / UI-binding API for subscribing to **approved** route departures\n * (`LEAVE_APPROVED` phase): all `canDeactivate` guards have passed, but the\n * departure is **tentative, not committed** — an activation (`canActivate`)\n * guard can still reject (or the target route be removed mid-transition),\n * leaving the user on the current route (#932). Treat the leave as tentative\n * for non-idempotent side-effects and use the payload `signal` (which aborts\n * with the failure reason, #943) to roll back when the navigation does not\n * commit. Async listeners block the activation phase.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — independent.** Each call pushes\n * `listener` onto the internal array; `router.subscribeLeave(fn)` twice\n * produces two entries and `fn` fires twice per leave. Each returned\n * `Unsubscribe` is **idempotent** (a `removed` flag, #1349) and removes\n * exactly ONE entry — the first still matching the reference (`indexOf`\n * semantic). So a repeated call of one unsubscribe is a true no-op and does\n * **not** touch the other registration; N subscribes + M *distinct*\n * unsubscribes leave N − M entries. Which physical entry survives is\n * irrelevant — the reference is the same.\n *\n * Contract differs from {@link addEventListener} (throws on duplicate).\n * For idempotent *registration* (one active subscription), gate at the call site.\n */\n subscribeLeave(listener: LeaveFn): Unsubscribe {\n // Same disposed-state enforcement as subscribe() (#946): a pre-bound\n // reference would otherwise push onto #leaveListeners after dispose() and\n // silently never fire (FSM is DISPOSED, no LEAVE_APPROVE emit).\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n this.#leaveListeners.push(listener);\n\n // Idempotency flag (#1349), mirroring extendRouter / addInterceptor (#1198).\n // Without it, a double call would `indexOf(listener)` again and splice a\n // DUPLICATE registration of the same fn — silently deactivating another\n // subscriber whose own unsubscribe was never called. The `Unsubscribe`\n // contract names subscribeLeave as idempotent. (Unlike addInterceptor, the\n // `idx !== -1` guard stays: `dispose()` empties `#leaveListeners` via\n // `clearAll`, so an unsubscribe called after dispose reaches this with\n // idx === -1.)\n let removed = false;\n\n return () => {\n if (removed) {\n return;\n }\n\n removed = true;\n\n const idx = this.#leaveListeners.indexOf(listener);\n\n if (idx !== -1) {\n this.#leaveListeners.splice(idx, 1);\n }\n };\n }\n\n hasLeaveListeners(): boolean {\n return this.#leaveListeners.length > 0;\n }\n\n /**\n * True when a plugin listener runs in a PRE-COMMIT transition window where a\n * synchronous `stop()`/`dispose()` can supersede the in-flight navigation\n * before it commits — i.e. `onTransitionStart` (TRANSITION_START) or\n * `onTransitionLeaveApprove` (TRANSITION_LEAVE_APPROVE). (`subscribeLeave` is\n * tracked separately via `hasLeaveListeners`; `onTransitionSuccess` fires\n * post-commit and cannot cancel.) Read into the pre-`startTransition` liveness\n * snapshot so the commit-gate (#1169) fires for these windows too.\n */\n hasPreCommitListeners(): boolean {\n return (\n this.#emitter.listenerCount(events.TRANSITION_START) > 0 ||\n this.#emitter.listenerCount(events.TRANSITION_LEAVE_APPROVE) > 0\n );\n }\n\n awaitLeaveListeners(\n toState: State,\n fromState: State | undefined,\n signal: AbortSignal,\n ): Promise<void> | undefined {\n if (fromState === undefined) {\n return undefined;\n }\n\n // Freeze the payload wrapper so listeners cannot mutate it (`payload.route`\n // is already deep-frozen via the State immutability invariant; this closes\n // the wrapper-mutation gap surfaced by audit `probe-05-payload-frozen`).\n const leaveState: LeaveState = Object.freeze({\n route: fromState,\n nextRoute: toState,\n signal,\n });\n\n let promises: Promise<void>[] | undefined;\n let firstSyncError: unknown;\n\n // Snapshot before iteration — a listener that reentrantly calls\n // `subscribeLeave(newFn)` or its own `unsubscribe()` must not affect the\n // current emit cycle. Symmetric with the EventEmitter snapshot invariant\n // (PR #666 / #659).\n const snapshot = [...this.#leaveListeners];\n\n // Elevated across the SYNC leave-listener dispatch: `isProcessing()` reads\n // it, so a sync subscribeLeave listener that calls navigate() is rejected\n // with REENTRANT_NAVIGATION at the facade (RFC §4). The `finally` restores it\n // before any async tail (returned promise) runs, so a DEFERRED navigate from\n // an async listener (after its first `await`) sees depth 0 and is allowed.\n this.#dispatchDepth++;\n\n try {\n for (const listener of snapshot) {\n try {\n const result = listener(leaveState);\n\n if (result !== undefined && typeof result.then === \"function\") {\n promises ??= [];\n promises.push(result);\n }\n } catch (error: unknown) {\n if (firstSyncError === undefined) {\n firstSyncError = error;\n }\n }\n }\n } finally {\n this.#dispatchDepth--;\n }\n\n if (promises === undefined) {\n if (firstSyncError !== undefined) {\n throw ensureError(firstSyncError);\n }\n\n return undefined;\n }\n\n return settleLeavePromises(promises, firstSyncError, signal);\n }\n\n clearAll(): void {\n this.#emitter.clearAll();\n this.#leaveListeners.length = 0;\n }\n\n setLimits(limits: { maxListeners: number; warnListeners: number }): void {\n this.#emitter.setLimits(limits);\n }\n\n /**\n * Injects the lazy validator accessor (wireNamespaces), mirroring\n * `PluginsNamespace` / `RouteLifecycleNamespace`. The closure reads the live\n * `RouterInternals.validator`, so a validation-plugin registered AFTER wiring\n * is still observed on the next `subscribe` / `addEventListener`.\n */\n setValidatorAccessor(getValidator: () => RouterValidator | null): void {\n this.#getValidator = getValidator;\n }\n\n // Single guarded entry point for routing a cancel into the FSM `CANCEL` action\n // — used by every source: stop/dispose (RouterLifecycle) pass no reason;\n // supersede / external `opts.signal` (via the wiring `cancelNavigation` dep)\n // pass the abort reason (#943). `canCancel()` makes it a no-op outside a\n // cancellable FSM state (#1034: was a second, unguarded `cancelNavigation` path).\n sendCancelIfPossible(fromState: State | undefined, reason?: unknown): void {\n const toState = this.#currentToState;\n\n if (!this.canCancel() || toState === undefined) {\n return;\n }\n\n this.sendCancel(toState, fromState, reason);\n }\n\n /**\n * Proactive listener-count threshold (#1188) — mirrors the plugins /\n * lifecycle / dependencies counters. Opt-in: the emitter's per-event count is\n * read ONLY when the validator is installed, so the bare-core hot path pays\n * nothing. `count` is the POST-add size (`listenerCount + 1`), matching\n * `RouteLifecycleNamespace`'s `count + 1`, so warn/error fire exactly when the\n * new listener reaches the threshold. Core keeps the emitter's bare-`Error`\n * hard cap; this only surfaces an actionable signal well before it.\n */\n #checkListenerThreshold(eventName: EventName, methodName: string): void {\n const validator = this.#getValidator?.();\n\n if (validator) {\n validator.eventBus.validateCountThresholds(\n this.#emitter.listenerCount(eventName) + 1,\n eventName,\n methodName,\n );\n }\n }\n\n #emitPendingError(): void {\n this.emitTransitionError(\n this.#pendingToState,\n this.#pendingFromState,\n this.#pendingError as RouterError | undefined,\n );\n\n // Clear the pending payload once this FAIL action has consumed it. `#pending*`\n // is only meaningful in the window between the sendFail()/sendFailSafe() that\n // sets it and this emit; keeping it afterwards pins a stale State/RouterError\n // on the instance and leaves an implicit \"valid only in this window\" coupling\n // (#949). Hygiene only — every consumer overwrites the fields before\n // re-reading (handleCancel reads what its own sendCancel just set), so there\n // is no observable behaviour change.\n this.#pendingToState = undefined;\n this.#pendingFromState = undefined;\n this.#pendingError = undefined;\n }\n\n #setupFSMActions(): void {\n const fsm = this.#fsm;\n\n fsm.on(routerStates.STARTING, routerEvents.STARTED, () => {\n this.emitRouterStart();\n });\n\n fsm.on(routerStates.READY, routerEvents.STOP, () => {\n this.emitRouterStop();\n });\n\n // NAVIGATE / LEAVE_APPROVE / COMPLETE emit their transition event as the FSM\n // action (payload = the transition states), so `send()` from an invalid\n // state (e.g. COMPLETE from IDLE after a listener's stop()/dispose()) is a\n // table no-op that emits nothing — the FSM table is the sole authority over\n // state, no `forceState` resurrection (#1169 D-full). NAVIGATE fires from\n // READY plus the TRANSITION_STARTED / LEAVE_APPROVED self-loops (supersede).\n const emitNavigate = (payload: RouterPayloads[\"NAVIGATE\"]): void => {\n this.emitTransitionStart(payload.toState, payload.fromState);\n };\n\n fsm.on(routerStates.READY, routerEvents.NAVIGATE, emitNavigate);\n fsm.on(\n routerStates.TRANSITION_STARTED,\n routerEvents.NAVIGATE,\n emitNavigate,\n );\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.NAVIGATE, emitNavigate);\n\n fsm.on(\n routerStates.TRANSITION_STARTED,\n routerEvents.LEAVE_APPROVE,\n (payload) => {\n this.emitTransitionLeaveApprove(payload.toState, payload.fromState);\n },\n );\n\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.COMPLETE, (payload) => {\n this.emitTransitionSuccess(\n payload.toState,\n payload.fromState,\n payload.opts,\n );\n });\n\n const handleCancel = () => {\n const toState = this.#pendingToState;\n const reason = this.#pendingCancelReason;\n\n this.#pendingCancelReason = undefined;\n\n // (RFC navigation-cancellation-unification §5): the FSM CANCEL\n // action OWNS the abort. Aborting the in-flight controller wakes the parked\n // async pipeline (#1018) and rejects navigate() — the post-race local\n // isActive() sees signal.aborted; `reason` surfaces as the leave signal's\n // reason (#943). Order: (i) abort (wake) then (ii) emit TRANSITION_CANCEL.\n // No cycle: onInternalAbort is wake-only, it does not re-enter cancel.\n this.#abortController(reason);\n\n /* v8 ignore next -- @preserve: #pendingToState guaranteed set by sendCancel before send() */\n if (toState === undefined) {\n return;\n }\n\n this.emitTransitionCancel(toState, this.#pendingFromState);\n };\n\n fsm.on(routerStates.TRANSITION_STARTED, routerEvents.CANCEL, handleCancel);\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.CANCEL, handleCancel);\n\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.STARTING, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.READY, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.TRANSITION_STARTED, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EventBusNamespace.js","names":["RouterError","errorCodes","#fsm","#emitter","#abortController","#leaveListeners","#currentToState","#setupFSMActions","events","#dispatchDepth","routerEvents","#pendingToState","#pendingFromState","#pendingError","#pendingCancelReason","routerStates","#checkListenerThreshold","#getValidator","#emitPendingError"],"sources":["../../../../src/namespaces/EventBusNamespace/EventBusNamespace.ts"],"sourcesContent":["// packages/core/src/namespaces/EventBusNamespace/EventBusNamespace.ts\n\nimport { errorCodes, events } from \"../../constants\";\nimport { RouterError } from \"../../RouterError\";\nimport { routerEvents, routerStates } from \"../../routerFSM\";\n\nimport type { EventBusOptions } from \"./types\";\nimport type { RouterEvent, RouterPayloads, RouterState } from \"../../routerFSM\";\nimport type {\n EventName,\n LeaveFn,\n LeaveState,\n NavigationOptions,\n Plugin,\n State,\n SubscribeFn,\n TreeChangedEvent,\n Unsubscribe,\n EventMethodMap,\n} from \"../../types\";\nimport type { RouterEventMap } from \"../../types/internal\";\nimport type { RouterValidator } from \"../../types/RouterValidator\";\nimport type { EventEmitter } from \"../../utils/event-emitter\";\nimport type { FSM } from \"../../utils/fsm\";\n\n/**\n * Internal-only event key for route-tree mutations. Lives on the same\n * `EventEmitter` as the 7 transition events but never enters the public\n * `EventName` union — reachable only through\n * `getRoutesApi(router).subscribeChanges()`.\n */\nconst TREE_CHANGED = \"TREE_CHANGED\";\n\nfunction ensureError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value));\n}\n\nfunction settleLeavePromises(\n promises: Promise<void>[],\n firstSyncError: unknown,\n signal: AbortSignal,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n // #1197 — canonicalize the abort outcome: reject with a\n // `RouterError(TRANSITION_CANCELLED)` carrying the external reason, so the\n // no-guards leave path classifies identically to the guard path (no raw\n // reject, no spurious TRANSITION_ERROR). Internal sources\n // (supersede/stop/dispose) already abort with such a RouterError — thread it\n // through unchanged so the #943 reason is preserved.\n const onAbort = (): void => {\n const reason: unknown = signal.reason;\n\n reject(\n reason instanceof RouterError &&\n reason.code === errorCodes.TRANSITION_CANCELLED\n ? reason\n : new RouterError(errorCodes.TRANSITION_CANCELLED, { reason }),\n );\n };\n\n if (signal.aborted) {\n onAbort();\n\n return;\n }\n\n // Stryker disable next-line ObjectLiteral,BooleanLiteral: equivalent — `{ once: true }` is redundant: onAbort fires at most once (a signal aborts once) and the success path explicitly removeEventListener's it, so dropping `once` is unobservable. StringLiteral sibling stays live (the \"abort\" event name is killed).\n signal.addEventListener(\"abort\", onAbort, { once: true });\n\n void Promise.allSettled(promises).then((results) => {\n // Stryker disable next-line StringLiteral: equivalent — this cleanup name is redundant: onAbort is registered with `{ once: true }` and the per-navigation signal is discarded unaborted on success, so failing to remove the listener leaks nothing observable.\n signal.removeEventListener(\"abort\", onAbort);\n\n // Stryker disable next-line BlockStatement: equivalent — emptying the post-allSettled abort-race early-return falls through to resolve()/reject(), but the abort handler already settled the promise, so the extra settle is a no-op. CE sibling stays live (→true hangs the pipeline = killed via timeout).\n if (signal.aborted) {\n // Race lost to abort — the abort handler already rejected; do nothing\n return;\n }\n\n if (firstSyncError !== undefined) {\n reject(ensureError(firstSyncError));\n\n return;\n }\n\n const rejected = results.find(\n (result): result is PromiseRejectedResult =>\n result.status === \"rejected\",\n );\n\n if (rejected !== undefined) {\n reject(ensureError(rejected.reason));\n\n return;\n }\n\n resolve();\n });\n });\n}\n\nexport class EventBusNamespace {\n readonly #fsm: FSM<RouterState, RouterEvent, null, RouterPayloads>;\n readonly #emitter: EventEmitter<RouterEventMap>;\n // Effect of the FSM CANCEL action: aborts the in-flight navigation's\n // controller. Wired to NavigationNamespace.\n readonly #abortController: (reason?: unknown) => void;\n // Lazy accessor for the opt-in RouterValidator (wired by wireNamespaces).\n // Returns `null` until validation-plugin is registered — so the proactive\n // listener-count threshold (#1188) costs the no-plugin path nothing.\n #getValidator: (() => RouterValidator | null) | undefined;\n readonly #leaveListeners: LeaveFn[] = [];\n\n // Depth of the synchronous transition-dispatch window — elevated while a\n // transition event is being emitted (`emitTransition*`) or a `subscribeLeave`\n // listener batch runs. `isProcessing()` reads it so the navigation facade can\n // reject a synchronous reentrant navigate() from inside a transition listener\n // with REENTRANT_NAVIGATION (RFC navigation-cancellation-unification §4). A\n // counter (not a boolean) tolerates legitimately-nested transition emits. The\n // ceiling-bounded \"allow reentrant navigate, throw RecursionDepthError at\n // `maxEventDepth`\" behaviour (#935/#945) is gone: such a navigate now throws\n // REENTRANT_NAVIGATION at depth 1, before it can recurse. (The emitter's old\n // `maxEventDepth` depth-bound is gone too — re-entrant emits are coalesced to a\n // no-op at the emitter, #1033 — so no event can re-enter its own dispatch.)\n #dispatchDepth = 0;\n\n #currentToState: State | undefined;\n #pendingToState: State | undefined;\n #pendingFromState: State | undefined;\n #pendingError: unknown;\n // Abort reason for the pending CANCEL — read by handleCancel, set by sendCancel.\n #pendingCancelReason: unknown;\n\n constructor(options: EventBusOptions) {\n this.#fsm = options.routerFSM;\n this.#emitter = options.emitter;\n this.#abortController = options.abortController;\n this.#currentToState = undefined;\n this.#setupFSMActions();\n }\n\n static validateSubscribeListener(listener: unknown): void {\n if (typeof listener !== \"function\") {\n throw new TypeError(\n \"[router.subscribe] Expected a function. \" +\n \"For Observable pattern use observable(router) from @real-router/rx\",\n );\n }\n }\n\n /**\n * Validates the `subscribeLeave` listener. Unlike\n * {@link validateSubscribeListener}, the error carries **no**\n * `@real-router/rx` hint — rx exposes the Observable pattern for *success*\n * transitions (`observable(router)`, `state$`, `events$`), not for leave\n * events, so steering leave-listener misuse toward rx would mislead. The\n * asymmetry is intentional (mirrored in `core/CLAUDE.md`).\n */\n static validateSubscribeLeaveListener(listener: unknown): void {\n if (typeof listener !== \"function\") {\n throw new TypeError(\"[router.subscribeLeave] Expected a function\");\n }\n }\n\n emitRouterStart(): void {\n this.#emitter.emit(events.ROUTER_START);\n }\n\n emitRouterStop(): void {\n this.#emitter.emit(events.ROUTER_STOP);\n }\n\n emitTransitionStart(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_START, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionSuccess(\n toState: State,\n fromState?: State,\n opts?: NavigationOptions,\n ): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_SUCCESS, toState, fromState, opts);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionError(\n toState?: State,\n fromState?: State,\n error?: RouterError,\n ): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_ERROR, toState, fromState, error);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionCancel(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_CANCEL, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n emitTransitionLeaveApprove(toState: State, fromState?: State): void {\n this.#dispatchDepth++;\n try {\n this.#emitter.emit(events.TRANSITION_LEAVE_APPROVE, toState, fromState);\n } finally {\n this.#dispatchDepth--;\n }\n }\n\n /**\n * True while a transition event is being dispatched synchronously — an\n * `emitTransition*` call or a `subscribeLeave` listener batch is on the stack.\n * The navigation facade reads this to reject a synchronous reentrant\n * navigate() from inside a transition listener (RFC §4).\n */\n isProcessing(): boolean {\n return this.#dispatchDepth > 0;\n }\n\n /**\n * Emits the internal `TREE_CHANGED` event after a structural route-tree\n * mutation. Reuses the shared `EventEmitter` — so re-entrancy coalescing\n * (#1033) and per-listener error isolation (`onListenerError`) apply\n * automatically.\n */\n emitTreeChanged(event: TreeChangedEvent): void {\n this.#emitter.emit(TREE_CHANGED, event);\n }\n\n /**\n * True while a `TREE_CHANGED` event is being dispatched synchronously.\n * Delegates to the emitter's own in-flight tracking (#1034) — `getRoutesApi`\n * reads this to reject reentrant route-CRUD from a `subscribeChanges` handler\n * (#1032).\n */\n isEmittingTreeChanged(): boolean {\n return this.#emitter.isDispatching(TREE_CHANGED);\n }\n\n /**\n * Subscribes to `TREE_CHANGED`. **Lenient** duplicate semantics (mirrors\n * {@link subscribe}): each call wraps the handler in a fresh closure, so N\n * registrations of the same reference produce N independent subscriptions.\n */\n subscribeTreeChanged(\n handler: (event: TreeChangedEvent) => void,\n ): Unsubscribe {\n // Same disposed-state enforcement as subscribe()/subscribeLeave() (#946),\n // completing the guard across all three subscription primitives — extended\n // here to the internal route-tree channel (#982). A `subscribeChanges`\n // reference bound before dispose() (`const s = routes.subscribeChanges\n // .bind(routes)`) reaches this method via the getRoutesApi delegate, which\n // — unlike its add/remove/update siblings — does not itself check\n // isDisposed(). Without this guard, `emitter.on` would re-register a\n // TREE_CHANGED listener that can never fire (clearAll already ran, the FSM\n // is DISPOSED, the route tree is torn down, no future emit) — a silent\n // no-op, the internal-channel counterpart of the #946 hazard.\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n return this.#emitter.on(TREE_CHANGED, (event: TreeChangedEvent) => {\n handler(event);\n });\n }\n\n /** Number of active `TREE_CHANGED` listeners (drives conditional emit). */\n treeChangedListenerCount(): number {\n return this.#emitter.listenerCount(TREE_CHANGED);\n }\n\n sendStart(): void {\n this.#fsm.send(routerEvents.START);\n }\n\n sendStop(): void {\n this.#fsm.send(routerEvents.STOP);\n }\n\n sendDispose(): void {\n this.#fsm.send(routerEvents.DISPOSE);\n }\n\n sendStarted(): void {\n this.#fsm.send(routerEvents.STARTED);\n }\n\n sendNavigate(toState: State, fromState?: State): void {\n this.#currentToState = toState;\n // Table-driven: the FSM action emits TRANSITION_START (#1169 D-full). A\n // NAVIGATE that the table rejects is a no-op — the FSM never leaves an\n // invalid state and no event fires.\n this.#fsm.send(routerEvents.NAVIGATE, { toState, fromState });\n }\n\n sendComplete(\n state: State,\n fromState?: State,\n opts: NavigationOptions = {},\n ): void {\n // Table-driven: the FSM action emits TRANSITION_SUCCESS (#1169 D-full).\n // COMPLETE from IDLE/DISPOSED (a listener stopped/disposed mid-transition)\n // is a table no-op — no resurrection, no phantom success emit.\n this.#fsm.send(routerEvents.COMPLETE, { toState: state, fromState, opts });\n\n // Nav committed — clear so a later stop()/dispose() cannot cancel a finished\n // navigation. Unconditional now that synchronous reentrant navigate is banned\n // (RFC §4): nothing can replace #currentToState during the emit above, so the\n // #308 reentrant-preserve guard is no longer needed.\n this.#currentToState = undefined;\n }\n\n sendLeaveApprove(toState: State, fromState?: State): void {\n // Table-driven: the FSM action emits TRANSITION_LEAVE_APPROVE (#1169 D-full).\n // LEAVE_APPROVE from IDLE/DISPOSED is a table no-op — no resurrection.\n this.#fsm.send(routerEvents.LEAVE_APPROVE, { toState, fromState });\n }\n\n sendFail(toState?: State, fromState?: State, error?: unknown): void {\n this.#pendingToState = toState;\n this.#pendingFromState = fromState;\n this.#pendingError = error;\n this.#fsm.send(routerEvents.FAIL);\n\n // Nav failed — clear (unconditional; synchronous reentrant navigate is\n // banned (RFC §4), so nothing replaces #currentToState during the emit).\n this.#currentToState = undefined;\n }\n\n /**\n * Surfaces a `TRANSITION_ERROR` for callers that do **not** know — or do not\n * control — the current FSM state: the plugin-facing `emitTransitionError`\n * primitive (`getPluginApi`), the dispose chain, and validator / same-state\n * rejections. It is the state-agnostic counterpart to {@link sendFail}.\n *\n * **What \"Safe\" means here.** The error event is never *dropped*, whatever the\n * FSM state — it does **not** mean the method catches every error. Errors\n * thrown *inside* a `TRANSITION_ERROR` listener are isolated by the\n * `EventEmitter`'s per-listener `onListenerError` sink, not by this method.\n *\n * **Why it branches on its own FSM state.** When the FSM is settled in `READY`\n * (no transition in flight) it routes through the FSM `FAIL` action via\n * {@link sendFail}, so the error rides the normal FSM-driven emit. Otherwise —\n * the router may be starting, mid-transition, or torn down — it emits\n * `TRANSITION_ERROR` directly: a fire-and-forget error report from an unknown\n * state must not drive a second FSM transition that could collide with an\n * in-flight one. Both branches guarantee the event reaches subscribers.\n */\n sendFailSafe(toState?: State, fromState?: State, error?: unknown): void {\n if (this.isReady()) {\n this.sendFail(toState, fromState, error);\n } else {\n this.emitTransitionError(toState, fromState, error as RouterError);\n }\n }\n\n sendCancel(toState: State, fromState?: State, reason?: unknown): void {\n this.#pendingToState = toState;\n this.#pendingFromState = fromState;\n this.#pendingCancelReason = reason;\n this.#fsm.send(routerEvents.CANCEL);\n\n // Nav cancelled — clear (unconditional; synchronous reentrant navigate is\n // banned (RFC §4), so nothing replaces #currentToState during the emit).\n this.#currentToState = undefined;\n }\n\n canBeginTransition(): boolean {\n return this.#fsm.canSend(routerEvents.NAVIGATE);\n }\n\n canStart(): boolean {\n return this.#fsm.canSend(routerEvents.START);\n }\n\n canCancel(): boolean {\n return this.#fsm.canSend(routerEvents.CANCEL);\n }\n\n isActive(): boolean {\n const fsmState = this.#fsm.getState();\n\n return fsmState !== routerStates.IDLE && fsmState !== routerStates.DISPOSED;\n }\n\n isDisposed(): boolean {\n return this.#fsm.getState() === routerStates.DISPOSED;\n }\n\n isTransitioning(): boolean {\n const state = this.#fsm.getState();\n\n return (\n state === routerStates.TRANSITION_STARTED ||\n state === routerStates.LEAVE_APPROVED\n );\n }\n\n isLeaveApproved(): boolean {\n return this.#fsm.getState() === routerStates.LEAVE_APPROVED;\n }\n\n isReady(): boolean {\n return this.#fsm.getState() === routerStates.READY;\n }\n\n isStarting(): boolean {\n return this.#fsm.getState() === routerStates.STARTING;\n }\n\n isIdle(): boolean {\n return this.#fsm.getState() === routerStates.IDLE;\n }\n\n /**\n * Plugin-author API for subscribing to internal router events.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — strict (throws).** Passing the same\n * callback reference twice for the same event throws\n * `Error(\"Duplicate listener for ...\")` from the underlying `EventEmitter`.\n * This is loud-on-misuse by design: plugin code is expected to register\n * each callback once. The contract differs from {@link subscribe} /\n * {@link subscribeLeave}, which are end-user surfaces and silently accept\n * duplicates.\n */\n addEventListener<E extends EventName>(\n eventName: E,\n cb: Plugin[EventMethodMap[E]],\n ): Unsubscribe {\n this.#checkListenerThreshold(eventName, \"addEventListener\");\n\n return this.#emitter.on(\n eventName,\n cb as (...args: RouterEventMap[typeof eventName]) => void,\n );\n }\n\n /**\n * End-user / UI-binding API for subscribing to successful transitions.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — independent.** Each call wraps\n * `listener` in a fresh closure and registers it as a distinct internal\n * slot. `router.subscribe(fn)` twice produces **two** active subscriptions;\n * `fn` fires twice per `TRANSITION_SUCCESS`. The returned `Unsubscribe` is\n * paired with its specific call — invoking it removes exactly that\n * registration.\n *\n * This contract differs from {@link addEventListener} (plugin API, throws\n * on duplicate). End-user code that wants idempotent registration must\n * gate itself, e.g. `if (!unsub) unsub = router.subscribe(fn);`.\n */\n subscribe(listener: SubscribeFn): Unsubscribe {\n // Enforce the disposed state HERE, not only on the facade. A reference\n // bound before dispose() (`const s = router.subscribe.bind(router)`)\n // bypasses the facade's #markDisposed swap and reaches this method\n // directly. Without this guard, `emitter.on` would silently re-register a\n // listener that can never fire (clearAll already ran, FSM is DISPOSED, no\n // future emit) — a silent no-op / stuck-UI hazard (#946).\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n this.#checkListenerThreshold(events.TRANSITION_SUCCESS, \"subscribe\");\n\n // `subscribe` is fire-and-forget; the listener's return value is ignored at\n // the type level (`SubscribeFn` is `=> void`). A void-typed async listener\n // still returns a rejecting Promise at runtime, so return it to the emitter,\n // whose central isolation routes the rejection to the same `onListenerError`\n // sink a sync throw flows through — instead of leaking an `unhandledRejection`\n // (fatal under `--unhandled-rejections=strict`, the Node 22+ default). This\n // is #944, now folded into the emitter's central #1412 async isolation — the\n // former per-site `.catch` here is redundant (symmetric with `subscribeLeave`,\n // which isolates via `Promise.allSettled`).\n return this.#emitter.on(\n events.TRANSITION_SUCCESS,\n (toState: State, fromState?: State) => {\n // Return the listener's runtime value to the emitter so its central\n // isolation catches an async listener's rejection (#944/#1412). Read the\n // void-typed result into `unknown` first — a returned `void` expression\n // is forbidden, but returning the read `unknown` is fine.\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -- read the void-typed listener's runtime value so it can be returned for central async isolation (#944/#1412)\n const result: unknown = listener({\n route: toState,\n previousRoute: fromState,\n });\n\n return result;\n },\n );\n }\n\n /**\n * End-user / UI-binding API for subscribing to **approved** route departures\n * (`LEAVE_APPROVED` phase): all `canDeactivate` guards have passed, but the\n * departure is **tentative, not committed** — an activation (`canActivate`)\n * guard can still reject (or the target route be removed mid-transition),\n * leaving the user on the current route (#932). Treat the leave as tentative\n * for non-idempotent side-effects and use the payload `signal` (which aborts\n * with the failure reason, #943) to roll back when the navigation does not\n * commit. Async listeners block the activation phase.\n *\n * @remarks\n *\n * **Duplicate-registration semantics — independent.** Each call pushes\n * `listener` onto the internal array; `router.subscribeLeave(fn)` twice\n * produces two entries and `fn` fires twice per leave. Each returned\n * `Unsubscribe` is **idempotent** (a `removed` flag, #1349) and removes\n * exactly ONE entry — the first still matching the reference (`indexOf`\n * semantic). So a repeated call of one unsubscribe is a true no-op and does\n * **not** touch the other registration; N subscribes + M *distinct*\n * unsubscribes leave N − M entries. Which physical entry survives is\n * irrelevant — the reference is the same.\n *\n * Contract differs from {@link addEventListener} (throws on duplicate).\n * For idempotent *registration* (one active subscription), gate at the call site.\n */\n subscribeLeave(listener: LeaveFn): Unsubscribe {\n // Same disposed-state enforcement as subscribe() (#946): a pre-bound\n // reference would otherwise push onto #leaveListeners after dispose() and\n // silently never fire (FSM is DISPOSED, no LEAVE_APPROVE emit).\n if (this.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n this.#leaveListeners.push(listener);\n\n // Idempotency flag (#1349), mirroring extendRouter / addInterceptor (#1198).\n // Without it, a double call would `indexOf(listener)` again and splice a\n // DUPLICATE registration of the same fn — silently deactivating another\n // subscriber whose own unsubscribe was never called. The `Unsubscribe`\n // contract names subscribeLeave as idempotent. (Unlike addInterceptor, the\n // `idx !== -1` guard stays: `dispose()` empties `#leaveListeners` via\n // `clearAll`, so an unsubscribe called after dispose reaches this with\n // idx === -1.)\n let removed = false;\n\n return () => {\n if (removed) {\n return;\n }\n\n removed = true;\n\n const idx = this.#leaveListeners.indexOf(listener);\n\n if (idx !== -1) {\n this.#leaveListeners.splice(idx, 1);\n }\n };\n }\n\n hasLeaveListeners(): boolean {\n return this.#leaveListeners.length > 0;\n }\n\n /**\n * True when a plugin listener runs in a PRE-COMMIT transition window where a\n * synchronous `stop()`/`dispose()` can supersede the in-flight navigation\n * before it commits — i.e. `onTransitionStart` (TRANSITION_START) or\n * `onTransitionLeaveApprove` (TRANSITION_LEAVE_APPROVE). (`subscribeLeave` is\n * tracked separately via `hasLeaveListeners`; `onTransitionSuccess` fires\n * post-commit and cannot cancel.) Read into the pre-`startTransition` liveness\n * snapshot so the commit-gate (#1169) fires for these windows too.\n */\n hasPreCommitListeners(): boolean {\n return (\n this.#emitter.listenerCount(events.TRANSITION_START) > 0 ||\n this.#emitter.listenerCount(events.TRANSITION_LEAVE_APPROVE) > 0\n );\n }\n\n awaitLeaveListeners(\n toState: State,\n fromState: State | undefined,\n signal: AbortSignal,\n ): Promise<void> | undefined {\n if (fromState === undefined) {\n return undefined;\n }\n\n // Freeze the payload wrapper so listeners cannot mutate it (`payload.route`\n // is already deep-frozen via the State immutability invariant; this closes\n // the wrapper-mutation gap surfaced by audit `probe-05-payload-frozen`).\n const leaveState: LeaveState = Object.freeze({\n route: fromState,\n nextRoute: toState,\n signal,\n });\n\n let promises: Promise<void>[] | undefined;\n let firstSyncError: unknown;\n\n // Snapshot before iteration — a listener that reentrantly calls\n // `subscribeLeave(newFn)` or its own `unsubscribe()` must not affect the\n // current emit cycle. Symmetric with the EventEmitter snapshot invariant\n // (PR #666 / #659).\n const snapshot = [...this.#leaveListeners];\n\n // Elevated across the SYNC leave-listener dispatch: `isProcessing()` reads\n // it, so a sync subscribeLeave listener that calls navigate() is rejected\n // with REENTRANT_NAVIGATION at the facade (RFC §4). The `finally` restores it\n // before any async tail (returned promise) runs, so a DEFERRED navigate from\n // an async listener (after its first `await`) sees depth 0 and is allowed.\n this.#dispatchDepth++;\n\n try {\n for (const listener of snapshot) {\n try {\n const result = listener(leaveState);\n\n if (result !== undefined && typeof result.then === \"function\") {\n promises ??= [];\n promises.push(result);\n }\n } catch (error: unknown) {\n if (firstSyncError === undefined) {\n firstSyncError = error;\n }\n }\n }\n } finally {\n this.#dispatchDepth--;\n }\n\n if (promises === undefined) {\n if (firstSyncError !== undefined) {\n throw ensureError(firstSyncError);\n }\n\n return undefined;\n }\n\n return settleLeavePromises(promises, firstSyncError, signal);\n }\n\n clearAll(): void {\n this.#emitter.clearAll();\n this.#leaveListeners.length = 0;\n }\n\n setLimits(limits: { maxListeners: number; warnListeners: number }): void {\n this.#emitter.setLimits(limits);\n }\n\n /**\n * Injects the lazy validator accessor (wireNamespaces), mirroring\n * `PluginsNamespace` / `RouteLifecycleNamespace`. The closure reads the live\n * `RouterInternals.validator`, so a validation-plugin registered AFTER wiring\n * is still observed on the next `subscribe` / `addEventListener`.\n */\n setValidatorAccessor(getValidator: () => RouterValidator | null): void {\n this.#getValidator = getValidator;\n }\n\n // Single guarded entry point for routing a cancel into the FSM `CANCEL` action\n // — used by every source: stop/dispose (RouterLifecycle) pass no reason;\n // supersede / external `opts.signal` (via the wiring `cancelNavigation` dep)\n // pass the abort reason (#943). `canCancel()` makes it a no-op outside a\n // cancellable FSM state (#1034: was a second, unguarded `cancelNavigation` path).\n sendCancelIfPossible(fromState: State | undefined, reason?: unknown): void {\n const toState = this.#currentToState;\n\n if (!this.canCancel() || toState === undefined) {\n return;\n }\n\n this.sendCancel(toState, fromState, reason);\n }\n\n /**\n * Proactive listener-count threshold (#1188) — mirrors the plugins /\n * lifecycle / dependencies counters. Opt-in: the emitter's per-event count is\n * read ONLY when the validator is installed, so the bare-core hot path pays\n * nothing. `count` is the POST-add size (`listenerCount + 1`), matching\n * `RouteLifecycleNamespace`'s `count + 1`, so warn/error fire exactly when the\n * new listener reaches the threshold. Core keeps the emitter's bare-`Error`\n * hard cap; this only surfaces an actionable signal well before it.\n */\n #checkListenerThreshold(eventName: EventName, methodName: string): void {\n const validator = this.#getValidator?.();\n\n if (validator) {\n validator.eventBus.validateCountThresholds(\n this.#emitter.listenerCount(eventName) + 1,\n eventName,\n methodName,\n );\n }\n }\n\n #emitPendingError(): void {\n this.emitTransitionError(\n this.#pendingToState,\n this.#pendingFromState,\n this.#pendingError as RouterError | undefined,\n );\n\n // Clear the pending payload once this FAIL action has consumed it. `#pending*`\n // is only meaningful in the window between the sendFail()/sendFailSafe() that\n // sets it and this emit; keeping it afterwards pins a stale State/RouterError\n // on the instance and leaves an implicit \"valid only in this window\" coupling\n // (#949). Hygiene only — every consumer overwrites the fields before\n // re-reading (handleCancel reads what its own sendCancel just set), so there\n // is no observable behaviour change.\n this.#pendingToState = undefined;\n this.#pendingFromState = undefined;\n this.#pendingError = undefined;\n }\n\n #setupFSMActions(): void {\n const fsm = this.#fsm;\n\n fsm.on(routerStates.STARTING, routerEvents.STARTED, () => {\n this.emitRouterStart();\n });\n\n fsm.on(routerStates.READY, routerEvents.STOP, () => {\n this.emitRouterStop();\n });\n\n // NAVIGATE / LEAVE_APPROVE / COMPLETE emit their transition event as the FSM\n // action (payload = the transition states), so `send()` from an invalid\n // state (e.g. COMPLETE from IDLE after a listener's stop()/dispose()) is a\n // table no-op that emits nothing — the FSM table is the sole authority over\n // state, no `forceState` resurrection (#1169 D-full). NAVIGATE fires from\n // READY plus the TRANSITION_STARTED / LEAVE_APPROVED self-loops (supersede).\n const emitNavigate = (payload: RouterPayloads[\"NAVIGATE\"]): void => {\n this.emitTransitionStart(payload.toState, payload.fromState);\n };\n\n fsm.on(routerStates.READY, routerEvents.NAVIGATE, emitNavigate);\n fsm.on(\n routerStates.TRANSITION_STARTED,\n routerEvents.NAVIGATE,\n emitNavigate,\n );\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.NAVIGATE, emitNavigate);\n\n fsm.on(\n routerStates.TRANSITION_STARTED,\n routerEvents.LEAVE_APPROVE,\n (payload) => {\n this.emitTransitionLeaveApprove(payload.toState, payload.fromState);\n },\n );\n\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.COMPLETE, (payload) => {\n this.emitTransitionSuccess(\n payload.toState,\n payload.fromState,\n payload.opts,\n );\n });\n\n const handleCancel = () => {\n const toState = this.#pendingToState;\n const reason = this.#pendingCancelReason;\n\n this.#pendingCancelReason = undefined;\n\n // (RFC navigation-cancellation-unification §5): the FSM CANCEL\n // action OWNS the abort. Aborting the in-flight controller wakes the parked\n // async pipeline (#1018) and rejects navigate() — the post-race local\n // isActive() sees signal.aborted; `reason` surfaces as the leave signal's\n // reason (#943). Order: (i) abort (wake) then (ii) emit TRANSITION_CANCEL.\n // No cycle: onInternalAbort is wake-only, it does not re-enter cancel.\n this.#abortController(reason);\n\n /* v8 ignore next -- @preserve: #pendingToState guaranteed set by sendCancel before send() */\n if (toState === undefined) {\n return;\n }\n\n this.emitTransitionCancel(toState, this.#pendingFromState);\n };\n\n fsm.on(routerStates.TRANSITION_STARTED, routerEvents.CANCEL, handleCancel);\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.CANCEL, handleCancel);\n\n fsm.on(routerStates.LEAVE_APPROVED, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.STARTING, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.READY, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n\n fsm.on(routerStates.TRANSITION_STARTED, routerEvents.FAIL, () => {\n this.#emitPendingError();\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/cjs/routerFSM.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./
|
|
1
|
+
const e=require("./utils/fsm/fsm.js"),t={IDLE:`IDLE`,STARTING:`STARTING`,READY:`READY`,TRANSITION_STARTED:`TRANSITION_STARTED`,LEAVE_APPROVED:`LEAVE_APPROVED`,DISPOSED:`DISPOSED`},n={START:`START`,STARTED:`STARTED`,NAVIGATE:`NAVIGATE`,LEAVE_APPROVE:`LEAVE_APPROVE`,COMPLETE:`COMPLETE`,FAIL:`FAIL`,CANCEL:`CANCEL`,STOP:`STOP`,DISPOSE:`DISPOSE`},r={initial:t.IDLE,context:null,transitions:{[t.IDLE]:{[n.START]:t.STARTING,[n.DISPOSE]:t.DISPOSED},[t.STARTING]:{[n.STARTED]:t.READY,[n.FAIL]:t.IDLE,[n.STOP]:t.IDLE,[n.DISPOSE]:t.DISPOSED},[t.READY]:{[n.NAVIGATE]:t.TRANSITION_STARTED,[n.FAIL]:t.READY,[n.STOP]:t.IDLE,[n.DISPOSE]:t.DISPOSED},[t.TRANSITION_STARTED]:{[n.NAVIGATE]:t.TRANSITION_STARTED,[n.LEAVE_APPROVE]:t.LEAVE_APPROVED,[n.CANCEL]:t.READY,[n.FAIL]:t.READY,[n.DISPOSE]:t.DISPOSED},[t.LEAVE_APPROVED]:{[n.NAVIGATE]:t.TRANSITION_STARTED,[n.COMPLETE]:t.READY,[n.CANCEL]:t.READY,[n.FAIL]:t.READY,[n.DISPOSE]:t.DISPOSED},[t.DISPOSED]:{}}};function i(){return new e.FSM(r)}exports.createRouterFSM=i,exports.routerEvents=n,exports.routerStates=t;
|
|
2
2
|
//# sourceMappingURL=routerFSM.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routerFSM.js","names":["FSM"],"sources":["../../src/routerFSM.ts"],"sourcesContent":["// packages/core/src/routerFSM.ts\n\nimport { FSM } from \"./
|
|
1
|
+
{"version":3,"file":"routerFSM.js","names":["FSM"],"sources":["../../src/routerFSM.ts"],"sourcesContent":["// packages/core/src/routerFSM.ts\n\nimport { FSM } from \"./utils/fsm\";\n\nimport type { NavigationOptions, State } from \"./types\";\nimport type { FSMConfig } from \"./utils/fsm\";\n\n/**\n * Router FSM states.\n *\n * - IDLE: Router not started or stopped\n * - STARTING: Router is initializing\n * - READY: Router is ready for navigation\n * - TRANSITION_STARTED: Navigation in progress (before deactivation guards)\n * - LEAVE_APPROVED: Deactivation guards passed, activation guards pending\n * - DISPOSED: Router has been disposed (R2+)\n */\nexport const routerStates = {\n IDLE: \"IDLE\",\n STARTING: \"STARTING\",\n READY: \"READY\",\n TRANSITION_STARTED: \"TRANSITION_STARTED\",\n LEAVE_APPROVED: \"LEAVE_APPROVED\",\n DISPOSED: \"DISPOSED\",\n} as const;\n\nexport type RouterState = (typeof routerStates)[keyof typeof routerStates];\n\n/**\n * Router FSM events.\n *\n * - START: Begin router initialization\n * - STARTED: Router initialization complete\n * - NAVIGATE: Begin navigation\n * - COMPLETE: Navigation completed successfully\n * - FAIL: Navigation or initialization failed\n * - CANCEL: Navigation cancelled\n * - STOP: Stop router\n * - DISPOSE: Dispose router (R2+)\n */\nexport const routerEvents = {\n START: \"START\",\n STARTED: \"STARTED\",\n NAVIGATE: \"NAVIGATE\",\n LEAVE_APPROVE: \"LEAVE_APPROVE\",\n COMPLETE: \"COMPLETE\",\n FAIL: \"FAIL\",\n CANCEL: \"CANCEL\",\n STOP: \"STOP\",\n DISPOSE: \"DISPOSE\",\n} as const;\n\nexport type RouterEvent = (typeof routerEvents)[keyof typeof routerEvents];\n\n/**\n * Per-event payloads for the router FSM (#1169 commit-gate). The three hot\n * navigation transitions carry their transition states so the FSM action\n * dispatched by `send()` emits the matching transition event — i.e. events are\n * literal consequences of FSM transitions (no `forceState` + manual emit). See\n * `EventBusNamespace.#setupFSMActions`.\n */\nexport interface RouterPayloads {\n NAVIGATE: { toState: State; fromState?: State | undefined };\n LEAVE_APPROVE: { toState: State; fromState?: State | undefined };\n COMPLETE: {\n toState: State;\n fromState?: State | undefined;\n opts?: NavigationOptions | undefined;\n };\n}\n\n/**\n * Router FSM configuration.\n *\n * Transitions:\n * - IDLE → STARTING (START), DISPOSED (DISPOSE)\n * - STARTING → READY (STARTED), IDLE (FAIL, STOP), DISPOSED (DISPOSE)\n * - READY → TRANSITION_STARTED (NAVIGATE), READY (FAIL, self-loop for early validation errors), IDLE (STOP), DISPOSED (DISPOSE)\n * - TRANSITION_STARTED → LEAVE_APPROVED (LEAVE_APPROVE), TRANSITION_STARTED (NAVIGATE, self-loop), READY (CANCEL, FAIL), DISPOSED (DISPOSE)\n * - LEAVE_APPROVED → READY (COMPLETE, CANCEL, FAIL), TRANSITION_STARTED (NAVIGATE), DISPOSED (DISPOSE)\n * - DISPOSED → (no transitions)\n *\n * DISPOSE is wired from every non-DISPOSED state so `router.dispose()` always\n * settles the FSM at DISPOSED. The facade orchestrates cleanup through IDLE\n * for healthy flows; the direct transitions guarantee the FSM is not left\n * stuck if cleanup is skipped (e.g. dispose mid-STARTING when the start\n * pipeline threw before STARTED/FAIL).\n */\nconst routerFSMConfig: FSMConfig<RouterState, RouterEvent, null> = {\n initial: routerStates.IDLE,\n context: null,\n transitions: {\n [routerStates.IDLE]: {\n [routerEvents.START]: routerStates.STARTING,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.STARTING]: {\n [routerEvents.STARTED]: routerStates.READY,\n [routerEvents.FAIL]: routerStates.IDLE,\n [routerEvents.STOP]: routerStates.IDLE,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.READY]: {\n [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,\n [routerEvents.FAIL]: routerStates.READY,\n [routerEvents.STOP]: routerStates.IDLE,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.TRANSITION_STARTED]: {\n [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,\n [routerEvents.LEAVE_APPROVE]: routerStates.LEAVE_APPROVED,\n [routerEvents.CANCEL]: routerStates.READY,\n [routerEvents.FAIL]: routerStates.READY,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.LEAVE_APPROVED]: {\n [routerEvents.NAVIGATE]: routerStates.TRANSITION_STARTED,\n [routerEvents.COMPLETE]: routerStates.READY,\n [routerEvents.CANCEL]: routerStates.READY,\n [routerEvents.FAIL]: routerStates.READY,\n [routerEvents.DISPOSE]: routerStates.DISPOSED,\n },\n [routerStates.DISPOSED]: {},\n },\n};\n\n/**\n * Factory function to create a router FSM instance.\n *\n * @returns FSM instance with initial state \"IDLE\"\n */\nexport function createRouterFSM(): FSM<\n RouterState,\n RouterEvent,\n null,\n RouterPayloads\n> {\n return new FSM<RouterState, RouterEvent, null, RouterPayloads>(\n routerFSMConfig,\n );\n}\n"],"mappings":"sCAiBa,EAAe,CAC1B,KAAM,OACN,SAAU,WACV,MAAO,QACP,mBAAoB,qBACpB,eAAgB,iBAChB,SAAU,UACZ,EAgBa,EAAe,CAC1B,MAAO,QACP,QAAS,UACT,SAAU,WACV,cAAe,gBACf,SAAU,WACV,KAAM,OACN,OAAQ,SACR,KAAM,OACN,QAAS,SACX,EAsCM,EAA6D,CACjE,QAAS,EAAa,KACtB,QAAS,KACT,YAAa,EACV,EAAa,MAAO,EAClB,EAAa,OAAQ,EAAa,UAClC,EAAa,SAAU,EAAa,QACvC,GACC,EAAa,UAAW,EACtB,EAAa,SAAU,EAAa,OACpC,EAAa,MAAO,EAAa,MACjC,EAAa,MAAO,EAAa,MACjC,EAAa,SAAU,EAAa,QACvC,GACC,EAAa,OAAQ,EACnB,EAAa,UAAW,EAAa,oBACrC,EAAa,MAAO,EAAa,OACjC,EAAa,MAAO,EAAa,MACjC,EAAa,SAAU,EAAa,QACvC,GACC,EAAa,oBAAqB,EAChC,EAAa,UAAW,EAAa,oBACrC,EAAa,eAAgB,EAAa,gBAC1C,EAAa,QAAS,EAAa,OACnC,EAAa,MAAO,EAAa,OACjC,EAAa,SAAU,EAAa,QACvC,GACC,EAAa,gBAAiB,EAC5B,EAAa,UAAW,EAAa,oBACrC,EAAa,UAAW,EAAa,OACrC,EAAa,QAAS,EAAa,OACnC,EAAa,MAAO,EAAa,OACjC,EAAa,SAAU,EAAa,QACvC,GACC,EAAa,UAAW,CAAC,CAC5B,CACF,EAOA,SAAgB,GAKd,CACA,OAAO,IAAIA,EAAAA,IACT,CACF,CACF"}
|
|
@@ -18,7 +18,7 @@ interface LoggerConfig {
|
|
|
18
18
|
* Core stores a concrete instance on `RouterInternals.logger` (built from
|
|
19
19
|
* `options.logger` in the `Router` constructor); plugins and other consumers
|
|
20
20
|
* reach that same per-instance logger via `getPluginApi(router).logger`. The
|
|
21
|
-
* concrete `RouterLogger` class lives in core's `
|
|
21
|
+
* concrete `RouterLogger` class lives in core's `utils/logger` — this
|
|
22
22
|
* interface is the shared contract, so nothing outside core needs to import the
|
|
23
23
|
* class (or depend on the former standalone `@real-router/logger` package).
|
|
24
24
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventEmitter.js","names":["#callbacks","#dispatching","#onListenerError","#onListenerWarn","#limits","#warnedEvents","#invokeIsolated","#callListener"],"sources":["../../../../src/utils/event-emitter/EventEmitter.ts"],"sourcesContent":["import type {\n EventEmitterLimits,\n EventEmitterOptions,\n Unsubscribe,\n} from \"./types\";\n\nconst DEFAULT_LIMITS: EventEmitterLimits = {\n maxListeners: 0,\n warnListeners: 0,\n};\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\ntype AnyCallback = Function;\n\n/**\n * Generic typed event emitter with listener limits, duplicate detection,\n * re-entrancy coalescing, and per-listener error isolation.\n *\n * All limits are opt-in via constructor options.\n */\nexport class EventEmitter<TEventMap extends Record<string, unknown[]>> {\n readonly #callbacks = new Map<string, Set<AnyCallback>>();\n // Names currently being dispatched. A re-entrant `emit` of an event already\n // on this set is coalesced to a no-op (see `emit`), so an event can never\n // re-enter its own dispatch — recursion is structurally impossible (depth ≤ 1)\n // with no depth bound and no stack-overflow path (#1033).\n readonly #dispatching = new Set<string>();\n #warnedEvents: Set<string> | null = null;\n #limits: EventEmitterLimits = DEFAULT_LIMITS;\n readonly #onListenerError:\n ((eventName: string, error: unknown) => void) | null;\n readonly #onListenerWarn: ((eventName: string, count: number) => void) | null;\n\n constructor(options?: EventEmitterOptions) {\n if (options?.limits) {\n this.#limits = options.limits;\n }\n\n this.#onListenerError = options?.onListenerError ?? null;\n this.#onListenerWarn = options?.onListenerWarn ?? null;\n }\n\n /**\n * Validates that a callback is a function.\n */\n static validateCallback(\n cb: unknown,\n eventName: string,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n ): asserts cb is Function {\n if (typeof cb !== \"function\") {\n throw new TypeError(\n `Expected callback to be a function for event ${eventName}`,\n );\n }\n }\n\n /**\n * Replaces current limits with the provided limits.\n */\n setLimits(limits: EventEmitterLimits): void {\n this.#limits = limits;\n }\n\n /**\n * Adds an event listener and returns an unsubscribe function.\n * Throws on duplicate listeners or when maxListeners is reached.\n *\n * Registration is atomic (validate-before-mutate, #1358): every rejection\n * check runs against the CURRENT record (read once, never created early), the\n * advisory warn hook runs before any mutation, and the record is created +\n * the listener added only after all checks pass. So a throw — a rejected\n * limit, or a throwing `onListenerWarn` — leaves NO side-effect behind: no\n * orphaned empty record (#1167) and no burnt warn latch (#1168).\n */\n on<E extends keyof TEventMap & string>(\n eventName: E,\n cb: (...args: TEventMap[E]) => void,\n ): Unsubscribe {\n const existing = this.#callbacks.get(eventName);\n const size = existing?.size ?? 0;\n\n if (existing?.has(cb)) {\n throw new Error(`Duplicate listener for \"${eventName}\"`);\n }\n\n const { maxListeners, warnListeners } = this.#limits;\n\n // Enforce the hard limit before warning, so onListenerWarn never fires for\n // a registration that then throws (the warnListeners === maxListeners case).\n if (maxListeners !== 0 && size >= maxListeners) {\n throw new Error(\n `Listener limit (${maxListeners}) reached for \"${eventName}\"`,\n );\n }\n\n // Warn at most once per emitter+event, using the PRE-add size. The hook is\n // invoked first and the latch set only after it returns without throwing, so\n // a throwing hook fails the registration atomically and leaves the latch\n // unspent — the next (W+1)th registration warns as documented (#1168). The\n // latch keeps the advisory hint \"exactly once\" across off/on churn around\n // the threshold; reset by clearAll() or by removing the last listener.\n if (\n warnListeners !== 0 &&\n size === warnListeners &&\n this.#onListenerWarn !== null\n ) {\n this.#warnedEvents ??= new Set();\n\n if (!this.#warnedEvents.has(eventName)) {\n this.#onListenerWarn(eventName, warnListeners);\n this.#warnedEvents.add(eventName);\n }\n }\n\n // Mutate last — create the record only now, so a rejected registration\n // above never strands an empty record (#1167).\n let set = existing;\n\n if (set === undefined) {\n set = new Set();\n this.#callbacks.set(eventName, set);\n }\n\n set.add(cb);\n\n return () => {\n this.off(eventName, cb);\n };\n }\n\n /**\n * Removes an event listener.\n */\n off<E extends keyof TEventMap & string>(\n eventName: E,\n cb: (...args: TEventMap[E]) => void,\n ): void {\n const set = this.#callbacks.get(eventName);\n\n if (!set) {\n return;\n }\n\n set.delete(cb);\n\n if (set.size === 0) {\n // Release per-event records once the last listener is gone, so consumers\n // with dynamic event names don't accumulate empty Sets unbounded\n // (listenerCount stays 0 either way, masking the growth). See #750.\n this.#callbacks.delete(eventName);\n this.#warnedEvents?.delete(eventName);\n }\n }\n\n /**\n * Emits an event, calling all registered listeners with the provided args.\n *\n * Uses snapshot iteration — listeners added/removed during emit don't affect\n * the current invocation. Per-listener errors are caught and reported via the\n * `onListenerError` callback; other listeners still run.\n *\n * Re-entrant emit is coalesced: emitting an event that is already being\n * dispatched (a listener that synchronously re-emits the same event) is a\n * no-op, so dispatch never recurses into itself (#1033).\n *\n * Uses explicit params instead of rest params to avoid V8 array materialization.\n * Extra undefined args are harmless — JS functions ignore extra arguments.\n */\n emit(\n eventName: keyof TEventMap & string,\n arg1?: unknown,\n arg2?: unknown,\n arg3?: unknown,\n arg4?: unknown,\n ): void {\n const set = this.#callbacks.get(eventName);\n\n if (!set || set.size === 0) {\n return;\n }\n\n // Coalesce a re-entrant emit of an in-flight event (depth ≤ 1, #1033).\n if (this.#dispatching.has(eventName)) {\n return;\n }\n\n // arguments.length is O(1) in V8 strict mode — no deopt\n const argc = arguments.length - 1;\n\n this.#dispatching.add(eventName);\n\n try {\n // Single-listener fast path — skip the [...set] snapshot allocation.\n if (set.size === 1) {\n const [cb] = set;\n\n this.#invokeIsolated(eventName, cb, argc, arg1, arg2, arg3, arg4);\n } else {\n const listeners = [...set];\n\n for (const cb of listeners) {\n this.#invokeIsolated(eventName, cb, argc, arg1, arg2, arg3, arg4);\n }\n }\n } finally {\n this.#dispatching.delete(eventName);\n }\n }\n\n /**\n * Removes all listeners and resets the warn latch.\n *\n * Does NOT touch `#dispatching`: the in-flight coalesce guard is owned by the\n * active `emit` frame (added when dispatch starts, self-released in that\n * frame's `finally`). Clearing it here would lift the guard for a live frame\n * when `clearAll()` runs from inside a listener, so a re-entrant same-event\n * emit would no longer coalesce and would re-enter — violating the depth-≤-1\n * contract (#1164). The guard self-releases; `clearAll()` has no business\n * sweeping state owned by active emit frames.\n */\n clearAll(): void {\n this.#callbacks.clear();\n this.#warnedEvents = null;\n }\n\n /**\n * Returns the number of listeners for the given event.\n */\n listenerCount(eventName: keyof TEventMap & string): number {\n return this.#callbacks.get(eventName)?.size ?? 0;\n }\n\n /**\n * Returns whether the given event is currently being dispatched (an `emit`\n * for it is on the stack). Single source of truth for \"is this event\n * in-flight\" — consumers read it to reject re-entrant operations that would\n * trigger such an emit (the emit itself would be coalesced regardless).\n */\n isDispatching(eventName: keyof TEventMap & string): boolean {\n return this.#dispatching.has(eventName);\n }\n\n // ===========================================================================\n // Private methods\n // ===========================================================================\n\n /**\n * Calls a listener with the correct number of arguments.\n * Dispatches by argc to preserve exact call semantics.\n */\n #invokeIsolated(\n eventName: keyof TEventMap & string,\n cb: AnyCallback,\n argc: number,\n arg1: unknown,\n arg2: unknown,\n arg3: unknown,\n arg4: unknown,\n ): void {\n try {\n const result = this.#callListener(cb, argc, arg1, arg2, arg3, arg4);\n\n // A listener typed `=> void` may still return a Promise at runtime (an\n // async hook or any-cast misuse). The sync `catch` below cannot see its\n // rejection, so route it to the same `#onListenerError` sink — otherwise\n // it escapes as a Node `unhandledRejection` (fatal under\n // `--unhandled-rejections=strict`, the Node 22+ default). Centralised here\n // so every listener kind (plugin hooks, `subscribe`, …) is isolated\n // symmetrically (#1412; `subscribe`'s per-site #944 wrapper folds in).\n if (\n result !== null &&\n result !== undefined &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n ) {\n Promise.resolve(result as PromiseLike<unknown>).catch(\n (error: unknown) => {\n this.#onListenerError?.(eventName, error);\n },\n );\n }\n } catch (error) {\n this.#onListenerError?.(eventName, error);\n }\n }\n\n #callListener(\n cb: AnyCallback,\n argc: number,\n arg1: unknown,\n arg2: unknown,\n arg3: unknown,\n arg4: unknown,\n ): unknown {\n switch (argc) {\n case 0: {\n return (cb as () => unknown)();\n }\n case 1: {\n return (cb as (a: unknown) => unknown)(arg1);\n }\n case 2: {\n return (cb as (a: unknown, b: unknown) => unknown)(arg1, arg2);\n }\n case 3: {\n return (cb as (a: unknown, b: unknown, c: unknown) => unknown)(\n arg1,\n arg2,\n arg3,\n );\n }\n default: {\n return (\n cb as (a: unknown, b: unknown, c: unknown, d: unknown) => unknown\n )(arg1, arg2, arg3, arg4);\n }\n }\n }\n\n // (record creation is inlined into `on()` so a rejected registration never\n // creates one — see the atomicity note there, #1167/#1358.)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fsm.js","names":["#context","#transitions","#listeners","#state","#currentTransitions","#actions","#listenerCount"],"sources":["../../../../src/utils/fsm/fsm.ts"],"sourcesContent":["import type { FSMConfig, TransitionInfo, TransitionListener } from \"./types\";\n\n/**\n * Shared guard for the engine-wide invariant \"the state is declared in\n * `config.transitions`\". Applied at every state-entry-point (constructor\n * `initial` and `on`'s `from`) so an undeclared state fails loud with\n * an explicit error instead of bricking the FSM or dead-registering an action\n * (#885). Returns the state's transition map for the caller to reuse.\n */\nfunction requireDeclared<TStates extends string, TEvents extends string>(\n transitions: Record<TStates, Partial<Record<TEvents, TStates>>>,\n state: TStates,\n where: string,\n): Partial<Record<TEvents, TStates>> {\n const stateTransitions = transitions[state];\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS / cast / string-typed callers passing a state outside TStates\n if (stateTransitions === undefined) {\n throw new Error(\n `[FSM.${where}] state \"${state}\" is not declared in config.transitions`,\n );\n }\n\n return stateTransitions;\n}\n\n/**\n * Synchronous finite state machine engine.\n *\n * Reentrancy: `send()` inside `onTransition` listener is allowed but unbounded —\n * callers are responsible for preventing infinite loops.\n *\n * Exceptions: if a listener throws, the exception propagates to the caller.\n * State is already updated before listeners fire, so `getState()` reflects the\n * new state even if the exception escapes `send()`.\n */\nexport class FSM<\n TStates extends string,\n TEvents extends string,\n TContext,\n TPayloadMap extends Partial<Record<TEvents, unknown>> = Record<never, never>,\n> {\n #state: TStates;\n #currentTransitions: Partial<Record<TEvents, TStates>>;\n #listenerCount = 0;\n #actions: Map<TStates, Map<TEvents, (payload: unknown) => void>> | null =\n null;\n readonly #context: TContext;\n readonly #transitions: Record<TStates, Partial<Record<TEvents, TStates>>>;\n readonly #listeners: (TransitionListener<\n TStates,\n TEvents,\n TPayloadMap\n > | null)[] = [];\n\n constructor(config: FSMConfig<TStates, TEvents, TContext>) {\n this.#state = config.initial;\n this.#context = config.context;\n this.#transitions = config.transitions;\n this.#currentTransitions = requireDeclared(\n config.transitions,\n config.initial,\n \"constructor\",\n );\n\n // #1159: validate table closure — every declared transition target must\n // itself be a declared state. `send()` applies table values\n // (`this.#transitions[nextState]`) without re-checking, so a dangling\n // target would silently enter an undeclared state (violating Validity #1)\n // and brick `canSend()` (violating No-bricking #10). One cold-path\n // O(states×events) pass at construction fails loud instead — the fourth\n // state-entry-point, mirroring the `initial` / `on` guards. Explicit\n // `undefined` values are the declared \"no transition\" no-op (send() returns\n // the current state) and are skipped. Post-construction mutation of the\n // shared table stays a documented GIGO boundary (Edge #5).\n for (const state of Object.keys(config.transitions)) {\n const stateTransitions = config.transitions[state as TStates];\n\n for (const event of Object.keys(stateTransitions)) {\n const target = stateTransitions[event as TEvents];\n\n if (target !== undefined) {\n requireDeclared(config.transitions, target, \"constructor\");\n }\n }\n }\n }\n\n send<E extends TEvents>(\n event: E,\n ...args: E extends keyof TPayloadMap ? [TPayloadMap[E]] : [undefined?]\n ): TStates {\n const nextState = this.#currentTransitions[event];\n\n if (nextState === undefined) {\n return this.#state;\n }\n\n const from = this.#state;\n\n this.#state = nextState;\n this.#currentTransitions = this.#transitions[nextState];\n\n const payload = args[0] as TPayloadMap[TEvents] | undefined;\n\n if (this.#actions !== null) {\n const action = this.#actions.get(from)?.get(event);\n\n if (action !== undefined) {\n action(payload);\n }\n }\n\n // Stryker disable next-line ConditionalExpression: equivalent — count>0 is a perf gate to skip the dispatch loop; `true` always enters it, but with no live listener `#listeners` holds only null slots and the loop body guards `listener !== null`, so dispatch is a no-op either way. The EqualityOperator `<=0` sibling on this line stays killed (not silenced here).\n if (this.#listenerCount > 0) {\n // `info` is structurally a valid TransitionInfo, but the distributive\n // union can't be matched to one variant while `event`/`payload` are\n // generic here — erase through `unknown` (TS2352), same spirit as the\n // `args[0]` cast above.\n const info = {\n from,\n to: nextState,\n event,\n payload,\n } as unknown as TransitionInfo<TStates, TEvents, TPayloadMap>;\n\n for (const listener of this.#listeners) {\n if (listener !== null) {\n listener(info);\n }\n }\n }\n\n return this.#state;\n }\n\n canSend(event: TEvents): boolean {\n return this.#currentTransitions[event] !== undefined;\n }\n\n getState(): TStates {\n return this.#state;\n }\n\n getContext(): TContext {\n return this.#context;\n }\n\n on<E extends TEvents>(\n from: TStates,\n event: E,\n action: E extends keyof TPayloadMap\n ? (payload: TPayloadMap[E]) => void\n : () => void,\n ): () => void {\n requireDeclared(this.#transitions, from, \"on\");\n\n this.#actions ??= new Map();\n\n let stateActions = this.#actions.get(from);\n\n if (!stateActions) {\n stateActions = new Map();\n this.#actions.set(from, stateActions);\n }\n\n const capturedAction = action as (payload: unknown) => void;\n\n stateActions.set(event, capturedAction);\n\n return () => {\n // Stryker disable next-line OptionalChaining: equivalent — `#actions` is assigned (`??= new Map()` above) before this unsubscribe closure is created and returned, so it is never null when the closure runs; `?.` can't short-circuit and behaves identically to `.get`.\n const stateMap = this.#actions?.get(from);\n\n if (stateMap?.get(event) === capturedAction) {\n stateMap.delete(event);\n }\n };\n }\n\n onTransition(\n listener: (info: TransitionInfo<TStates, TEvents, TPayloadMap>) => void,\n ): () => void {\n const nullIndex = this.#listeners.indexOf(null);\n let index: number;\n\n if (nullIndex === -1) {\n index = this.#listeners.length;\n this.#listeners.push(listener);\n } else {\n this.#listeners[nullIndex] = listener;\n index = nullIndex;\n }\n\n this.#listenerCount++;\n let subscribed = true;\n\n return () => {\n if (!subscribed) {\n return;\n }\n\n subscribed = false;\n this.#listeners[index] = null;\n // Stryker disable next-line UpdateOperator: equivalent — #listenerCount feeds only the `> 0` loop gate; `++` inflates it but the loop then iterates already-nulled slots (no-op), and no public reader exposes the count, so the miscount is unobservable.\n this.#listenerCount--;\n };\n }\n}\n"],"mappings":"AASA,SAAS,EACP,EACA,EACA,EACmC,CACnC,IAAM,EAAmB,EAAY,GAGrC,GAAI,IAAqB,IAAA,GACvB,MAAU,MACR,QAAQ,EAAM,WAAW,EAAM,wCACjC,EAGF,OAAO,CACT,CAYA,IAAa,EAAb,KAKE,CACA,GACA,GACA,GAAiB,EACjB,GACE,KACF,GACA,GACA,GAIc,CAAC,EAEf,YAAY,EAA+C,CACzD,KAAKG,GAAS,EAAO,QACrB,KAAKH,GAAW,EAAO,QACvB,KAAKC,GAAe,EAAO,YAC3B,KAAKG,GAAsB,EACzB,EAAO,YACP,EAAO,QACP,aACF,EAYA,IAAK,IAAM,KAAS,OAAO,KAAK,EAAO,WAAW,EAAG,CACnD,IAAM,EAAmB,EAAO,YAAY,GAE5C,IAAK,IAAM,KAAS,OAAO,KAAK,CAAgB,EAAG,CACjD,IAAM,EAAS,EAAiB,GAE5B,IAAW,IAAA,IACb,EAAgB,EAAO,YAAa,EAAQ,aAAa,CAE7D,CACF,CACF,CAEA,KACE,EACA,GAAG,EACM,CACT,IAAM,EAAY,KAAKA,GAAoB,GAE3C,GAAI,IAAc,IAAA,GAChB,OAAO,KAAKD,GAGd,IAAM,EAAO,KAAKA,GAElB,KAAKA,GAAS,EACd,KAAKC,GAAsB,KAAKH,GAAa,GAE7C,IAAM,EAAU,EAAK,GAErB,GAAI,KAAKI,KAAa,KAAM,CAC1B,IAAM,EAAS,KAAKA,GAAS,IAAI,CAAI,CAAC,EAAE,IAAI,CAAK,EAE7C,IAAW,IAAA,IACb,EAAO,CAAO,CAElB,CAGA,GAAI,KAAKC,GAAiB,EAAG,CAK3B,IAAM,EAAO,CACX,OACA,GAAI,EACJ,QACA,SACF,EAEA,IAAK,IAAM,KAAY,KAAKJ,GACtB,IAAa,MACf,EAAS,CAAI,CAGnB,CAEA,OAAO,KAAKC,EACd,CAEA,QAAQ,EAAyB,CAC/B,OAAO,KAAKC,GAAoB,KAAW,IAAA,EAC7C,CAEA,UAAoB,CAClB,OAAO,KAAKD,EACd,CAEA,YAAuB,CACrB,OAAO,KAAKH,EACd,CAEA,GACE,EACA,EACA,EAGY,CACZ,EAAgB,KAAKC,GAAc,EAAM,IAAI,EAE7C,KAAKI,KAAa,IAAI,IAEtB,IAAI,EAAe,KAAKA,GAAS,IAAI,CAAI,EAEpC,IACH,EAAe,IAAI,IACnB,KAAKA,GAAS,IAAI,EAAM,CAAY,GAGtC,IAAM,EAAiB,EAIvB,OAFA,EAAa,IAAI,EAAO,CAAc,MAEzB,CAEX,IAAM,EAAW,KAAKA,IAAU,IAAI,CAAI,EAEpC,GAAU,IAAI,CAAK,IAAM,GAC3B,EAAS,OAAO,CAAK,CAEzB,CACF,CAEA,aACE,EACY,CACZ,IAAM,EAAY,KAAKH,GAAW,QAAQ,IAAI,EAC1C,EAEA,IAAc,IAChB,EAAQ,KAAKA,GAAW,OACxB,KAAKA,GAAW,KAAK,CAAQ,IAE7B,KAAKA,GAAW,GAAa,EAC7B,EAAQ,GAGV,KAAKI,KACL,IAAI,EAAa,GAEjB,UAAa,CACN,IAIL,EAAa,GACb,KAAKJ,GAAW,GAAS,KAEzB,KAAKI,KACP,CACF,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RouterLogger.js","names":["#config","LEVEL_CONFIGS","#currentThreshold","#writeLog","LOG_LEVELS","#writeToConsole","#invokeCallback","#inCallback","#reportError"],"sources":["../../../../src/utils/logger/RouterLogger.ts"],"sourcesContent":["// packages/core/src/utils/logger/RouterLogger.ts\n\nimport { LOG_LEVELS, LEVEL_CONFIGS } from \"./constants\";\n\nimport type {\n LogLevel,\n LoggerConfig,\n LogLevelConfig,\n LogCallback,\n} from \"../../types\";\n\n/**\n * Internal config type with required callbackIgnoresLevel\n * (always initialized to false)\n */\ninterface InternalLoggerConfig {\n level: LogLevelConfig;\n callback?: LogCallback | undefined;\n callbackIgnoresLevel: boolean;\n}\n\n/**\n * Logger class for centralized logging with configurable levels and callbacks.\n *\n * Features:\n * - Three log levels: log, warn, error\n * - Configurable threshold filtering (all, warn-error, error-only, none)\n * - Optional callback for custom log processing\n * - Callback can optionally ignore level threshold\n * - Context-based message formatting\n *\n * @example\n * ```ts\n * import { logger } from './Logger';\n *\n * // Configure logger\n * logger.configure({ level: 'warn-error' });\n *\n * // Use logger\n * logger.log('Router', 'Navigation started'); // Won't show (below threshold)\n * logger.warn('Router', 'Deprecated API used'); // Will show\n * ```\n */\nexport class RouterLogger {\n /** Internal configuration storage using private field */\n readonly #config: InternalLoggerConfig = {\n level: \"all\",\n callbackIgnoresLevel: false,\n };\n\n /** Cached numeric threshold value for performance (avoids repeated lookups) */\n #currentThreshold = 0;\n\n /**\n * Re-entrancy guard: true while a user callback is executing. Prevents a\n * callback that itself calls `logger.*` from recursing back through\n * `#invokeCallback` (which would otherwise spin ~5.9k deep until a swallowed\n * RangeError, see #791). Console output is unaffected.\n */\n #inCallback = false;\n\n /**\n * @param config - Optional initial configuration (level / callback /\n * callbackIgnoresLevel), applied once at construction.\n *\n * Each router owns its own `RouterLogger` instance, built from\n * `options.logger` in the `Router` constructor. This replaces the former\n * process-global singleton whose `configure()` leaked across every router in\n * the process — the last `createRouter` won (#724).\n */\n constructor(config?: Partial<LoggerConfig>) {\n if (config) {\n this.configure(config);\n }\n }\n\n /**\n * Configures the logger with new settings.\n *\n * @param config - Partial configuration to merge with existing config\n * @param config.level - Minimum log level to output ('all' | 'warn-error' | 'error-only' | 'none')\n * @param config.callback - Optional callback function to receive log messages\n * @param config.callbackIgnoresLevel - If true, callback receives all messages regardless of level\n *\n * @example\n * ```ts\n * // Set minimum level to warnings\n * logger.configure({ level: 'warn-error' });\n *\n * // Add custom callback that ignores level\n * logger.configure({\n * callback: (level, context, message) => {\n * sendToAnalytics({ level, context, message });\n * },\n * callbackIgnoresLevel: true\n * });\n * ```\n */\n configure(config: Partial<LoggerConfig>): void {\n // Read each field ONCE into a local — an unstable getter must not be re-read\n // between validation and storage: re-reading could pass validation with a\n // valid level and then store a later, unvalidated one, disabling the\n // threshold filter (a TOCTOU, #1162).\n const level = config.level;\n\n if (level !== undefined) {\n // Validate that the provided level is a valid configuration level\n if (!Object.hasOwn(LEVEL_CONFIGS, level)) {\n throw new Error(\n `Invalid log level: \"${level}\". Valid levels are: ${Object.keys(LEVEL_CONFIGS).join(\", \")}`,\n );\n }\n\n this.#config.level = level;\n this.#currentThreshold = LEVEL_CONFIGS[level];\n }\n if (Object.hasOwn(config, \"callback\")) {\n this.#config.callback = config.callback;\n }\n\n const callbackIgnoresLevel = config.callbackIgnoresLevel;\n\n if (callbackIgnoresLevel !== undefined) {\n this.#config.callbackIgnoresLevel = callbackIgnoresLevel;\n }\n }\n\n /**\n * Returns the current logger configuration.\n *\n * @returns Current configuration object with level, callback, and callbackIgnoresLevel\n *\n * @example\n * ```ts\n * const config = logger.getConfig();\n * console.log(config.level); // 'warn'\n * console.log(config.callbackIgnoresLevel); // false\n * ```\n */\n getConfig(): LoggerConfig {\n return {\n level: this.#config.level,\n callback: this.#config.callback,\n callbackIgnoresLevel: this.#config.callbackIgnoresLevel,\n };\n }\n\n /**\n * Logs an informational message at 'log' level.\n *\n * This is the lowest severity level. Messages are shown when level is 'all'.\n *\n * @param context - Context identifier (e.g., 'Router', 'Plugin')\n * @param message - Main log message\n * @param args - Additional arguments to log (objects, arrays, etc.)\n *\n * @example\n * ```ts\n * logger.log('Router', 'Navigation started', { from: '/home', to: '/about' });\n * // Output: [Router] Navigation started { from: '/home', to: '/about' }\n * ```\n */\n log(context: string, message: string, ...args: unknown[]): void {\n this.#writeLog(\"log\", context, message, args);\n }\n\n /**\n * Logs a warning message at 'warn' level.\n *\n * Use for deprecation notices, non-critical issues, or potential problems.\n * Messages are shown when level is 'all' or 'warn-error'.\n *\n * @param context - Context identifier (e.g., 'Router', 'Plugin')\n * @param message - Warning message\n * @param args - Additional arguments to log\n *\n * @example\n * ```ts\n * logger.warn('Router', 'Using deprecated API', { method: 'oldNavigate' });\n * // Output: [Router] Using deprecated API { method: 'oldNavigate' }\n * ```\n */\n warn(context: string, message: string, ...args: unknown[]): void {\n this.#writeLog(\"warn\", context, message, args);\n }\n\n /**\n * Logs an error message at 'error' level.\n *\n * Use for critical errors, exceptions, or failures that require attention.\n * Messages are shown when level is 'all', 'warn-error', or 'error-only'.\n *\n * @param context - Context identifier (e.g., 'Router', 'Plugin')\n * @param message - Error message\n * @param args - Additional arguments to log (often error objects)\n *\n * @example\n * ```ts\n * logger.error('Router', 'Navigation failed', new Error('Route not found'));\n * // Output: [Router] Navigation failed Error: Route not found\n * ```\n */\n error(context: string, message: string, ...args: unknown[]): void {\n this.#writeLog(\"error\", context, message, args);\n }\n\n /**\n * Central logging method that coordinates console output and callback invocation.\n *\n * This method implements the core logging logic:\n * 1. Early exit optimization for 'none' level (unless callback ignores level)\n * 2. Level threshold comparison for console output filtering\n * 3. Delegates to #writeToConsole and #invokeCallback\n *\n * @param level - Log level ('log' | 'warn' | 'error')\n * @param context - Context identifier\n * @param message - Log message\n * @param args - Additional arguments\n *\n * @private\n */\n #writeLog(\n level: LogLevel,\n context: string,\n message: string,\n args: unknown[],\n ): void {\n // Early exit optimization: if level is 'none' and callback doesn't ignore level,\n // skip all processing (both console and callback)\n // Stryker disable next-line BlockStatement: equivalent — emptying this early-exit block falls through, but at level \"none\" the downstream guards already yield no output: #writeToConsole skips (threshold 3 > every message level) and #invokeCallback returns (this branch runs only when callbackIgnoresLevel is false). Pure perf shortcut; the ConditionalExpression →true sibling on this line stays killed (not silenced here).\n if (this.#config.level === \"none\" && !this.#config.callbackIgnoresLevel) {\n return;\n }\n\n // Convert message level to numeric value for threshold comparison\n // LOG_LEVELS: { log: 0, warn: 1, error: 2 }\n const messageLevelValue = LOG_LEVELS[level];\n\n // Determine if this message should skip console output\n // Example: if threshold is 'warn' (1), then 'log' messages (0) are skipped\n const shouldSkipConsole = messageLevelValue < this.#currentThreshold;\n\n // Console output (respects level threshold)\n if (!shouldSkipConsole) {\n this.#writeToConsole(level, context, message, args);\n }\n\n // Callback handling (may ignore level threshold based on config)\n this.#invokeCallback(level, context, message, shouldSkipConsole, args);\n }\n\n /**\n * Writes a formatted log message to the console.\n *\n * Features:\n * - Formats message with context: \"[Context] message\"\n * - Uses appropriate console method (log/warn/error)\n * - Safe: checks for console existence (for non-browser environments)\n *\n * @param level - Console method to use ('log' | 'warn' | 'error')\n * @param context - Context identifier (prepended to message if present)\n * @param message - Log message\n * @param args - Additional arguments to pass to console\n *\n * @private\n */\n #writeToConsole(\n level: LogLevel,\n context: string,\n message: string,\n args: unknown[],\n ): void {\n // Safety check: ensure console exists and has the required method\n // This is important for environments like Node.js tests or edge cases\n if (\n typeof console !== \"undefined\" &&\n typeof console[level] === \"function\"\n ) {\n // Format message with context bracket notation for visual clarity\n // Note: formatting is done inside the check to avoid unnecessary string allocation\n // when console is not available\n const formattedMessage = context ? `[${context}] ${message}` : message;\n\n console[level](formattedMessage, ...args);\n }\n }\n\n /**\n * Invokes the configured callback with log data, respecting level settings.\n *\n * Complex logic handling:\n * 1. Skip if no callback configured\n * 2. Skip if callback respects level AND message is below threshold\n * 3. Call callback with error handling (prevents callback errors from breaking logger)\n *\n * The callbackIgnoresLevel flag enables two modes:\n * - false (default): callback only receives messages that pass threshold (same as console)\n * - true: callback receives ALL messages regardless of threshold (useful for analytics)\n *\n * @param level - Log level\n * @param context - Context identifier\n * @param message - Log message\n * @param shouldSkipConsole - Whether console output was skipped (used for level logic)\n * @param args - Additional arguments\n *\n * @private\n */\n #invokeCallback(\n level: LogLevel,\n context: string,\n message: string,\n shouldSkipConsole: boolean,\n args: unknown[],\n ): void {\n // Early exit: no callback configured, or callback respects level and message is filtered\n if (\n !this.#config.callback ||\n (!this.#config.callbackIgnoresLevel && shouldSkipConsole)\n ) {\n return;\n }\n\n // Re-entrancy guard: a callback calling logger.* re-enters here via\n // #writeLog → #invokeCallback. Skip the nested invocation so the pattern is\n // a safe no-op (console output already happened in #writeLog) instead of\n // recursing to a swallowed RangeError (#791).\n if (this.#inCallback) {\n return;\n }\n\n // Wrap callback invocation in try-catch to prevent user code errors\n // from breaking the logger or causing cascading failures\n this.#inCallback = true;\n try {\n // An async callback (`(...) => Promise<void>` is assignable to the\n // void-typed LogCallback) returns a Promise whose rejection would otherwise\n // leak as a Node `unhandledRejection` — process-fatal under\n // `--unhandled-rejections=strict` (Node 22+ default). Read the runtime\n // return and isolate it like core's subscribe (#944): duck-check the\n // thenable + `.catch` into the same console.error sink a sync throw uses\n // (#1161).\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -- read the runtime Promise of a void-typed async callback (#1161)\n const result: unknown = this.#config.callback(\n level,\n context,\n message,\n ...args,\n );\n\n if (\n result !== null &&\n result !== undefined &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n ) {\n Promise.resolve(result as PromiseLike<unknown>).catch(\n (error: unknown) => {\n this.#reportError(\"[Logger] Error in async callback:\", error);\n },\n );\n }\n } catch (error) {\n // Fallback error reporting if the callback throws synchronously\n this.#reportError(\"[Logger] Error in callback:\", error);\n } finally {\n this.#inCallback = false;\n }\n }\n\n // Report a callback error via console.error directly — never call the logger\n // (would recurse). Shared by the sync-throw catch and the async-rejection\n // `.catch` (#1161). Console-safety guard mirrors #writeToConsole.\n #reportError(message: string, error: unknown): void {\n if (typeof console !== \"undefined\" && typeof console.error === \"function\") {\n console.error(message, error);\n }\n }\n}\n"],"mappings":"kCA2CA,IAAa,EAAb,KAA0B,CAExB,GAAyC,CACvC,MAAO,MACP,qBAAsB,EACxB,EAGA,GAAoB,EAQpB,GAAc,GAWd,YAAY,EAAgC,CACtC,GACF,KAAK,UAAU,CAAM,CAEzB,CAwBA,UAAU,EAAqC,CAK7C,IAAM,EAAQ,EAAO,MAErB,GAAI,IAAU,IAAA,GAAW,CAEvB,GAAI,CAAC,OAAO,OAAOC,EAAAA,cAAe,CAAK,EACrC,MAAU,MACR,uBAAuB,EAAM,uBAAuB,OAAO,KAAKA,EAAAA,aAAa,CAAC,CAAC,KAAK,IAAI,GAC1F,EAGF,KAAKD,GAAQ,MAAQ,EACrB,KAAKE,GAAoBD,EAAAA,cAAc,EACzC,CACI,OAAO,OAAO,EAAQ,UAAU,IAClC,KAAKD,GAAQ,SAAW,EAAO,UAGjC,IAAM,EAAuB,EAAO,qBAEhC,IAAyB,IAAA,KAC3B,KAAKA,GAAQ,qBAAuB,EAExC,CAcA,WAA0B,CACxB,MAAO,CACL,MAAO,KAAKA,GAAQ,MACpB,SAAU,KAAKA,GAAQ,SACvB,qBAAsB,KAAKA,GAAQ,oBACrC,CACF,CAiBA,IAAI,EAAiB,EAAiB,GAAG,EAAuB,CAC9D,KAAKG,GAAU,MAAO,EAAS,EAAS,CAAI,CAC9C,CAkBA,KAAK,EAAiB,EAAiB,GAAG,EAAuB,CAC/D,KAAKA,GAAU,OAAQ,EAAS,EAAS,CAAI,CAC/C,CAkBA,MAAM,EAAiB,EAAiB,GAAG,EAAuB,CAChE,KAAKA,GAAU,QAAS,EAAS,EAAS,CAAI,CAChD,CAiBA,GACE,EACA,EACA,EACA,EACM,CAIN,GAAI,KAAKH,GAAQ,QAAU,QAAU,CAAC,KAAKA,GAAQ,qBACjD,OASF,IAAM,EAJoBI,EAAAA,WAAW,GAIS,KAAKF,GAG9C,GACH,KAAKG,GAAgB,EAAO,EAAS,EAAS,CAAI,EAIpD,KAAKC,GAAgB,EAAO,EAAS,EAAS,EAAmB,CAAI,CACvE,CAiBA,GACE,EACA,EACA,EACA,EACM,CAGN,GACE,OAAO,QAAY,KACnB,OAAO,QAAQ,IAAW,WAC1B,CAIA,IAAM,EAAmB,EAAU,IAAI,EAAQ,IAAI,IAAY,EAE/D,QAAQ,EAAM,CAAC,EAAkB,GAAG,CAAI,CAC1C,CACF,CAsBA,GACE,EACA,EACA,EACA,EACA,EACM,CAGJ,MAAC,KAAKN,GAAQ,UACb,CAAC,KAAKA,GAAQ,sBAAwB,IASrC,MAAKO,GAMT,MAAKA,GAAc,GACnB,GAAI,CASF,IAAM,EAAkB,KAAKP,GAAQ,SACnC,EACA,EACA,EACA,GAAG,CACL,EAGE,GAAW,MAEX,OAAQ,EAAgC,MAAS,YAEjD,QAAQ,QAAQ,CAA8B,CAAC,CAAC,MAC7C,GAAmB,CAClB,KAAKQ,GAAa,oCAAqC,CAAK,CAC9D,CACF,CAEJ,OAAS,EAAO,CAEd,KAAKA,GAAa,8BAA+B,CAAK,CACxD,QAAU,CACR,KAAKD,GAAc,EACrB,CAjCmB,CAkCrB,CAKA,GAAa,EAAiB,EAAsB,CAC9C,OAAO,QAAY,KAAe,OAAO,QAAQ,OAAU,YAC7D,QAAQ,MAAM,EAAS,CAAK,CAEhC,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../../../src/utils/logger/constants.ts"],"sourcesContent":["import type { LogLevel, LogLevelConfig } from \"../../types\";\n\n/**\n * Numeric mapping for log message severity levels.\n *\n * Maps each severity level to a numeric value for threshold comparison.\n * Lower values = lower severity, higher values = higher severity.\n *\n * Used internally to determine if a message should be filtered based on\n * the configured threshold level.\n *\n * Mapping:\n * - `log`: 0 (lowest severity - informational)\n * - `warn`: 1 (medium severity - warnings)\n * - `error`: 2 (highest severity - critical errors)\n *\n * @example\n * ```ts\n * const messageLevel = LOG_LEVELS['warn']; // 1\n * const threshold = 2; // error-only\n * const shouldFilter = messageLevel < threshold; // true (warn is filtered)\n * ```\n *\n * @internal This is used for internal filtering logic\n */\nexport const LOG_LEVELS: Record<LogLevel, number> = Object.freeze({\n log: 0,\n warn: 1,\n error: 2,\n});\n\n/**\n * Numeric thresholds for logger configuration levels.\n *\n * Maps each configuration level to a minimum threshold value.\n * Messages with a severity level below this threshold are filtered out.\n *\n * Threshold logic:\n * - A message is shown if: `LOG_LEVELS[messageLevel] >= LEVEL_CONFIGS[configLevel]`\n * - Higher threshold value = stricter filtering = fewer messages shown\n *\n * Mapping:\n * - `all`: 0 (no filtering - show everything)\n * - Shows: log (0), warn (1), error (2) ✓\n * - `warn-error`: 1 (filter log messages)\n * - Shows: warn (1), error (2) ✓\n * - Filters: log (0) ✗\n * - `error-only`: 2 (filter log and warn messages)\n * - Shows: error (2) ✓\n * - Filters: log (0), warn (1) ✗\n * - `none`: 3 (filter all messages - complete silence)\n * - Filters: log (0), warn (1), error (2) ✗\n *\n * @example\n * ```ts\n * // Configuration: warn-error\n * const threshold = LEVEL_CONFIGS['warn-error']; // 1\n *\n * // Check if 'log' message should be shown\n * LOG_LEVELS['log'] >= threshold // 0 >= 1 = false (filtered)\n *\n * // Check if 'warn' message should be shown\n * LOG_LEVELS['warn'] >= threshold // 1 >= 1 = true (shown)\n *\n * // Check if 'error' message should be shown\n * LOG_LEVELS['error'] >= threshold // 2 >= 1 = true (shown)\n * ```\n *\n * @internal This is used for internal threshold comparison\n */\nexport const LEVEL_CONFIGS: Record<LogLevelConfig, number> = Object.freeze({\n all: 0,\n \"warn-error\": 1,\n \"error-only\": 2,\n none: 3,\n});\n"],"mappings":"AAyBA,MAAa,EAAuC,OAAO,OAAO,CAChE,IAAK,EACL,KAAM,EACN,MAAO,CACT,CAAC,EAyCY,EAAgD,OAAO,OAAO,CACzE,IAAK,EACL,aAAc,EACd,aAAc,EACd,KAAM,CACR,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{c as e,d as t,l as n,n as r,o as i,r as a,s as o,t as s,u as c}from"./buildParamMeta-bOLhLF9h.mjs";const l=Object.freeze({ROUTER_NOT_STARTED:`NOT_STARTED`,NO_START_PATH_OR_STATE:`NO_START_PATH_OR_STATE`,ROUTER_ALREADY_STARTED:`ALREADY_STARTED`,ROUTE_NOT_FOUND:`ROUTE_NOT_FOUND`,SAME_STATES:`SAME_STATES`,CANNOT_DEACTIVATE:`CANNOT_DEACTIVATE`,CANNOT_ACTIVATE:`CANNOT_ACTIVATE`,TRANSITION_ERR:`TRANSITION_ERR`,TRANSITION_CANCELLED:`CANCELLED`,ROUTER_DISPOSED:`DISPOSED`,PLUGIN_CONFLICT:`PLUGIN_CONFLICT`,CONTEXT_NAMESPACE_ALREADY_CLAIMED:`CONTEXT_NAMESPACE_ALREADY_CLAIMED`,REENTRANT_NAVIGATION:`REENTRANT_NAVIGATION`,REENTRANT_TREE_MUTATION:`REENTRANT_TREE_MUTATION`}),u=`@@router/UNKNOWN_ROUTE`,d={UNKNOWN_ROUTE:u},f={ROUTER_START:`onStart`,ROUTER_STOP:`onStop`,TRANSITION_START:`onTransitionStart`,TRANSITION_LEAVE_APPROVE:`onTransitionLeaveApprove`,TRANSITION_CANCEL:`onTransitionCancel`,TRANSITION_SUCCESS:`onTransitionSuccess`,TRANSITION_ERROR:`onTransitionError`},p={ROUTER_START:`$start`,ROUTER_STOP:`$stop`,TRANSITION_START:`$$start`,TRANSITION_LEAVE_APPROVE:`$$leaveApprove`,TRANSITION_CANCEL:`$$cancel`,TRANSITION_SUCCESS:`$$success`,TRANSITION_ERROR:`$$error`},m={maxDependencies:100,maxPlugins:50,maxListeners:1e4,warnListeners:1e3,maxLifecycleHandlers:200},h=Object.freeze({}),g=Object.freeze({deactivated:Object.freeze([]),activated:Object.freeze([]),intersection:``}),ee=Object.freeze({phase:`activating`,reason:`success`,segments:g});function te(e){if(!e||typeof e!=`object`||e.constructor!==Object)throw TypeError(`dependencies must be a plain object`);for(let t in e)if(Object.getOwnPropertyDescriptor(e,t)?.get)throw TypeError(`dependencies cannot contain getters: "${t}"`)}function _(e,t){for(let n of e){let e=n;if(typeof e!=`object`||!e||Array.isArray(e))throw TypeError(`route must be a non-array object`);t?.routes.guardRouteCallbacks(n),t?.routes.guardNoAsyncCallbacks(n);let r=n.children;r&&_(r,t)}}const v=new Set([`all`,`warn-error`,`error-only`,`none`]);function ne(e){return typeof e==`string`&&v.has(e)}function re(e){return typeof e==`string`?`"${e}"`:typeof e==`object`?JSON.stringify(e):String(e)}function ie(e){if(typeof e!=`object`)throw TypeError(`Logger config must be an object`);let t=e;for(let e of Object.keys(t))if(e!==`level`&&e!==`callback`&&e!==`callbackIgnoresLevel`)throw TypeError(`Unknown logger config property: "${e}"`);if(`level`in t&&t.level!==void 0&&!ne(t.level))throw TypeError(`Invalid logger level: ${re(t.level)}. Expected: "all" | "warn-error" | "error-only" | "none"`);if(`callback`in t&&t.callback!==void 0&&typeof t.callback!=`function`)throw TypeError(`Logger callback must be a function, got ${typeof t.callback}`);if(`callbackIgnoresLevel`in t&&t.callbackIgnoresLevel!==void 0&&typeof t.callbackIgnoresLevel!=`boolean`)throw TypeError(`Logger callbackIgnoresLevel must be a boolean, got ${typeof t.callbackIgnoresLevel}`)}function ae(e){return Object.freeze(e)}function oe(e={}){return{...m,...e}}function y(e){if(e===void 0)return e;let t;for(let n in e){if(!Object.hasOwn(e,n))continue;let r=e[n];r!==void 0&&(t??={},t[n]=r)}return t??h}function se(e={}){let t=Object.create(null);for(let n in e)e[n]!==void 0&&(t[n]=e[n]);return{dependencies:t,limits:m}}function b(e,t){let n=e.path,r=n.startsWith(`~`),i=r?n.slice(1):n,a=i!==``&&!i.startsWith(`/`)&&!i.startsWith(`?`)?`/${i}`:i,o={name:e.name,path:a,absolute:r,children:[],parent:t};if(e.children)for(let t of e.children){let e=b(t,o);o.children.push(e)}return o}function ce(e,t,n){let r=b({name:e,path:t},null);for(let e of n){let t=b(e,r);r.children.push(t)}return r}const le=/[^\w!$'()*+,.:;|~-]/gu,ue=/[^\w!$'()*+,.:;|~-]/u,de=/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,x=e=>t=>{try{return e(t)}catch{return e(t.replaceAll(de,`�`))}},fe=x(e=>e.replaceAll(le,e=>encodeURIComponent(e))),pe={default:e=>ue.test(e)?fe(e):e,uri:x(encodeURI),uriComponent:x(encodeURIComponent),none:e=>e},me={default:decodeURIComponent,uri:decodeURI,uriComponent:decodeURIComponent,none:e=>e},he=(e,t)=>{let n=pe[t],r=String(e).split(`/`),i=n(r[0]);for(let e=1;e<r.length;e++)i+=`/`+n(r[e]);return i},ge=Object.freeze(Object.create(null));function S(){return{staticChildren:ge,hasChildren:!1,paramChild:void 0,splatChild:void 0,route:void 0,slashChildRoute:void 0}}function C(e){return e.length>1&&e.endsWith(`/`)?e.slice(0,-1):e}function _e(e,t){return e===``?t:t===``?e:e+t}function ve(e){return e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102}function ye(e){let t=0;for(;t<e.length;)if(e.codePointAt(t)===37){if(t+2>=e.length)return!1;let n=e.codePointAt(t+1)??0,r=e.codePointAt(t+2)??0;if(!ve(n)||!ve(r))return!1;t+=3}else t++;return!0}const be=Object.freeze([]),xe=Object.freeze(new Set),Se=Object.freeze([]),Ce=Object.freeze({}),we=Object.freeze({});function Te(e,t,n){let r=t.has(e.name);return{paramName:e.name,encoder:r?e=>he(e,n):pe[n]}}function Ee(e,t,n){let r=new Set,a=new Set;for(let e of t){for(let t of e.paramMeta.urlParams)r.add(t);for(let t of e.paramMeta.spatParams)a.add(t)}if(r.size===0)return{buildStaticParts:[e],buildParamSlots:Se};let s=[],c=[],l=``,u=o(e);for(let[e,t]of u.entries()){e>0&&(l+=`/`);let r=i(t);if(`error`in r||r.kind===`static`){l+=t;continue}s.push(l),l=``,c.push(Te(r,a,n))}return s.push(l),{buildStaticParts:s,buildParamSlots:c}}function De(e,t,n){throw Error(`[SegmentMatcher.registerTree] Parameter name conflict at the same path position: '${n}${e}' and '${n}${t}'. A parametric URL segment binds to a single name across every route that shares that position — the value cannot be captured under two names. Rename one so both routes agree (e.g. use '${n}${e}' in both).`)}function Oe(){throw Error(`[SegmentMatcher.registerTree] Empty parameter name: a parameter marker (':' or '*') must be followed by a name (e.g. ':id', '*rest'). A name-less marker, or a trailing '?' with no parameter name, would capture under an empty key at match but emit a literal at build — the two disagree, so it is rejected.`)}function ke(e){throw Error(`[SegmentMatcher.registerTree] Fused parameter marker in segment "${e}": a ':'/'*' marker must begin a segment (e.g. 'a/:b', not 'a:b'). build extracts it as a param while the trie treats the segment as a literal — the two disagree.`)}function Ae(e){throw Error(`[SegmentMatcher.registerTree] Trailing parameter marker in segment "${e}": a param name cannot end in a bare ':' or '*' (e.g. ':y*' — the name is 'y' plus a stray marker). build/meta would capture the marker into the name while the gate rejects it as name-less — the two disagree, so it is rejected.`)}function je(e){throw Error(`[SegmentMatcher.registerTree] Optional params are not supported: "${e}" — declare two sibling routes instead (one with the segment, one without). The route hierarchy already expresses optionality.`)}function Me(e){throw Error(`[SegmentMatcher.registerTree] Regex constraints are not supported: "<"/">" are reserved in path segments ("${e}"). Match the segment as a plain string and validate the value in a guard (canActivate) or app code.`)}function Ne(e){throw Error(`[SegmentMatcher.registerTree] Non-ASCII static segment "${e}": match rejects non-ASCII input and compares static keys raw, so this route would never match. Percent-encode it (e.g. "/caf%C3%A9") or use a param.`)}function Pe(e,t){switch(e){case`name-less`:return Oe();case`trailing-marker`:return Ae(t);case`fused-marker`:return ke(t);case`optional-removed`:return je(t);case`constraint-removed`:return Me(t)}}function Fe(e,t){let n=new Set,r=``;for(let e of t){if(n.has(e)){r=e;break}n.add(e)}throw Error(`[SegmentMatcher.registerTree] Duplicate parameter name ':${r}' in route "${e}": a param name must be unique within a route — two positions cannot both bind ':${r}' (the second silently overwrites the first). Rename one.`)}function Ie(e,t){throw Error(`[SegmentMatcher.registerTree] Invalid query-param declaration "${t}" in route "${e}": a query-param name cannot contain '<' or '>' — it would never round-trip. Rename the query param.`)}function Le(e,t){throw Error(`[SegmentMatcher.registerTree] Name collision in route "${e}": "${t}" is declared as BOTH a path param (':${t}') and a query param ('?${t}'). buildPath would emit its value twice (once in the path, once in the query). Rename one.`)}function Re(e,t){throw Error(`[SegmentMatcher.registerTree] Duplicate route path: routes "${e}" and "${t}" resolve to the same URL. The later registration would silently shadow the earlier (its deep link would resolve to the other route). Give them distinct paths.`)}function ze(e,t){throw Error(`[SegmentMatcher.registerTree] Index route "${e}" (path "/") under the splat parent "${t}" is not supported: the index sits on the splat node, which the wildcard match never reaches, so it is unreachable. Give the index a distinct path, or make the parent static.`)}function Be(e){let t=i(e);return(`error`in t||t.kind===`static`)&&Oe(),t.name}function Ve(e,t){return e.paramChild?e.paramChild.name!==t&&De(e.paramChild.name,t,`:`):e.paramChild={node:S(),name:t},e.paramChild.node}function He(e,t){return e.splatChild?e.splatChild.name!==t&&De(e.splatChild.name,t,`*`):e.splatChild={node:S(),name:t},e.splatChild.node}function Ue(e,t){e.route!==void 0&&e.route!==t&&Re(e.route.name,t.name),e.route=t}function We(e){for(let t=0;t<e.length;t++)if(e.charCodeAt(t)>=128)return!0;return!1}function Ge(e,t,n){let r=C(n);if(r===`/`){Ue(e.root,t);return}Ke(e,e.root,r,1,t)}function Ke(e,t,n,r,i){let a=n.length;for(;r<=a;){let i=n.indexOf(`/`,r),o=i===-1?a:i,s=n.slice(r,o);t=Xe(e,t,s),r=o+1}Ue(t,i)}function qe(e,t,n){n.slice(n.lastIndexOf(`/`)+1).startsWith(`*`)&&ze(t.name,n);let r=Je(e,n);r.slashChildRoute=t}function Je(e,t){return Ye(e,e.root,t)}function Ye(e,t,n){let r=C(n);if(r===`/`||r===``)return t;let i=t,a=1,o=r.length;for(;a<=o;){let t=r.indexOf(`/`,a),n=t===-1?o:t;if(n<=a)break;let s=r.slice(a,n);i=Xe(e,i,s),a=n+1}return i}function Xe(e,t,n){if(n.startsWith(`*`)){let e=He(t,Be(n));return t.hasChildren=!0,e}if(n.startsWith(`:`)){let e=Ve(t,Be(n));return t.hasChildren=!0,e}We(n)&&Ne(n);let r=e.options.caseSensitive?n:n.toLowerCase();return r in t.staticChildren||(t.staticChildren===ge&&(t.staticChildren=Object.create(null)),t.staticChildren[r]=S(),t.hasChildren=!0),t.staticChildren[r]}function Ze(e,t,n,r,a){let c=t.fullName===``;c||r.push(t);let l=t.absolute,u=t.paramMeta===s?t.path:t.paramMeta.pathPattern,d=l&&u.startsWith(`~`)?u.slice(1):u,f=l?d:u;for(let e of o(f)){let t=i(e);`error`in t&&Pe(t.error,e)}let p=f,m=l?p:_e(n,p),h=c?a:Qe(e,t,m,l?``:n,r,a);for(let n of t.children.values())Ze(e,n,m,r,h);c||r.pop()}function Qe(e,t,n,r,i,a){let o=rt(n,r),s=Object.freeze([...i]),c=$e(s),l=C(n),u=it(e.rootQueryParams,i),{buildStaticParts:d,buildParamSlots:f}=Ee(o?C(r):l,o?i.slice(0,-1):i,e.options.urlParamsEncoding),p=f.map(e=>e.paramName),m=p.length===0?xe:new Set(p);m.size!==p.length&&Fe(t.fullName,p),at(t.fullName,u,m);let h={name:t.fullName,parent:a,matchSegments:s,meta:c,declaredQueryParams:u,declaredQueryParamsSet:u.length===0?xe:new Set(u),hasTrailingSlash:n.length>1&&n.endsWith(`/`),buildStaticParts:d,buildParamSlots:f,buildParamNamesSet:m,cachedResult:void 0};return t.paramMeta.urlParams.length===0&&(h.cachedResult=Object.freeze({segments:h.matchSegments,params:Ce,meta:h.meta})),e.routesByName.set(t.fullName,h),o?tt(e,h,r):nt(e,h,n,l,t),h}function $e(e){let t;for(let n of e)et(n.paramTypeMap)&&(t??={},t[n.fullName]=n.paramTypeMap);return t===void 0?we:Object.freeze(t)}function et(e){for(let t in e)if(Object.hasOwn(e,t))return!0;return!1}function tt(e,t,n){qe(e,t,n);let r=C(n),i=e.options.caseSensitive?r:r.toLowerCase();e.staticCache.has(i)&&e.staticCache.set(i,t)}function nt(e,t,n,r,i){if(Ge(e,t,n),i.paramMeta.urlParams.length===0){let n=e.options.caseSensitive?r:r.toLowerCase();e.staticCache.set(n,t)}}function rt(e,t){return C(e)===C(t)}function it(e,t){let n=[];e.length>0&&n.push(...e);for(let e of t)e.paramMeta.queryParams.length>0&&n.push(...e.paramMeta.queryParams);return n.length===0?be:n}function at(e,t,n){for(let i of t)r.test(i)&&Ie(e,i),n.has(i)&&Le(e,i)}function ot(e){return typeof e==`string`?e:typeof e==`object`?JSON.stringify(e):String(e)}function st(e,t,n){t===`__proto__`?Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0}):e[t]=n}var ct=class{get options(){return this.#e}#e;#t=S();#n=new Map;#r=new Map;#i={cleanPath:``,normalized:``,queryString:void 0};#a=[];#o=``;#s;#c;constructor(e){this.#e={caseSensitive:e.caseSensitive??!0,strictTrailingSlash:e.strictTrailingSlash??!1,strictQueryParams:e.strictQueryParams??!1,urlParamsEncoding:e.urlParamsEncoding??`default`,parseQueryString:e.parseQueryString,buildQueryString:e.buildQueryString},this.#s=this.#e.caseSensitive,this.#c=this.#e.urlParamsEncoding===`none`?null:me[this.#e.urlParamsEncoding]}registerTree(e){this.#a=e.paramMeta.queryParams,Ze({root:this.#t,options:this.#e,routesByName:this.#n,staticCache:this.#r,rootQueryParams:this.#a},e,``,[],null)}match(e){if(!this.#f(e))return;let{cleanPath:t,normalized:n,queryString:r}=this.#i,i=this.#s?n:n.toLowerCase(),a=this.#r.get(i);if(a)return this.#e.strictTrailingSlash&&!this.#g(t,a)?void 0:r===void 0&&a.cachedResult?a.cachedResult:this.#m(a,{},r);let o={},s=this.#_(n,o);if(s&&!(this.#e.strictTrailingSlash&&!this.#g(t,s))&&this.#b(o))return this.#m(s,o,r)}buildPath(e,t,n){let r=this.#n.get(e);if(!r)throw Error(`[SegmentMatcher.buildPath] '${e}' is not defined`);let i=this.#l(r,t),a=this.#u(i,n?.trailingSlash),o=this.#d(r,t,n?.queryParamsMode);return a+(o?`?${o}`:``)}getSegmentsByName(e){return this.#n.get(e)?.matchSegments}getMetaByName(e){return this.#n.get(e)?.meta}hasRoute(e){return this.#n.has(e)}#l(e,t){let n=e.buildStaticParts,r=e.buildParamSlots;if(r.length===0)return n[0];let i=n[0];for(let[e,a]of r.entries()){let r=t?.[a.paramName];if(r==null)throw Error(`[SegmentMatcher.buildPath] Missing required param '${a.paramName}'`);if(r===``)throw Error(`[SegmentMatcher.buildPath] Missing required param '${a.paramName}' (empty string)`);let o=a.encoder(ot(r));i+=o+n[e+1]}return i}#u(e,t){return t===`always`&&!e.endsWith(`/`)?`${e}/`:t===`never`&&e!==`/`&&e.endsWith(`/`)?e.slice(0,-1):e}#d(e,t,n){if(!t||e.declaredQueryParams.length===0&&n!==`loose`)return``;let r={},i=!1;for(let n of e.declaredQueryParams)n in t&&(r[n]=t[n],i=!0);if(n===`loose`)for(let n in t)Object.hasOwn(t,n)&&!e.declaredQueryParamsSet.has(n)&&!e.buildParamNamesSet.has(n)&&(r[n]=t[n],i=!0);return i?this.#e.buildQueryString(r):``}#f(e){if(e===``&&(e=`/`),e.codePointAt(0)!==47)return!1;let t=this.#p(e);if(t===-2)return!1;t===-3&&(e=this.#o);let n=t>=0?e.slice(0,t):e,r=t>=0?e.slice(t+1):void 0;if(r!==void 0){let e=r.indexOf(`#`);e!==-1&&(r=r.slice(0,e))}let i=C(n);return this.#i.cleanPath=n,this.#i.normalized=i,this.#i.queryString=r,!0}#p(e){let t=!1;for(let n=0;n<e.length;n++){let r=e.codePointAt(n);if(r===35)return this.#o=e.slice(0,n),-3;if(r===63)return n;if(r>=128)return-2;if(r===47){if(t)return-2;t=!0}else t=!1}return-1}#m(e,t,n){if(!(n!==void 0&&!this.#h(e,t,n)))return{segments:e.matchSegments,params:t,meta:e.meta}}#h(e,t,n){let r;try{r=this.#e.parseQueryString(n)}catch{return!1}if(this.#e.strictQueryParams){let n=e.declaredQueryParamsSet;for(let e in r){if(!n.has(e))return!1;st(t,e,r[e])}}else for(let e in r)st(t,e,r[e]);return!0}#g(e,t){return(e.length>1&&e.endsWith(`/`))===t.hasTrailingSlash}#_(e,t){return e.length===1?this.#t.slashChildRoute??this.#t.route:this.#v(this.#t,e,1,t)}#v(e,t,n,r){let i=e,a=t.length,o=this.#s;for(;n<=a;){let e=t.indexOf(`/`,n),s=e===-1?a:e,c=t.slice(n,s),l=o?c:c.toLowerCase(),u;if(l in i.staticChildren)u=i.staticChildren[l];else if(i.paramChild){let e=i.paramChild;if(i.splatChild!==void 0){let a={[e.name]:c},o=this.#v(e.node,t,s+1,a);return o===void 0?this.#y(i.splatChild,t,n,r):(Object.assign(r,a),o)}u=e.node,r[e.name]=c}else if(i.splatChild)return this.#y(i.splatChild,t,n,r);else return;i=u,n=s+1}return i.slashChildRoute??i.route}#y(e,t,n,r){let i=e.node;if(!i.hasChildren)return r[e.name]=t.slice(n),i.route;let a={},o=this.#v(i,t,n,a);return o?(Object.assign(r,a),o):(r[e.name]=t.slice(n),i.route)}#b(e){let t=this.#c;if(!t)return!0;for(let n in e){let r=e[n];if(r.includes(`%`)){if(!ye(r))return!1;try{e[n]=t(r)}catch{return!1}}}return!0}};const lt=Object.freeze(new Map),ut=Object.freeze([]);function dt(e){return e.parent?.name?`${e.parent.fullName}.${e.name}`:e.name}function ft(e){let t=new Map;for(let n of e)t.set(n.name,n);return t}function pt(e,t){let n=[],r=[];for(let i of e){let e=mt(i,t);n.push(e),e.absolute||r.push(e)}return{childrenMap:ft(n),nonAbsoluteChildren:r}}function mt(e,t){let n=a(e.path),r=n.urlParams.length===0&&n.queryParams.length===0&&n.spatParams.length===0&&n.pathPattern===e.path?s:n,i=r.paramTypeMap,o={name:e.name,path:e.path,absolute:e.absolute,parent:t,children:void 0,paramMeta:r,nonAbsoluteChildren:void 0,fullName:``,paramTypeMap:i};if(o.fullName=dt(o),e.children.length===0)o.children=lt,o.nonAbsoluteChildren=ut;else{let{childrenMap:t,nonAbsoluteChildren:n}=pt(e.children,o);o.children=t,o.nonAbsoluteChildren=n,Object.freeze(o.nonAbsoluteChildren),Object.freeze(o.children)}return Object.freeze(i),Object.freeze(r.urlParams),Object.freeze(r.queryParams),Object.freeze(r.spatParams),Object.freeze(r),Object.freeze(o),o}function ht(e){return mt(e,null)}function gt(e,t,n){return ht(ce(e,t,n))}function _t(e){let t=e.absolute?`~${e.path}`:e.path,n={name:e.name,path:t};return e.children.size>0&&(n.children=Array.from(e.children.values(),_t)),n}function vt(e){return Array.from(e.children.values(),_t)}const yt=e=>{let t=e.indexOf(`%`),n=e.indexOf(`+`);if(t===-1&&n===-1)return e;let r=n===-1?e:e.replaceAll(`+`,` `);return t===-1?r:decodeURIComponent(r)},bt=(e,t)=>{if(e===void 0)return t.boolean.decodeUndefined();let n=t.boolean.decodeRaw(e);if(n!==null)return n;let r=yt(e),i=t.number.decode(r);return i===null?t.boolean.decodeValue(r):i},xt=/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,St=e=>{try{return encodeURIComponent(e)}catch(t){if(!(t instanceof URIError))throw t;return encodeURIComponent(String(e).replaceAll(xt,`�`))}},w=e=>{let t=typeof e;if(t!==`string`&&t!==`number`&&t!==`boolean`)throw TypeError(`[search-params] Array element must be a string, number, or boolean — received ${t}`);return St(e)},Ct=(e,t,n,r)=>{let i=`${e}${n}`,a=[];for(let e of t)if(e===null){let e=r.encode(i);e&&a.push(e)}else a.push(`${i}=${w(e)}`);return a.join(`&`)},wt={none:{encodeArray:(e,t,n)=>Ct(e,t,``,n)},brackets:{encodeArray:(e,t,n)=>Ct(e,t,`[]`,n)},index:{encodeArray:(e,t,n)=>{let r=[];for(let[i,a]of t.entries()){let t=`${e}[${i}]`;if(a===null){let e=n.encode(t);e&&r.push(e)}else r.push(`${t}=${w(a)}`)}return r.join(`&`)},indexed:!0},comma:{encodeArray:(e,t)=>{let n=[];for(let e of t)e!==null&&n.push(w(e));return n.length===0?``:`${e}=${n.join(`,`)}`},decodeValue:e=>e.includes(`,`)?e.split(`,`):null}},Tt={none:{encode:(e,t)=>`${e}=${t}`,decodeUndefined:()=>null,decodeRaw:()=>null,decodeValue:e=>e},auto:{encode:(e,t)=>`${e}=${t}`,decodeUndefined:()=>null,decodeRaw:e=>e===`true`||e!==`false`&&null,decodeValue:e=>e},"empty-true":{encode:(e,t)=>t?e:`${e}=false`,decodeUndefined:()=>!0,decodeRaw:e=>e===`true`||e!==`false`&&null,decodeValue:e=>e}},Et={default:{encode:e=>e},hidden:{encode:()=>``}},Dt={auto:{decode:e=>{let t=e.length;if(t===0)return null;let n=+(e.codePointAt(0)===45);if(n===t||t-n>1&&e.codePointAt(n)===48&&e.codePointAt(n+1)!==46)return null;let r=!1;for(let i=n;i<t;i++){let a=e.codePointAt(i);if(!(a!==void 0&&a>=48&&a<=57)){if(a===46&&!r&&i!==n&&i!==t-1){r=!0;continue}return null}}let i=Number(e);return Object.is(i,-0)||!Number.isSafeInteger(i)&&!r?null:i}},none:{decode:()=>null}},T=(e,t,n,r)=>{if(e===void 0)throw TypeError(`[search-params] Unknown ${t} "${n}" — expected ${r}`);return e},Ot=(e,t,n,r)=>({boolean:T(Tt[t],`booleanFormat`,t,`"none" | "auto" | "empty-true"`),null:T(Et[n],`nullFormat`,n,`"default" | "hidden"`),number:T(Dt[r],`numberFormat`,r,`"none" | "auto"`),array:T(wt[e],`arrayFormat`,e,`"none" | "brackets" | "index" | "comma"`)}),kt={boolean:Tt.auto,null:Et.default,number:Dt.auto,array:wt.none},E={arrayFormat:`none`,booleanFormat:`auto`,nullFormat:`default`,numberFormat:`auto`},At={...E,strategies:kt},jt=e=>{if(!e||e.arrayFormat===void 0&&e.booleanFormat===void 0&&e.nullFormat===void 0&&e.numberFormat===void 0)return At;let t=e.arrayFormat??E.arrayFormat,n=e.booleanFormat??E.booleanFormat,r=e.nullFormat??E.nullFormat,i=e.numberFormat??E.numberFormat;return{arrayFormat:t,booleanFormat:n,nullFormat:r,numberFormat:i,strategies:Ot(t,n,r,i)}},D=e=>St(e),Mt=(e,t,n)=>{let r=D(e);switch(typeof t){case`string`:case`number`:return`${r}=${D(t)}`;case`boolean`:return n.strategies.boolean.encode(r,t);case`object`:return t===null?n.strategies.null.encode(r):Array.isArray(t)?n.strategies.array.encodeArray(r,t,n.strategies.null):`${r}=${D(t)}`;default:return`${r}=${D(t)}`}};function O(e,t,n){t===`__proto__`?Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0}):e[t]=n}function Nt(e,t,n,r){if(!Object.hasOwn(e,t)){O(e,t,r?[n]:n);return}let i=e[t];Array.isArray(i)?i.push(n):O(e,t,[i,n])}function Pt(e,t,n,r,i){return bt(r?e.slice(t+1,n):void 0,i)}function Ft(e,t,n){let r=t+1,i=0,a=!1;for(;r<n;){let t=e.codePointAt(r);if(t===93)return a?i:null;if(t!==void 0&&t>=48&&t<=57){i=i*10+(t-48),a=!0,r++;continue}return null}return null}function It(e,t,n){let{searchPart:r,nameEnd:i,nameSourceEnd:a,eqPos:o,end:s,hasValue:c,decodedName:l}=e,u=Ft(r,i,a);if(u===null)return!1;let d=Pt(r,o,s,c,t),f=n.get(l);return f===void 0?n.set(l,[[u,d]]):f.push([u,d]),!0}function Lt(e,t,n,r,i,a,o){let s=a!==-1&&a<n,c=s?a:n,l=c,u=!1;for(let n=t;n<c;n++)if(e.codePointAt(n)===91){l=n,u=!0;break}let d=yt(e.slice(t,l));if(!(o!==void 0&&u&&It({searchPart:e,nameEnd:l,nameSourceEnd:c,eqPos:a,end:n,hasValue:s,decodedName:d},i,o))){if(!u&&s&&i.array.decodeValue){let t=e.slice(a+1,n),o=i.array.decodeValue(t);if(o){for(let e of o)Nt(r,d,bt(e,i),!0);return}}Nt(r,d,Pt(e,a,n,s,i),u)}}const Rt=(e,t)=>{if(e===``||e===`?`)return{};let n={};return zt(e,n,jt(t).strategies),n};function zt(e,t,n){let r=n.array.indexed?new Map:void 0,i=0,a=e.length,o=-2;for(;i<a;){let s=e.indexOf(`&`,i);s===-1&&(s=a),s>i&&(o!==-1&&o<i&&(o=e.indexOf(`=`,i)),Lt(e,i,s,t,n,o,r)),i=s+1}if(r!==void 0)for(let[e,n]of r)n.sort((e,t)=>e[0]-t[0]),O(t,e,n.map(e=>e[1]))}const Bt=(e,t)=>{let n=Object.keys(e);if(n.length===0)return``;let r=jt(t),i=[];for(let t of n){let n=e[t];if(n===void 0)continue;let a=Mt(t,n,r);a&&i.push(a)}return i.join(`&`)};function Vt(e){let t=e?.queryParams;return new ct({...e?.caseSensitive!==void 0&&{caseSensitive:e.caseSensitive},...e?.strictTrailingSlash!==void 0&&{strictTrailingSlash:e.strictTrailingSlash},...e?.strictQueryParams!==void 0&&{strictQueryParams:e.strictQueryParams},...e?.urlParamsEncoding!==void 0&&{urlParamsEncoding:e.urlParamsEncoding},parseQueryString:e=>Rt(e,t),buildQueryString:e=>Bt(e,t)})}const Ht={defaultRoute:``,defaultParams:{},trailingSlash:`preserve`,caseSensitive:!0,queryParamsMode:`loose`,queryParams:E,urlParamsEncoding:`default`,allowNotFound:!0,rewritePathOnMatch:!0};function Ut(e){Object.freeze(e);for(let t of Object.values(e))t&&typeof t==`object`&&t.constructor===Object&&Ut(t);return e}function Wt(e,t){return typeof e==`function`?e(t):e}function Gt(e){if(!e||typeof e!=`object`||Array.isArray(e))throw TypeError(`[router.constructor] options must be a plain object`)}var Kt=class{#e;constructor(e={}){this.#e=Ut({...Ht,...e})}static validateOptionsIsObject(e){Gt(e)}get(){return this.#e}};function k(e,t){if(e===t)return!0;if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(!k(e[n],t[n]))return!1;return!0}return!1}const qt=new WeakMap;function A(e){return qt.get(e)}function Jt(e,t){qt.set(e,t)}var Yt=class{#e=void 0;#t=void 0;#n;get(){return this.#e}set(e){this.#t=this.#e,this.#e=e?ae(e):void 0}getPrevious(){return this.#t}reset(){this.#e=void 0,this.#t=void 0}setDependencies(e){this.#n=e}makeState(e,t,n,r,i){let a=this.#n.getDefaultParams(),o=Object.hasOwn(a,e),s;s=o?Object.freeze({...a[e],...t}):!t||t===h?h:Object.freeze({...t});let c={name:e,params:s,path:n??this.#n.buildPath(e,t),context:{},...!i&&{transition:ee}};return r&&Jt(c,r),i?c:ae(c)}areStatesEqual(e,t,n=!0){if(!e||!t)return!!e==!!t;if(e.name!==t.name)return!1;if(n){let n=this.#n.getUrlParams(e.name);for(let r of n)if(!k(e.params[r],t.params[r]))return!1;return!0}let r=Object.keys(e.params),i=Object.keys(t.params);if(r.length!==i.length)return!1;for(let n of r)if(!(n in t.params)||!k(e.params[n],t.params[n]))return!1;return!0}};const Xt={[f.ROUTER_START]:p.ROUTER_START,[f.ROUTER_STOP]:p.ROUTER_STOP,[f.TRANSITION_SUCCESS]:p.TRANSITION_SUCCESS,[f.TRANSITION_START]:p.TRANSITION_START,[f.TRANSITION_LEAVE_APPROVE]:p.TRANSITION_LEAVE_APPROVE,[f.TRANSITION_ERROR]:p.TRANSITION_ERROR,[f.TRANSITION_CANCEL]:p.TRANSITION_CANCEL},Zt=Object.keys(Xt),j=`router.usePlugin`;function Qt(e){if(!(e&&typeof e==`object`)||Array.isArray(e))throw TypeError(`[router.usePlugin] Plugin factory must return an object, got ${typeof e}`);if(typeof e.then==`function`)throw TypeError(`[router.usePlugin] Async plugin factories are not supported. Factory returned a Promise instead of a plugin object.`)}var $t=class e{#e=new Set;#t=new Set;#n;static validatePlugin(e){Qt(e)}setDependencies(e){this.#n=e}count(){return this.#e.size}use(...e){if(this.#n.getValidator()?.plugins.validateCountThresholds(this.#e.size+e.length),e.length===1){let t=e[0],n=this.#i(t);this.#e.add(t);let r=!1,i=()=>{if(!r){r=!0,this.#e.delete(t),this.#t.delete(i);try{n()}catch(e){this.#n.logger.error(j,`Error during cleanup:`,e)}}};return this.#t.add(i),i}let t=this.#r(e),n=[];try{for(let e of t){let t=this.#i(e);n.push({factory:e,cleanup:t})}}catch(e){for(let{cleanup:e}of n)try{e()}catch(e){this.#n.logger.error(j,`Cleanup error:`,e)}throw e}for(let{factory:e}of n)this.#e.add(e);let r=!1,i=()=>{if(!r){r=!0,this.#t.delete(i);for(let{factory:e}of n)this.#e.delete(e);for(let{cleanup:e}of n)try{e()}catch(e){this.#n.logger.error(j,`Error during cleanup:`,e)}}};return this.#t.add(i),i}getAll(){return[...this.#e]}has(e){return this.#e.has(e)}disposeAll(){for(let e of this.#t)e();this.#e.clear(),this.#t.clear()}#r(e){let t=new Set;for(let n of e)t.has(n)?this.#n.getValidator()?.plugins.warnBatchDuplicates(e):t.add(n);return t}#i(t){let n=this.#n.compileFactory(t);e.validatePlugin(n),this.#n.getValidator()?.plugins.validatePluginKeys(n),Object.freeze(n);let r=[];for(let e of Zt)e in n&&(typeof n[e]==`function`?(r.push(this.#n.addEventListener(Xt[e],n[e])),e===`onStart`&&this.#n.canNavigate()&&this.#n.getValidator()?.plugins.warnPluginAfterStart(e)):this.#n.getValidator()?.plugins.warnPluginMethodType(e));return()=>{for(let e of r)e();typeof n.teardown==`function`&&n.teardown()}}};const en=()=>!0,tn=()=>!1,nn=()=>en,rn=()=>tn;function an(e){return e?nn:rn}var on=class{#e=new Map;#t=new Map;#n=new Map;#r=new Map;#i=new Map;#a=new Map;#o=[this.#i,this.#a];#s;setDependencies(e){this.#s=e}getHandlerCount(e){let t=e===`activate`?this.#e:this.#n,n=e===`activate`?this.#t:this.#r;if(t.size===0)return n.size;if(n.size===0)return t.size;let r=new Set(t.keys());for(let e of n.keys())r.add(e);return r.size}preflightHandlerLimit(e,t,n){let r=this.#s.getValidator();if(!r)return;let i=(e,t,i)=>{let{definition:a,external:o}=this.#d(e),s=0;for(let e of t)(n?o.has(e):a.has(e)||o.has(e))||s++;if(s===0)return;let c=n?o.size:this.getHandlerCount(e);r.lifecycle.validateHandlerLimit(c+s-1,i)};i(`activate`,e,`canActivate`),i(`deactivate`,t,`canDeactivate`)}addCanActivate(e,t,n=!1,r){this.#c(`activate`,e,t,n,`canActivate`,r)}addCanDeactivate(e,t,n=!1,r){this.#c(`deactivate`,e,t,n,`canDeactivate`,r)}clearCanActivate(e,t){this.#l(`activate`,e,t)}clearCanDeactivate(e,t){this.#l(`deactivate`,e,t)}clearAll(){this.#e.clear(),this.#t.clear(),this.#n.clear(),this.#r.clear(),this.#a.clear(),this.#i.clear()}clearDefinitionGuards(){for(let e of this.#e.keys())this.#t.has(e)?this.#u(`activate`,e):this.#a.delete(e);for(let e of this.#n.keys())this.#r.has(e)?this.#u(`deactivate`,e):this.#i.delete(e);this.#e.clear(),this.#n.clear()}getFactories(){let e={},t={};for(let[t,n]of this.#n)e[t]=n;for(let[t,n]of this.#r)e[t]=n;for(let[e,n]of this.#e)t[e]=n;for(let[e,n]of this.#t)t[e]=n;return[e,t]}getFactoriesByOrigin(){let e={},t={},n={},r={};for(let[t,n]of this.#n)e[t]=n;for(let[e,n]of this.#e)t[e]=n;for(let[e,t]of this.#r)n[e]=t;for(let[e,t]of this.#t)r[e]=t;return{definition:[e,t],external:[n,r]}}getFunctions(){return this.#o}canNavigateTo(e,t,n,r){for(let t of e)if(!this.#f(this.#i,t,n,r,`canNavigateTo`))return!1;for(let e of t)if(!this.#f(this.#a,e,n,r,`canNavigateTo`))return!1;return!0}compileGuardFactory(e,t){let n=typeof e==`boolean`?an(e):e,r=this.#s.compileFactory(n);if(typeof r!=`function`)throw TypeError(`[router.${t}] Factory must return a function, got ${typeof r}`);return r}#c(e,t,n,r,i,a){let o=this.#d(e),s=e===`activate`?this.#a:this.#i,c=r?o.definition:o.external,l=r?o.external:o.definition;if(c.has(t)||l.has(t))this.#s.getValidator()?.lifecycle.warnOverwrite(t,e,i);else{let t=this.#s.getValidator();if(t){let n=this.getHandlerCount(e);t.lifecycle.validateHandlerLimit(n,i),t.lifecycle.validateCountThresholds(n+1,i)}}let u=typeof n==`boolean`?an(n):n,d=c.get(t);c.set(t,u);let f=r&&l.has(t);try{let e=a??this.compileGuardFactory(u,i);f||s.set(t,e)}catch(n){throw d===void 0?c.delete(t):c.set(t,d),this.#u(e,t),n}}#l(e,t,n){let{definition:r,external:i}=this.#d(e),a=n!==`external`&&r.delete(t),o=n!==`definition`&&i.delete(t);(a||o)&&this.#u(e,t)}#u(e,t){let n=this.#d(e),r=e===`activate`?this.#a:this.#i,i=n.external.get(t)??n.definition.get(t);if(!i){r.delete(t);return}try{let e=this.#s.compileFactory(i);if(typeof e!=`function`){r.delete(t);return}r.set(t,e)}catch{r.delete(t)}}#d(e){return e===`activate`?{definition:this.#e,external:this.#t}:{definition:this.#n,external:this.#r}}#f(e,t,n,r,i){let a=e.get(t);if(!a)return!0;try{let e=a(n,r);return typeof e==`boolean`?e:(this.#s.getValidator()?.lifecycle.warnAsyncGuardSync(t,i),!1)}catch(e){return this.#s.logger.warn(`router.${i}`,`Guard for "${t}" threw — treated as navigation-blocking (returned false)`,e),!1}}};const sn=new Set([`name`,`path`,`children`,`canActivate`,`canDeactivate`,`forwardTo`,`encodeParams`,`decodeParams`,`defaultParams`]);function cn(){return{decoders:Object.create(null),encoders:Object.create(null),defaultParams:Object.create(null),forwardMap:Object.create(null),forwardFnMap:Object.create(null)}}function ln(e,t){for(let n of Object.keys(t))Object.assign(e[n],t[n])}function un(e,t){for(let n in e)if(e[n]!==t[n])return!1;return!0}function dn(e,t,n){for(let r in e)if(!(r in n)&&e[r]!==t[r])return!1;return!0}function fn(e,t){if(!t||!pn(e,t))return e;let n={};for(let r in e)t[r]!==`query`&&(n[r]=e[r]);return n}function pn(e,t){for(let n in e)if(t[n]===`query`)return!0;return!1}function M(e){let t={name:e.name,path:e.path};return e.children&&(t.children=e.children.map(e=>M(e))),t}function mn(e,t,n=``){for(let r=0;r<e.length;r++){let i=e[r],a=n?`${n}.${i.name}`:i.name;if(a===t)return e.splice(r,1),!0;if(i.children&&t.startsWith(`${a}.`)&&mn(i.children,t,a))return!0}return!1}function hn(e,t){for(let n of Object.keys(e))t(n)&&delete e[n]}function gn(e,t){let n=t.search(/[?#]/),r=n===-1?t:t.slice(0,n);if(r===`/`||r.endsWith(`/`))return t;let i=e.search(/[?#]/),a=i===-1?e:e.slice(0,i);return a.length>1&&a.endsWith(`/`)?`${r}/${n===-1?``:t.slice(n)}`:t}function _n(e,t,n=100){let r=new Set,i=[e],a=e;for(;t[a];){let e=t[a];if(r.has(e)){let t=i.indexOf(e),n=[...i.slice(t),e];throw Error(`Circular forwardTo: ${n.join(` → `)}`)}if(r.add(a),i.push(e),a=e,i.length>n)throw Error(`forwardTo chain exceeds maximum depth (${n}): ${i.join(` → `)}`)}return a}function vn(e,t,n){let r=gt(``,t,e),i=Vt(n);return i.registerTree(r),{tree:r,matcher:i}}function N(e,t=e.definitions){let n=vn(t,e.rootPath,e.matcherOptions);e.tree=n.tree,e.matcher=n.matcher,e.urlParamsCache.clear()}function yn(e,t){N(e,t),e.resolvedForwardMap=P(e.config)}function bn(e){xn(e),N(e,[])}function xn(e){Object.assign(e.config,cn()),e.resolvedForwardMap=Object.create(null),e.routeCustomFields=Object.create(null)}function P(e){let t=Object.create(null);for(let n of Object.keys(e.forwardMap))t[n]=_n(n,e.forwardMap);return t}function Sn(e,t){if(typeof e!=`function`)return;let n=e.constructor.name===`AsyncFunction`,r=e.toString().includes(`__awaiter`);if(n||r)throw TypeError(`forwardTo callback cannot be async for route "${t}". Async functions break matchPath/buildPath.`)}function Cn(e,t,n,r){if(e.canActivate){let n=typeof e.forwardTo==`string`?e.forwardTo:`[dynamic]`;r.warn(`real-router`,`Route "${t}" has both forwardTo and canActivate. canActivate will be ignored because forwardTo creates a redirect (industry standard). Move canActivate to the target route "${n}".`)}if(e.canDeactivate){let n=typeof e.forwardTo==`string`?e.forwardTo:`[dynamic]`;r.warn(`real-router`,`Route "${t}" has both forwardTo and canDeactivate. canDeactivate will be ignored because forwardTo creates a redirect (industry standard). Move canDeactivate to the target route "${n}".`)}Sn(e.forwardTo,t),typeof e.forwardTo==`string`?n.forwardMap[t]=e.forwardTo:n.forwardFnMap[t]=e.forwardTo}function wn(e,t,n,r,i,a,o){let s=Object.fromEntries(Object.entries(e).filter(([e])=>!sn.has(e)));Object.keys(s).length>0&&(r[t]=s),e.canActivate&&i.set(t,e.canActivate),e.canDeactivate&&a.set(t,e.canDeactivate),e.forwardTo&&Cn(e,t,n,o),e.decodeParams&&(n.decoders[t]=t=>e.decodeParams?.(t)??t),e.encodeParams&&(n.encoders[t]=t=>e.encodeParams?.(t)??t),e.defaultParams&&(n.defaultParams[t]=e.defaultParams)}function Tn(e,t,n,r,i,a,o=``){for(let s of e){let e=o?`${o}.${s.name}`:s.name;wn(s,e,t,n,r,i,a),s.children&&Tn(s.children,t,n,r,i,a,e)}}function En(e){let t=cn();return ln(t,e),t}function Dn(e,t,n){if(n.length===0)return[...e,...t];let[r,...i]=n;return e.map(e=>{if(e.name!==r)return e;let n=e.children??[];return{...e,children:i.length===0?[...n,...t]:Dn(n,t,i)}})}function F(e,t,n){for(let r of e){let e=t?`${t}.${r.name}`:r.name;n(e),r.children&&F(r.children,e,n)}}function I(e,t,n){let r=new Set;F(e,t,e=>{if(r.has(e))throw Error(`[router.${n}] Duplicate route "${e}" in batch`);r.add(e)})}function On(e,t){if(e.startsWith(`@@`))throw Error(`[router.${t}] Route name "${e}" uses the reserved "@@" prefix. Routes with this prefix are internal and cannot be modified through the public API.`)}function L(e,t){for(let n of e)On(n.name,t),n.children&&L(n.children,t)}function kn(e,t,n){let r=new Map,i=(e,t)=>{for(let a of e){let e=r.get(t);if(e?.has(a.path))throw Error(`[router.${n}] Path "${a.path}" is already defined`);e?e.add(a.path):r.set(t,new Set([a.path])),a.children&&i(a.children,t?`${t}.${a.name}`:a.name)}};i(e,t)}function An(e,t,n){if(L(t,`addRoute`),n!==void 0&&!e.matcher.hasRoute(n))throw Error(`[router.addRoute] Parent route "${n}" does not exist`);F(t,n??``,t=>{if(e.matcher.hasRoute(t))throw Error(`[router.addRoute] Route "${t}" already exists`)}),I(t,n??``,`addRoute`),kn(t,n??``,`addRoute`)}function jn({definitions:e,routesForHandlers:t,config:n,routeCustomFields:r,handlerParentName:i,rootPath:a,matcherOptions:o,logger:s}){let c=new Map,l=new Map;Tn(t,n,r,c,l,s,i);let u=P(n),{tree:d,matcher:f}=vn(e,a,o);return{config:n,routeCustomFields:r,pendingCanActivate:c,pendingCanDeactivate:l,tree:d,matcher:f,resolvedForwardMap:u}}function Mn(e,t,n,r){return jn({definitions:Dn(e.definitions,t.map(e=>M(e)),n===void 0?[]:n.split(`.`)),routesForHandlers:t,config:En(e.config),routeCustomFields:Object.assign(Object.create(null),e.routeCustomFields),handlerParentName:n??``,rootPath:e.rootPath,matcherOptions:e.matcherOptions,logger:r})}function Nn(e,t,n,r){return jn({definitions:e.map(e=>M(e)),routesForHandlers:e,config:cn(),routeCustomFields:Object.create(null),handlerParentName:``,rootPath:t,matcherOptions:n,logger:r})}function Pn(e,t,n){let r=[];for(let[i,a]of e)r.push([i,a,t(a,n)]);return r}function Fn(e,t){return{activate:Pn(e.pendingCanActivate,t.compileGuard,`canActivate`),deactivate:Pn(e.pendingCanDeactivate,t.compileGuard,`canDeactivate`)}}function In(e,t,n){let r=e.depsStore,{activate:i,deactivate:a}=n??Fn(t,r);Object.assign(e.config,t.config),e.routeCustomFields=t.routeCustomFields,e.tree=t.tree,e.matcher=t.matcher,e.urlParamsCache.clear(),e.resolvedForwardMap=t.resolvedForwardMap;for(let[e,t,n]of i)r.addActivateGuard(e,t,n);for(let[e,t,n]of a)r.addDeactivateGuard(e,t,n)}function Ln(e,t,n,r){let{forwardTo:i,defaultParams:a,decodeParams:o,encodeParams:s,canActivate:c,canDeactivate:l}=r,u=i===void 0?void 0:Rn(n,i,e.config),d=zn(e,n,r),f=c==null?void 0:t.compileGuardFactory(c,`canActivate`),p=l==null?void 0:t.compileGuardFactory(l,`canDeactivate`);return t.preflightHandlerLimit(f===void 0?[]:[n],p===void 0?[]:[n],!1),d!==void 0&&(Object.keys(d).length>0?e.routeCustomFields[n]=d:delete e.routeCustomFields[n]),u!==void 0&&(e.config.forwardMap=u.forwardMap,e.config.forwardFnMap=u.forwardFnMap,e.resolvedForwardMap=u.resolved),Bn(e,n,{defaultParams:a,decodeParams:o,encodeParams:s}),Vn(t,`activate`,n,c,f),Vn(t,`deactivate`,n,l,p),{forwardTo:i,defaultParams:a,decodeParams:o,encodeParams:s}}function Rn(e,t,n){Sn(t,e);let r=Object.assign(Object.create(null),n.forwardMap),i=Object.assign(Object.create(null),n.forwardFnMap);return t===null?(delete r[e],delete i[e]):typeof t==`string`?(delete i[e],r[e]=t):(delete r[e],i[e]=t),{forwardMap:r,forwardFnMap:i,resolved:P({...n,forwardMap:r})}}function zn(e,t,n){let r;for(let i of Object.keys(n)){if(sn.has(i))continue;let a=n[i];a!==void 0&&(r??={...e.routeCustomFields[t]},a===null?delete r[i]:r[i]=a)}return r}function Bn(e,t,n){if(n.defaultParams!==void 0&&(n.defaultParams===null?delete e.config.defaultParams[t]:e.config.defaultParams[t]=n.defaultParams),n.decodeParams!==void 0)if(n.decodeParams===null)delete e.config.decoders[t];else{let r=n.decodeParams;e.config.decoders[t]=e=>r(e)??e}if(n.encodeParams!==void 0)if(n.encodeParams===null)delete e.config.encoders[t];else{let r=n.encodeParams;e.config.encoders[t]=e=>r(e)??e}}function Vn(e,t,n,r,i){r!==void 0&&(t===`activate`?r===null?e.clearCanActivate(n,`definition`):e.addCanActivate(n,r,!0,i):r===null?e.clearCanDeactivate(n,`definition`):e.addCanDeactivate(n,r,!0,i))}function Hn(e,t,n){L(e,`addRoute`),I(e,``,`addRoute`);let r=Nn(e,``,t,n),i={get definitions(){return vt(i.tree)},config:r.config,tree:r.tree,matcher:r.matcher,urlParamsCache:new Map,resolvedForwardMap:r.resolvedForwardMap,routeCustomFields:r.routeCustomFields,rootPath:``,matcherOptions:t,depsStore:void 0,lifecycleNamespace:void 0,pendingCanActivate:r.pendingCanActivate,pendingCanDeactivate:r.pendingCanDeactivate};return i}const R=[];Object.freeze(R);function Un(e){let t=e.split(`.`),n=t.length,r=[t[0]],i=t[0].length;for(let a=1;a<n-1;a++)i+=1+t[a].length,r.push(e.slice(0,i));return r.push(e),r}const Wn=new Set([`string`,`number`,`boolean`]);function Gn(e){return Wn.has(typeof e)}function Kn(e,t,n,r){let i=t[e];if(!i||typeof i!=`object`)return!0;for(let e of Object.keys(i)){let t=n.params[e],i=r.params[e];if(Gn(t)&&Gn(i)&&String(t)!==String(i))return!1}return!0}function qn(e,t,n,r,i,a){for(let o=0;o<a;o++){let a=r[o];if(a!==i[o]||!Kn(a,e,t,n))return o}return a}const Jn=new Map;function z(e){let t=Jn.get(e);if(t)return t;let n=Yn(e);return Object.freeze(n),Jn.set(e,n),n}function Yn(e){if(!e)return[``];let t=e.indexOf(`.`);if(t===-1)return[e];let n=e.indexOf(`.`,t+1);if(n===-1)return[e.slice(0,t),e];let r=e.indexOf(`.`,n+1);return r===-1?[e.slice(0,t),e.slice(0,n),e]:e.indexOf(`.`,r+1)===-1?[e.slice(0,t),e.slice(0,n),e.slice(0,r),e]:Un(e)}let B,V,H=null,Xn,Zn,U=null;function Qn(e,t){if(!t)return{intersection:``,toActivate:z(e.name),toDeactivate:R};let n=A(e),r=A(t);if(!n&&!r)return{intersection:``,toActivate:z(e.name),toDeactivate:z(t.name)};let i=z(e.name),a=z(t.name),o=Math.min(a.length,i.length),s=qn(n??r,e,t,i,a,o),c;if(s>=a.length)c=R;else if(s===0&&a.length===1)c=a;else{c=[];for(let e=a.length-1;e>=s;e--)c.push(a[e])}let l=s===0?i:i.slice(s);return{intersection:s>0?a[s-1]:``,toDeactivate:c,toActivate:l}}function W(e,t){if(H!==null&&e===B&&t===V)return H;if(U!==null&&e===Xn&&t===Zn)return U;let n=Qn(e,t);return Xn=B,Zn=V,U=H,B=e,V=t,H=n,n}function $n(e){let t=[];for(let n of e)for(let e of n.paramMeta.urlParams)t.push(e);return t}function er(e,t){return{name:t??e.segments.at(-1).fullName,params:e.params,meta:e.meta}}var tr=class{#e;#t;#n;get#r(){return this.#e.depsStore}constructor(e,t,n){this.#e=Hn(e,t,n)}static shouldUpdateNode(e){return(t,n)=>{if(!(t&&typeof t==`object`&&`name`in t))throw TypeError(`[router.shouldUpdateNode] toState must be valid State object`);if(t.transition.reload||e===``)return!0;let{intersection:r,toActivate:i,toDeactivate:a}=W(t,n);return e===r||i.includes(e)?!0:a.includes(e)}}setDependencies(e){this.#e.depsStore=e}flushPendingGuards(){let e=this.#r;for(let[t,n]of this.#e.pendingCanActivate)e.addActivateGuard(t,n);this.#e.pendingCanActivate.clear();for(let[t,n]of this.#e.pendingCanDeactivate)e.addDeactivateGuard(t,n);this.#e.pendingCanDeactivate.clear()}setLifecycleNamespace(e){this.#e.lifecycleNamespace=e}setRootPath(e){this.#e.rootPath=e,N(this.#e)}hasRoute(e){return this.#e.matcher.hasRoute(e)}clearRoutes(){bn(this.#e)}buildPath(e,t,n){if(e===d.UNKNOWN_ROUTE)return typeof t?.path==`string`?t.path:``;let r=Object.hasOwn(this.#e.config.defaultParams,e)?{...this.#e.config.defaultParams[e],...t}:t??{},i=typeof this.#e.config.encoders[e]==`function`?this.#e.config.encoders[e]({...r}):r;return this.#e.matcher.buildPath(e,i,this.#a(n))}matchPath(e,t){let n=t,r=this.#e.matcher.match(e);if(!r)return;let{name:i,params:a,meta:o}=er(r),s=typeof this.#e.config.decoders[i]==`function`?this.#e.config.decoders[i](a):a,{name:c,params:l}=this.#r.forwardState(i,s),u=e;if(n.rewritePathOnMatch){let t=typeof this.#e.config.encoders[c]==`function`?this.#e.config.encoders[c]({...l}):l,r=n.trailingSlash;try{u=this.#e.matcher.buildPath(c,t,{trailingSlash:r===`never`||r===`always`?r:void 0,queryParamsMode:n.queryParamsMode}),r===`preserve`&&(u=gn(e,u))}catch{u=e}}return this.#r.makeState(c,l,u,o)}forwardState(e,t){if(Object.hasOwn(this.#e.config.forwardFnMap,e)){let n=this.#i(e,t),r=this.#e.config.forwardFnMap[e],i=this.#o(e,r,t);return{name:i,params:this.#i(i,n)}}let n=this.#e.resolvedForwardMap[e]??e;if(n!==e&&Object.hasOwn(this.#e.config.forwardFnMap,n)){let r=this.#i(e,t),i=this.#e.config.forwardFnMap[n],a=this.#o(n,i,t);return{name:a,params:this.#i(a,r)}}if(n!==e){let r=this.#i(e,t);return{name:n,params:this.#i(n,r)}}return{name:e,params:this.#i(e,t)}}buildStateResolved(e,t){let n=this.#e.matcher.getSegmentsByName(e);if(n)return er({segments:n,params:t,meta:this.#e.matcher.getMetaByName(e)},e)}isActiveRoute(e,t={},n=!1,r=!0){let i=this.#r.getState();if(!i)return!1;let a=i.name;if(a!==e&&!a.startsWith(`${e}.`)&&!e.startsWith(`${a}.`))return!1;let o=this.#e.config.defaultParams[e];if(n||a===e){let n={name:e,params:o?{...o,...t}:t,path:``,transition:ee,context:{}};return this.#r.areStatesEqual(n,i,r)}if(!a.startsWith(`${e}.`))return!1;let s=i.params;return un(t,s)?!o||dn(r?fn(o,this.#e.matcher.getMetaByName(e)?.[e]):o,s,t):!1}getMetaForState(e){return this.#e.matcher.hasRoute(e)?this.#e.matcher.getMetaByName(e):void 0}getUrlParams(e){let t=this.#e.urlParamsCache.get(e);if(t!==void 0)return t;let n=this.#e.matcher.getSegmentsByName(e),r=n?$n(n):[];return this.#e.urlParamsCache.set(e,r),r}getStore(){return this.#e}#i(e,t){return Object.hasOwn(this.#e.config.defaultParams,e)?{...this.#e.config.defaultParams[e],...t}:t}#a(e){if(this.#t)return e!==this.#n&&this.#r.logger.warn(`router.buildPath`,"`options` differs from the cached source reference; router options are immutable per router instance, so the first-cached buildPath options are reused (#957)."),this.#t;this.#n=e;let t=e?.trailingSlash;return this.#t=Object.freeze({trailingSlash:t===`never`||t===`always`?t:void 0,queryParamsMode:e?.queryParamsMode}),this.#t}#o(e,t,n){let r=new Set([e]),i=t(this.#r.getDependency,n),a=0;if(typeof i!=`string`)throw TypeError(`forwardTo callback must return a string, got ${typeof i}`);for(;a<100;){if(this.#e.matcher.getSegmentsByName(i)===void 0)throw Error(`Route "${i}" does not exist`);if(r.has(i)){let e=[...r,i].join(` → `);throw Error(`Circular forwardTo: ${e}`)}if(r.add(i),Object.hasOwn(this.#e.config.forwardFnMap,i)){let e=this.#e.config.forwardFnMap[i];i=e(this.#r.getDependency,n),a++;continue}let e=this.#e.config.forwardMap[i];if(e!==void 0){i=e,a++;continue}return i}throw Error(`forwardTo exceeds maximum depth of 100`)}};const nr=new Set(Object.values(l)),rr=new Set([`code`,`segment`,`path`]),ir=new Set([`setCode`,`setErrorInstance`,`setAdditionalFields`,`hasField`,`getField`,`toJSON`]);var G=class extends Error{segment;path;code;constructor(e,{message:t,segment:n,path:r,...i}={}){super(t??e),this.name=`RouterError`,this.code=e,this.segment=n,this.path=r;for(let[e,t]of Object.entries(i)){if(rr.has(e))throw TypeError(`[RouterError] Cannot set reserved property "${e}"`);ir.has(e)||(this[e]=t)}}setCode(e){this.code=e,nr.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,n]of Object.entries(e)){if(rr.has(t))throw TypeError(`[RouterError.setAdditionalFields] Cannot set reserved property "${t}"`);ir.has(t)||(this[t]=n)}}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}};const ar=new G(l.ROUTER_NOT_STARTED),or=new G(l.ROUTE_NOT_FOUND),sr=new G(l.SAME_STATES),K=Promise.reject(ar),cr=Promise.reject(or),lr=Promise.reject(sr);K.catch(()=>{}),cr.catch(()=>{}),lr.catch(()=>{});function ur(e,t,n,r,i){Object.freeze(n),Object.freeze(r);let a={phase:`activating`,reason:`success`,segments:Object.freeze({deactivated:n,activated:r,intersection:i})};return e?.name!==void 0&&(a.from=e.name),t.reload!==void 0&&(a.reload=t.reload),t.replace!==void 0&&(a.replace=t.replace),t.redirected!==void 0&&(a.redirected=t.redirected),Object.freeze(a)}function dr({signal:e,...t}){return t}function fr(e,t){let{toState:n,fromState:r,opts:i,toDeactivate:a,toActivate:o,intersection:s}=t;if(n.name!==d.UNKNOWN_ROUTE&&!e.hasRoute(n.name)){let t=new G(l.ROUTE_NOT_FOUND,{routeName:n.name});throw e.sendTransitionFail(n,r,t),t}if(r)for(let n of a)!o.includes(n)&&t.canDeactivateFunctions.has(n)&&e.clearCanDeactivate(n);n.transition=ur(r,i,a,o,s);let c=Object.freeze(n);e.setState(c);let u=i.signal===void 0?i:dr(i);return e.sendTransitionDone(c,r,u),c}function pr(e,t,n,r){let i=t;i.code!==l.TRANSITION_CANCELLED&&i.code!==l.ROUTE_NOT_FOUND&&e.sendTransitionFail(n,r,i)}function q(e,t,n){if(e instanceof DOMException&&e.name===`AbortError`)throw new G(l.TRANSITION_CANCELLED);if(e instanceof G&&e.code===l.TRANSITION_CANCELLED)throw e;mr(e,t,n)}function mr(e,t,n){throw e instanceof G?(e.setCode(t),e):new G(t,gr(e,n))}const hr=new Set([`code`,`segment`,`path`,`then`]);function gr(e,t){let n={segment:t};if(e instanceof Error)return{...n,message:e.message,stack:e.stack,...`cause`in e&&e.cause!==void 0&&{cause:e.cause}};if(e&&typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))hr.has(n)||(t[n]=r);return{...n,...t}}return n}async function _r(e,t,n){let r;try{r=await e}catch(e){q(e,t,n);return}if(!r)throw new G(t,{segment:n})}async function vr(e,t,n,r,i,a,o,s,c,u){await _r(c,n,u);for(let c=s;c<t.length;c++){if(!o())throw new G(l.TRANSITION_CANCELLED);let s=t[c],u=e.get(s);if(!u)continue;let d=!1;try{d=u(r,i,a)}catch(e){q(e,n,s)}if(d instanceof Promise){await _r(d,n,s);continue}if(!d)throw new G(n,{segment:s})}}async function yr(e,t,n,r,i,a,o,s,c){if(await e,!s())throw new G(l.TRANSITION_CANCELLED);let u=c();if(u!==void 0&&(await u,!s()))throw new G(l.TRANSITION_CANCELLED);if(r){let e=J(t,n,l.CANNOT_ACTIVATE,i,a,o,s);if(e!==void 0&&await e,!s())throw new G(l.TRANSITION_CANCELLED)}}function br(e,t,n,r,i,a,o,s,c,u,d){if(i){let i=J(e,n,l.CANNOT_DEACTIVATE,o,s,c,u);if(i!==void 0)return yr(i,t,r,a,o,s,c,u,d)}if(!u())throw new G(l.TRANSITION_CANCELLED);let f=d();if(f!==void 0)return xr(f,a?t:void 0,r,o,s,c,u);if(a)return J(t,r,l.CANNOT_ACTIVATE,o,s,c,u)}async function xr(e,t,n,r,i,a,o){if(await e,!o())throw new G(l.TRANSITION_CANCELLED);if(t!==void 0){let e=J(t,n,l.CANNOT_ACTIVATE,r,i,a,o);if(e!==void 0&&await e,!o())throw new G(l.TRANSITION_CANCELLED)}}function J(e,t,n,r,i,a,o){for(let[s,c]of t.entries()){if(!o())throw new G(l.TRANSITION_CANCELLED);let u=e.get(c);if(!u)continue;let d=!1;try{d=u(r,i,a)}catch(e){q(e,n,c)}if(d instanceof Promise)return vr(e,t,n,r,i,a,o,s+1,d,c);if(!d)throw new G(n,{segment:c})}}const Sr=Object.freeze([d.UNKNOWN_ROUTE]),Cr=Object.freeze({replace:!0});function wr(e,t){return t?.name===d.UNKNOWN_ROUTE&&!e.replace?{...e,replace:!0}:e}function Tr(e,t,n){return!!e&&!t.reload&&!t.force&&e.path===n.path}var Er=class{lastSyncResolved=!1;lastSyncRejected=!1;#e;#t=null;#n=0;setDependencies(e){this.#e=e}navigate(e,t,n){this.lastSyncResolved=!1;let r=this.#e;if(!r.canNavigate())return this.lastSyncRejected=!0,K;let i;try{i=r.buildNavigateState(e,t)}catch(e){return Promise.reject(e)}return i?this.#r(i,n):(r.emitTransitionError(void 0,r.getState(),or),this.lastSyncRejected=!0,cr)}navigateToState(e,t){this.lastSyncResolved=!1;let n=this.#e;if(!n.canNavigate())return this.lastSyncRejected=!0,K;if(e.name!==d.UNKNOWN_ROUTE&&!n.hasRoute(e.name)){let t=new G(l.ROUTE_NOT_FOUND,{routeName:e.name});return n.emitTransitionError(void 0,n.getState(),t),Promise.reject(t)}let r={name:e.name,params:e.params,path:e.path,context:{...e.context}},i=A(e);return i!==void 0&&Jt(r,i),this.#r(r,t)}navigateToDefault(e){this.lastSyncResolved=!1;let t=this.#e;if(!t.getOptions().defaultRoute)return Promise.reject(new G(l.ROUTE_NOT_FOUND,{routeName:`defaultRoute not configured`}));let n,r;try{({route:n,params:r}=t.resolveDefault())}catch(e){return Promise.reject(e)}return n?this.navigate(n,r,e):Promise.reject(new G(l.ROUTE_NOT_FOUND,{routeName:`defaultRoute resolved to empty`}))}navigateToNotFound(e){if(!this.#e.isActive())throw new G(l.ROUTER_DISPOSED);this.#c();let t=this.#e.getState(),n=t?z(t.name).toReversed():[];Object.freeze(n);let r={deactivated:n,activated:Sr,intersection:``};Object.freeze(r);let i={phase:`activating`,...t&&{from:t.name},reason:`success`,replace:!0,segments:r};Object.freeze(i);let a={name:d.UNKNOWN_ROUTE,params:h,path:e,transition:i,context:{}};return Object.freeze(a),this.#e.setState(a),this.#e.emitTransitionSuccess(a,t,Cr),a}abortCurrentController(e){this.#t?.abort(e??new G(l.TRANSITION_CANCELLED)),this.#t=null}#r(e,t){let n=this.#e,r,i=!1,a=null;try{if(r=n.getState(),t=wr(t,r),Tr(r,t,e))return n.emitTransitionError(e,r,sr),this.lastSyncRejected=!0,lr;this.#c(t.signal);let o=++this.#n,s=t.signal!==void 0||n.hasLeaveListeners()||n.hasPreCommitListeners();n.startTransition(e,r),i=!0;let[c,u]=n.getLifecycleFunctions(),f=e.name===d.UNKNOWN_ROUTE,p=W(e,r),{toDeactivate:m,toActivate:h,intersection:g}=p,ee=r&&!t.forceDeactivate&&m.length>0,te=!f&&h.length>0,_=c.size>0||u.size>0,v=e;if(!_){let e=this.#o(v,r,o,t,p,c);if(e!==void 0)return e}if(_){a=new AbortController,this.#t=a;let i=()=>this.#n===o&&n.isActive(),s=a.signal,d=br(c,u,m,h,!!ee,te,e,r,s,i,()=>{if(n.sendLeaveApprove(v,r),n.hasLeaveListeners())return n.awaitLeaveListeners(v,r,s)});if(d!==void 0)return this.#i(d,{toState:e,fromState:r,opts:t,toDeactivate:m,toActivate:h,intersection:g,canDeactivateFunctions:c},a,o);if(!i())throw new G(l.TRANSITION_CANCELLED);this.#s(a,!1)}if(s&&(!n.isActive()||t.signal?.aborted===!0))throw new G(l.TRANSITION_CANCELLED);let ne=fr(n,{toState:e,fromState:r,opts:t,toDeactivate:m,toActivate:h,intersection:g,canDeactivateFunctions:c});return this.lastSyncResolved=!0,Promise.resolve(ne)}catch(t){return this.#a(t,a,i,e,r),Promise.reject(t)}}async#i(e,t,n,r){let i=this.#e,a=()=>this.#n===r&&!n.signal.aborted&&i.isActive(),o=t.opts.signal,s,c,u=!1,d,f=new Promise(e=>{if(n.signal.aborted){e();return}c=()=>{e()},n.signal.addEventListener(`abort`,c,{once:!0})});e.catch(()=>{});try{if(o){if(o.aborted)throw new G(l.TRANSITION_CANCELLED,{reason:o.reason});s=()=>{i.cancelNavigation(o.reason)},o.addEventListener(`abort`,s,{once:!0})}if(await Promise.race([e,f]),!a())throw new G(l.TRANSITION_CANCELLED);let n=fr(i,t);return u=!0,n}catch(e){throw d=e,pr(i,e,t.toState,t.fromState),e}finally{s&&o?.removeEventListener(`abort`,s),c&&n.signal.removeEventListener(`abort`,c),this.#s(n,!u,d)}}#a(e,t,n,r,i){t&&this.#s(t,!0,e),n&&r&&pr(this.#e,e,r,i)}#o(e,t,n,r,i,a){let o=this.#e;if(o.sendLeaveApprove(e,t),o.hasLeaveListeners()){let s=new AbortController;this.#t=s;let c;try{c=o.awaitLeaveListeners(e,t,s.signal)}catch(e){throw this.#s(s,!0,e),e}if(c!==void 0)return this.#i(c,{toState:e,fromState:t,opts:r,toDeactivate:i.toDeactivate,toActivate:i.toActivate,intersection:i.intersection,canDeactivateFunctions:a},s,n);this.#s(s,!1);return}}#s(e,t,n){t&&e.abort(n),this.#t===e&&(this.#t=null)}#c(e){if(this.#e.isTransitioning()&&(this.#e.logger.warn(`router.navigate`,`Concurrent navigation detected on shared router instance. For SSR, use cloneRouter() to create isolated instance per request.`),this.#e.cancelNavigation()),e?.aborted)throw new G(l.TRANSITION_CANCELLED,{reason:e.reason})}};const Dr=Object.freeze({replace:!0});var Or=class{#e;setDependencies(e){this.#e=e}async start(e){let t=this.#e;if(t.isIdle())throw new G(l.TRANSITION_CANCELLED);let n=t.getOptions();if(typeof e!=`string`)throw TypeError(`[router.start] path must be a string, got ${typeof e}`);let r=t.matchPath(e);if(!r&&!n.allowNotFound){let n=new G(l.ROUTE_NOT_FOUND,{path:e});throw t.emitTransitionError(void 0,void 0,n),n}return t.completeStart(),r?t.navigateToState(r,Dr):t.navigateToNotFound(e)}stop(){this.#e.clearState()}};function Y(e,t,n){let r=e[t];if(r===void 0)throw Error(`[FSM.${n}] state "${t}" is not declared in config.transitions`);return r}var kr=class{#e;#t;#n=0;#r=null;#i;#a;#o=[];constructor(e){this.#e=e.initial,this.#i=e.context,this.#a=e.transitions,this.#t=Y(e.transitions,e.initial,`constructor`);for(let t of Object.keys(e.transitions)){let n=e.transitions[t];for(let t of Object.keys(n)){let r=n[t];r!==void 0&&Y(e.transitions,r,`constructor`)}}}send(e,...t){let n=this.#t[e];if(n===void 0)return this.#e;let r=this.#e;this.#e=n,this.#t=this.#a[n];let i=t[0];if(this.#r!==null){let t=this.#r.get(r)?.get(e);t!==void 0&&t(i)}if(this.#n>0){let t={from:r,to:n,event:e,payload:i};for(let e of this.#o)e!==null&&e(t)}return this.#e}canSend(e){return this.#t[e]!==void 0}getState(){return this.#e}getContext(){return this.#i}on(e,t,n){Y(this.#a,e,`on`),this.#r??=new Map;let r=this.#r.get(e);r||(r=new Map,this.#r.set(e,r));let i=n;return r.set(t,i),()=>{let n=this.#r?.get(e);n?.get(t)===i&&n.delete(t)}}onTransition(e){let t=this.#o.indexOf(null),n;t===-1?(n=this.#o.length,this.#o.push(e)):(this.#o[t]=e,n=t),this.#n++;let r=!0;return()=>{r&&(r=!1,this.#o[n]=null,this.#n--)}}};const X={IDLE:`IDLE`,STARTING:`STARTING`,READY:`READY`,TRANSITION_STARTED:`TRANSITION_STARTED`,LEAVE_APPROVED:`LEAVE_APPROVED`,DISPOSED:`DISPOSED`},Z={START:`START`,STARTED:`STARTED`,NAVIGATE:`NAVIGATE`,LEAVE_APPROVE:`LEAVE_APPROVE`,COMPLETE:`COMPLETE`,FAIL:`FAIL`,CANCEL:`CANCEL`,STOP:`STOP`,DISPOSE:`DISPOSE`},Ar={initial:X.IDLE,context:null,transitions:{[X.IDLE]:{[Z.START]:X.STARTING,[Z.DISPOSE]:X.DISPOSED},[X.STARTING]:{[Z.STARTED]:X.READY,[Z.FAIL]:X.IDLE,[Z.STOP]:X.IDLE,[Z.DISPOSE]:X.DISPOSED},[X.READY]:{[Z.NAVIGATE]:X.TRANSITION_STARTED,[Z.FAIL]:X.READY,[Z.STOP]:X.IDLE,[Z.DISPOSE]:X.DISPOSED},[X.TRANSITION_STARTED]:{[Z.NAVIGATE]:X.TRANSITION_STARTED,[Z.LEAVE_APPROVE]:X.LEAVE_APPROVED,[Z.CANCEL]:X.READY,[Z.FAIL]:X.READY,[Z.DISPOSE]:X.DISPOSED},[X.LEAVE_APPROVED]:{[Z.NAVIGATE]:X.TRANSITION_STARTED,[Z.COMPLETE]:X.READY,[Z.CANCEL]:X.READY,[Z.FAIL]:X.READY,[Z.DISPOSE]:X.DISPOSED},[X.DISPOSED]:{}}};function jr(){return new kr(Ar)}const Q=`TREE_CHANGED`;function Mr(e){return e instanceof Error?e:Error(String(e))}function Nr(e,t,n){return new Promise((r,i)=>{let a=()=>{let e=n.reason;i(e instanceof G&&e.code===l.TRANSITION_CANCELLED?e:new G(l.TRANSITION_CANCELLED,{reason:e}))};if(n.aborted){a();return}n.addEventListener(`abort`,a,{once:!0}),Promise.allSettled(e).then(e=>{if(n.removeEventListener(`abort`,a),n.aborted)return;if(t!==void 0){i(Mr(t));return}let o=e.find(e=>e.status===`rejected`);if(o!==void 0){i(Mr(o.reason));return}r()})})}var Pr=class{#e;#t;#n;#r;#i=[];#a=0;#o;#s;#c;#l;#u;constructor(e){this.#e=e.routerFSM,this.#t=e.emitter,this.#n=e.abortController,this.#o=void 0,this.#p()}static validateSubscribeListener(e){if(typeof e!=`function`)throw TypeError(`[router.subscribe] Expected a function. For Observable pattern use observable(router) from @real-router/rx`)}static validateSubscribeLeaveListener(e){if(typeof e!=`function`)throw TypeError(`[router.subscribeLeave] Expected a function`)}emitRouterStart(){this.#t.emit(p.ROUTER_START)}emitRouterStop(){this.#t.emit(p.ROUTER_STOP)}emitTransitionStart(e,t){this.#a++;try{this.#t.emit(p.TRANSITION_START,e,t)}finally{this.#a--}}emitTransitionSuccess(e,t,n){this.#a++;try{this.#t.emit(p.TRANSITION_SUCCESS,e,t,n)}finally{this.#a--}}emitTransitionError(e,t,n){this.#a++;try{this.#t.emit(p.TRANSITION_ERROR,e,t,n)}finally{this.#a--}}emitTransitionCancel(e,t){this.#a++;try{this.#t.emit(p.TRANSITION_CANCEL,e,t)}finally{this.#a--}}emitTransitionLeaveApprove(e,t){this.#a++;try{this.#t.emit(p.TRANSITION_LEAVE_APPROVE,e,t)}finally{this.#a--}}isProcessing(){return this.#a>0}emitTreeChanged(e){this.#t.emit(Q,e)}isEmittingTreeChanged(){return this.#t.isDispatching(Q)}subscribeTreeChanged(e){if(this.isDisposed())throw new G(l.ROUTER_DISPOSED);return this.#t.on(Q,t=>{e(t)})}treeChangedListenerCount(){return this.#t.listenerCount(Q)}sendStart(){this.#e.send(Z.START)}sendStop(){this.#e.send(Z.STOP)}sendDispose(){this.#e.send(Z.DISPOSE)}sendStarted(){this.#e.send(Z.STARTED)}sendNavigate(e,t){this.#o=e,this.#e.send(Z.NAVIGATE,{toState:e,fromState:t})}sendComplete(e,t,n={}){this.#e.send(Z.COMPLETE,{toState:e,fromState:t,opts:n}),this.#o=void 0}sendLeaveApprove(e,t){this.#e.send(Z.LEAVE_APPROVE,{toState:e,fromState:t})}sendFail(e,t,n){this.#s=e,this.#c=t,this.#l=n,this.#e.send(Z.FAIL),this.#o=void 0}sendFailSafe(e,t,n){this.isReady()?this.sendFail(e,t,n):this.emitTransitionError(e,t,n)}sendCancel(e,t,n){this.#s=e,this.#c=t,this.#u=n,this.#e.send(Z.CANCEL),this.#o=void 0}canBeginTransition(){return this.#e.canSend(Z.NAVIGATE)}canStart(){return this.#e.canSend(Z.START)}canCancel(){return this.#e.canSend(Z.CANCEL)}isActive(){let e=this.#e.getState();return e!==X.IDLE&&e!==X.DISPOSED}isDisposed(){return this.#e.getState()===X.DISPOSED}isTransitioning(){let e=this.#e.getState();return e===X.TRANSITION_STARTED||e===X.LEAVE_APPROVED}isLeaveApproved(){return this.#e.getState()===X.LEAVE_APPROVED}isReady(){return this.#e.getState()===X.READY}isStarting(){return this.#e.getState()===X.STARTING}isIdle(){return this.#e.getState()===X.IDLE}addEventListener(e,t){return this.#d(e,`addEventListener`),this.#t.on(e,t)}subscribe(e){if(this.isDisposed())throw new G(l.ROUTER_DISPOSED);return this.#d(p.TRANSITION_SUCCESS,`subscribe`),this.#t.on(p.TRANSITION_SUCCESS,(t,n)=>e({route:t,previousRoute:n}))}subscribeLeave(e){if(this.isDisposed())throw new G(l.ROUTER_DISPOSED);this.#i.push(e);let t=!1;return()=>{if(t)return;t=!0;let n=this.#i.indexOf(e);n!==-1&&this.#i.splice(n,1)}}hasLeaveListeners(){return this.#i.length>0}hasPreCommitListeners(){return this.#t.listenerCount(p.TRANSITION_START)>0||this.#t.listenerCount(p.TRANSITION_LEAVE_APPROVE)>0}awaitLeaveListeners(e,t,n){if(t===void 0)return;let r=Object.freeze({route:t,nextRoute:e,signal:n}),i,a,o=[...this.#i];this.#a++;try{for(let e of o)try{let t=e(r);t!==void 0&&typeof t.then==`function`&&(i??=[],i.push(t))}catch(e){a===void 0&&(a=e)}}finally{this.#a--}if(i===void 0){if(a!==void 0)throw Mr(a);return}return Nr(i,a,n)}clearAll(){this.#t.clearAll(),this.#i.length=0}setLimits(e){this.#t.setLimits(e)}setValidatorAccessor(e){this.#r=e}sendCancelIfPossible(e,t){let n=this.#o;!this.canCancel()||n===void 0||this.sendCancel(n,e,t)}#d(e,t){let n=this.#r?.();n&&n.eventBus.validateCountThresholds(this.#t.listenerCount(e)+1,e,t)}#f(){this.emitTransitionError(this.#s,this.#c,this.#l),this.#s=void 0,this.#c=void 0,this.#l=void 0}#p(){let e=this.#e;e.on(X.STARTING,Z.STARTED,()=>{this.emitRouterStart()}),e.on(X.READY,Z.STOP,()=>{this.emitRouterStop()});let t=e=>{this.emitTransitionStart(e.toState,e.fromState)};e.on(X.READY,Z.NAVIGATE,t),e.on(X.TRANSITION_STARTED,Z.NAVIGATE,t),e.on(X.LEAVE_APPROVED,Z.NAVIGATE,t),e.on(X.TRANSITION_STARTED,Z.LEAVE_APPROVE,e=>{this.emitTransitionLeaveApprove(e.toState,e.fromState)}),e.on(X.LEAVE_APPROVED,Z.COMPLETE,e=>{this.emitTransitionSuccess(e.toState,e.fromState,e.opts)});let n=()=>{let e=this.#s,t=this.#u;this.#u=void 0,this.#n(t),e!==void 0&&this.emitTransitionCancel(e,this.#c)};e.on(X.TRANSITION_STARTED,Z.CANCEL,n),e.on(X.LEAVE_APPROVED,Z.CANCEL,n),e.on(X.LEAVE_APPROVED,Z.FAIL,()=>{this.#f()}),e.on(X.STARTING,Z.FAIL,()=>{this.#f()}),e.on(X.READY,Z.FAIL,()=>{this.#f()}),e.on(X.TRANSITION_STARTED,Z.FAIL,()=>{this.#f()})}};const Fr=new G(l.ROUTER_ALREADY_STARTED),Ir={maxListeners:0,warnListeners:0};var Lr=class{#e=new Map;#t=new Set;#n=null;#r=Ir;#i;#a;constructor(e){e?.limits&&(this.#r=e.limits),this.#i=e?.onListenerError??null,this.#a=e?.onListenerWarn??null}static validateCallback(e,t){if(typeof e!=`function`)throw TypeError(`Expected callback to be a function for event ${t}`)}setLimits(e){this.#r=e}on(e,t){let n=this.#e.get(e),r=n?.size??0;if(n?.has(t))throw Error(`Duplicate listener for "${e}"`);let{maxListeners:i,warnListeners:a}=this.#r;if(i!==0&&r>=i)throw Error(`Listener limit (${i}) reached for "${e}"`);a!==0&&r===a&&this.#a!==null&&(this.#n??=new Set,this.#n.has(e)||(this.#a(e,a),this.#n.add(e)));let o=n;return o===void 0&&(o=new Set,this.#e.set(e,o)),o.add(t),()=>{this.off(e,t)}}off(e,t){let n=this.#e.get(e);n&&(n.delete(t),n.size===0&&(this.#e.delete(e),this.#n?.delete(e)))}emit(e,t,n,r,i){let a=this.#e.get(e);if(!a||a.size===0||this.#t.has(e))return;let o=arguments.length-1;this.#t.add(e);try{if(a.size===1){let[s]=a;this.#o(e,s,o,t,n,r,i)}else{let s=[...a];for(let a of s)this.#o(e,a,o,t,n,r,i)}}finally{this.#t.delete(e)}}clearAll(){this.#e.clear(),this.#n=null}listenerCount(e){return this.#e.get(e)?.size??0}isDispatching(e){return this.#t.has(e)}#o(e,t,n,r,i,a,o){try{let s=this.#s(t,n,r,i,a,o);s!=null&&typeof s.then==`function`&&Promise.resolve(s).catch(t=>{this.#i?.(e,t)})}catch(t){this.#i?.(e,t)}}#s(e,t,n,r,i,a){switch(t){case 0:return e();case 1:return e(n);case 2:return e(n,r);case 3:return e(n,r,i);default:return e(n,r,i,a)}}};const Rr=Object.freeze({log:0,warn:1,error:2}),zr=Object.freeze({all:0,"warn-error":1,"error-only":2,none:3});var Br=class{#e={level:`all`,callbackIgnoresLevel:!1};#t=0;#n=!1;constructor(e){e&&this.configure(e)}configure(e){let t=e.level;if(t!==void 0){if(!Object.hasOwn(zr,t))throw Error(`Invalid log level: "${t}". Valid levels are: ${Object.keys(zr).join(`, `)}`);this.#e.level=t,this.#t=zr[t]}Object.hasOwn(e,`callback`)&&(this.#e.callback=e.callback);let n=e.callbackIgnoresLevel;n!==void 0&&(this.#e.callbackIgnoresLevel=n)}getConfig(){return{level:this.#e.level,callback:this.#e.callback,callbackIgnoresLevel:this.#e.callbackIgnoresLevel}}log(e,t,...n){this.#r(`log`,e,t,n)}warn(e,t,...n){this.#r(`warn`,e,t,n)}error(e,t,...n){this.#r(`error`,e,t,n)}#r(e,t,n,r){if(this.#e.level===`none`&&!this.#e.callbackIgnoresLevel)return;let i=Rr[e]<this.#t;i||this.#i(e,t,n,r),this.#a(e,t,n,i,r)}#i(e,t,n,r){if(typeof console<`u`&&typeof console[e]==`function`){let i=t?`[${t}] ${n}`:n;console[e](i,...r)}}#a(e,t,n,r,i){if(!(!this.#e.callback||!this.#e.callbackIgnoresLevel&&r)&&!this.#n){this.#n=!0;try{let r=this.#e.callback(e,t,n,...i);r!=null&&typeof r.then==`function`&&Promise.resolve(r).catch(e=>{this.#o(`[Logger] Error in async callback:`,e)})}catch(e){this.#o(`[Logger] Error in callback:`,e)}finally{this.#n=!1}}}#o(e,t){typeof console<`u`&&typeof console.error==`function`&&console.error(e,t)}};function Vr(e){let t=Hr(e),n=()=>c(e.router).validator;Ur(e),Wr(e,n),Gr(e,t,n),Kr(e),qr(e,t,n),Jr(e),Yr(e),Xr(e)}function Hr(e){let{router:t,dependenciesStore:n}=e,r=e=>n.dependencies[e];return e=>e(t,r)}function Ur(e){e.dependenciesStore.limits=e.limits,e.eventBus.setLimits({maxListeners:e.limits.maxListeners,warnListeners:e.limits.warnListeners})}function Wr(e,t){e.eventBus.setValidatorAccessor(t)}function Gr(e,t,n){let r={logger:c(e.router).logger,compileFactory:t,getValidator:n};e.routeLifecycle.setDependencies(r)}function Kr(e){let t={logger:c(e.router).logger,addActivateGuard:(t,n,r)=>{e.routeLifecycle.addCanActivate(t,n,!0,r)},addDeactivateGuard:(t,n,r)=>{e.routeLifecycle.addCanDeactivate(t,n,!0,r)},compileGuard:(t,n)=>e.routeLifecycle.compileGuardFactory(t,n),makeState:(t,n,r,i)=>e.state.makeState(t,n,r,i),getState:()=>e.state.get(),areStatesEqual:(t,n,r)=>e.state.areStatesEqual(t,n,r),getDependency:t=>e.dependenciesStore.dependencies[t],forwardState:(t,n)=>{let r=c(e.router);return r.validator?.routes.validateStateBuilderArgs(t,n,`forwardState`),r.forwardState(t,n)}};e.routes.setDependencies(t),e.routes.setLifecycleNamespace(e.routeLifecycle)}function qr(e,t,n){let r={logger:c(e.router).logger,addEventListener:(t,n)=>e.eventBus.addEventListener(t,n),canNavigate:()=>e.eventBus.canBeginTransition(),compileFactory:t,getValidator:n};e.plugins.setDependencies(r)}function Jr(e){let t={logger:c(e.router).logger,getOptions:()=>e.options.get(),hasRoute:t=>e.routes.hasRoute(t),getState:()=>e.state.get(),setState:t=>{e.state.set(t)},buildNavigateState:(t,n)=>{let r=c(e.router);r.validator?.routes.validateStateBuilderArgs(t,n,`navigate`);let i=r.forwardState(t,n),a=i.name,o=y(i.params),s=e.routes.getMetaForState(a);if(s===void 0)return;let l=r.buildPath(a,o);return e.state.makeState(a,o,l,s,!0)},resolveDefault:()=>{let t=e.options.get(),n=c(e.router),r=Wt(t.defaultRoute,t=>e.dependenciesStore.dependencies[t]),i=Wt(t.defaultParams,t=>e.dependenciesStore.dependencies[t]);return typeof t.defaultRoute==`function`&&n.validator?.options.validateResolvedDefaultRoute(r,n.routeGetStore()),{route:r,params:i}},startTransition:(t,n)=>{e.eventBus.sendNavigate(t,n)},cancelNavigation:t=>{e.eventBus.sendCancelIfPossible(e.state.get(),t)},sendTransitionDone:(t,n,r)=>{e.eventBus.sendComplete(t,n,r)},sendTransitionFail:(t,n,r)=>{e.eventBus.sendFail(t,n,r)},emitTransitionError:(t,n,r)=>{e.eventBus.sendFailSafe(t,n,r)},emitTransitionSuccess:(t,n,r)=>{e.eventBus.emitTransitionSuccess(t,n,r)},sendLeaveApprove:(t,n)=>{e.eventBus.sendLeaveApprove(t,n)},canNavigate:()=>e.eventBus.canBeginTransition(),getLifecycleFunctions:()=>e.routeLifecycle.getFunctions(),isActive:()=>e.router.isActive(),isTransitioning:()=>e.eventBus.isTransitioning(),clearCanDeactivate:t=>{e.routeLifecycle.clearCanDeactivate(t,`external`)},hasLeaveListeners:()=>e.eventBus.hasLeaveListeners(),hasPreCommitListeners:()=>e.eventBus.hasPreCommitListeners(),awaitLeaveListeners:(t,n,r)=>e.eventBus.awaitLeaveListeners(t,n,r)};e.navigation.setDependencies(t)}function Yr(e){e.lifecycle.setDependencies({getOptions:()=>e.options.get(),navigateToState:(t,n)=>e.navigation.navigateToState(t,n),navigateToNotFound:t=>e.navigation.navigateToNotFound(t),clearState:()=>{e.state.set(void 0)},matchPath:t=>e.routes.matchPath(t,e.options.get()),completeStart:()=>{e.eventBus.sendStarted()},isIdle:()=>e.eventBus.isIdle(),emitTransitionError:(t,n,r)=>{e.eventBus.sendFail(t,n,r)}})}function Xr(e){e.state.setDependencies({getDefaultParams:()=>e.routes.getStore().config.defaultParams,buildPath:(t,n)=>c(e.router).buildPath(t,n),getUrlParams:t=>e.routes.getUrlParams(t)})}const Zr=Object.freeze({}),Qr=new Set([l.SAME_STATES,l.TRANSITION_CANCELLED,l.ROUTER_NOT_STARTED,l.ROUTE_NOT_FOUND,l.CANNOT_ACTIVATE,l.CANNOT_DEACTIVATE]);var $r=class r{#e;#t;#n;#r;#i;#a;#o;#s;#c;#l;#u;#d;constructor(i=[],a={},o={}){let{logger:s,...c}=a;s&&ie(s);let l=new Br(s);this.#u=e=>{r.#f(e)||l.error(`router.navigate`,`Unexpected navigation error`,e)},this.#d=e=>{r.#f(e)||l.error(`router.start`,`Unexpected start error`,e)},Kt.validateOptionsIsObject(a),te(o),i.length>0&&_(i),this.#e=new Kt(c),this.#t=oe(c.limits),this.#n=se(o),this.#r=new Yt,this.#i=new tr(i,ei(this.#e.get()),l),this.#a=new on,this.#o=new $t,this.#s=new Er,this.#c=new Or;let u=jr(),d=new Lr({onListenerError:(e,t)=>{l.error(`Router`,`Error in listener for ${e}:`,t)},onListenerWarn:(e,t)=>{l.warn(`router.addEventListener`,`Event "${e}" has ${t} listeners — possible memory leak`)}});this.#l=new Pr({routerFSM:u,emitter:d,abortController:e=>{this.#s.abortCurrentController(e)}});let f=new Map;t(this,{logger:l,makeState:(e,t,n,r)=>this.#r.makeState(e,t,n,r),forwardState:e(`forwardState`,(e,t)=>this.#i.forwardState(e,t),f),buildStateResolved:(e,t)=>this.#i.buildStateResolved(e,t),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:e(`buildPath`,(e,t)=>this.#i.buildPath(e,t??h,this.#e.get()),f),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:n(`start`,e=>this.#c.start(e),f),navigateToState:(e,t)=>{this.#m();let n=this.#s.navigateToState(e,t??Zr);return this.#s.lastSyncResolved?this.#s.lastSyncResolved=!1:this.#s.lastSyncRejected?this.#s.lastSyncRejected=!1:this.#p(n),n},interceptors:f,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:l.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}),Vr({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){return c(this).validator?.routes.validateIsActiveRouteArgs(e,t,n,r),c(this).validator?.routes.validateRouteName(e,`isActiveRoute`),e===``?(c(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)}buildPath(e,t){let n=c(this);return n.validator?.routes.validateBuildPathArgs(e),n.validator?.navigation.validateParams(t,`buildPath`),n.buildPath(e,y(t))}getState(){return this.#r.get()}getPreviousState(){return this.#r.getPrevious()}areStatesEqual(e,t,n=!0){return c(this).validator?.state.validateAreStatesEqualArgs(e,t,n),this.#r.areStatesEqual(e,t,n)}shouldUpdateNode(e){return c(this).validator?.routes.validateShouldUpdateNodeArgs(e),tr.shouldUpdateNode(e)}isActive(){return this.#l.isActive()}start(e){if(!this.#l.canStart())return Promise.reject(Fr);c(this).validator?.navigation.validateStartArgs(e),this.#l.sendStart();let t;try{let n=c(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)}let n=t.catch(e=>this.#h(e));return this.#p(n,this.#d),n}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=c(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.#g()}canNavigateTo(e,t){let n=c(this);if(n.validator?.routes.validateRouteName(e,`canNavigateTo`),n.validator?.navigation.validateParams(t,`canNavigateTo`),!this.#i.hasRoute(e))return!1;let{name:r,params:i}=n.forwardState(e,t??{}),a;try{let e=y(i),t=this.#i.getMetaForState(r),o=n.buildPath(r,e);a=this.#r.makeState(r,e,o,t,!0)}catch{return!1}let o=this.#r.get(),{toDeactivate:s,toActivate:l}=W(a,o);return this.#a.canNavigateTo(s,l,a,o)}usePlugin(...e){if(this.#l.isDisposed())throw new G(l.ROUTER_DISPOSED);let t=e.filter(Boolean);if(t.length===0)return()=>{};let n=c(this);n.validator?.plugins.validatePluginLimit(this.#o.count(),this.#t);for(let e of t)n.validator?.plugins.validateNoDuplicatePlugins(e,this.#o.getAll());return this.#o.use(...t)}subscribe(e){return Pr.validateSubscribeListener(e),this.#l.subscribe(e)}subscribeLeave(e){return Pr.validateSubscribeLeaveListener(e),this.#l.subscribeLeave(e)}isLeaveApproved(){return this.#l.isLeaveApproved()}navigate(e,t,n){this.#m();let r=c(this);r.validator?.navigation.validateNavigateArgs(e),r.validator?.navigation.validateParams(t,`navigate`);let i=n??Zr;r.validator?.navigation.validateNavigationOptions(i,`navigate`);let a=this.#s.navigate(e,t??h,i);return this.#s.lastSyncResolved?this.#s.lastSyncResolved=!1:this.#s.lastSyncRejected?this.#s.lastSyncRejected=!1:this.#p(a),a}navigateToDefault(e){this.#m();let t=c(this);t.validator?.navigation.validateNavigateToDefaultArgs(e);let n=e??Zr;t.validator?.navigation.validateNavigationOptions(n,`navigateToDefault`);let r=this.#s.navigateToDefault(n);return this.#s.lastSyncResolved?this.#s.lastSyncResolved=!1:this.#s.lastSyncRejected?this.#s.lastSyncRejected=!1:this.#p(r),r}navigateToNotFound(e){if(this.#m(),!this.#l.isActive())throw new G(l.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 t=this.#r.get();if(t===void 0)throw new G(l.ROUTER_NOT_STARTED,{message:`[router.navigateToNotFound] cannot derive the path before the start navigation commits — pass an explicit path`});return this.#s.navigateToNotFound(t.path)}static#f(e){return e instanceof G&&Qr.has(e.code)}#p(e,t=this.#u){e.catch(t)}#m(){if(this.#l.isProcessing())throw new G(l.REENTRANT_NAVIGATION)}#h(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}#g(){this.navigate=$,this.navigateToDefault=$,this.navigateToNotFound=$,this.start=$,this.stop=$,this.usePlugin=$,this.subscribe=$,this.subscribeLeave=$,this.canNavigateTo=$}};function $(){throw new G(l.ROUTER_DISPOSED)}function ei(e){return{strictTrailingSlash:e.trailingSlash===`strict`,caseSensitive:e.caseSensitive,strictQueryParams:e.queryParamsMode===`strict`,urlParamsEncoding:e.urlParamsEncoding,queryParams:e.queryParams}}export{u as C,p as E,_ as S,l as T,ln as _,An as a,_t as b,L as c,Nn as d,Ln as f,_n as g,bn as h,In as i,On as l,Fn as m,G as n,I as o,yn as p,W as r,kn as s,$r as t,Mn as u,hn as v,d as w,vt as x,mn as y};
|
|
2
|
+
//# sourceMappingURL=Router-BIui2eew.mjs.map
|