{"version":3,"file":"index.mjs","names":[],"sources":["../../src/components/modern/RouteView/components.tsx","../../src/components/modern/RouteView/helpers.tsx","../../src/components/modern/RouteView/RouteView.tsx","../../src/hooks/useRouteExit.tsx","../../src/hooks/useRouteEnter.tsx"],"sourcesContent":["import type { MatchProps, NotFoundProps, SelfProps } from \"./types\";\n\nexport function Match(_props: MatchProps): null {\n  return null;\n}\n\nMatch.displayName = \"RouteView.Match\";\n\nexport function Self(_props: SelfProps): null {\n  return null;\n}\n\nSelf.displayName = \"RouteView.Self\";\n\nexport function NotFound(_props: NotFoundProps): null {\n  return null;\n}\n\nNotFound.displayName = \"RouteView.NotFound\";\n","import { UNKNOWN_ROUTE } from \"@real-router/core\";\nimport { startsWithSegment } from \"@real-router/route-utils\";\nimport { Activity, Children, Fragment, Suspense, isValidElement } from \"react\";\n\nimport { Match, NotFound, Self } from \"./components\";\n\nimport type { MatchProps, NotFoundProps, SelfProps } from \"./types\";\nimport type { ReactElement, ReactNode } from \"react\";\n\nconst MARKER_TYPES: ReadonlySet<unknown> = new Set([Match, Self, NotFound]);\n\ninterface FallbackSlots {\n  selfChildren: ReactNode;\n  selfFallback: ReactNode | undefined;\n  selfFound: boolean;\n  notFoundChildren: ReactNode;\n  notFoundFound: boolean;\n}\n\n// Fixed keys used by appendFallback to distinguish the Self / NotFound\n// render slots from user-supplied <Match> children. Match render slots key\n// off `fullSegmentName` instead — these two are the only synthetic keys.\nconst SELF_KEY = \"__route-view-self__\";\nconst NOT_FOUND_KEY = \"__route-view-not-found__\";\n\nfunction isSegmentMatch(\n  routeName: string,\n  fullSegmentName: string,\n  exact: boolean,\n): boolean {\n  if (fullSegmentName === \"\") {\n    return false;\n  }\n\n  if (exact) {\n    return routeName === fullSegmentName;\n  }\n\n  return startsWithSegment(routeName, fullSegmentName);\n}\n\nexport function collectElements(\n  children: ReactNode,\n  result: ReactElement[],\n): void {\n  // Recurses into Fragment-like wrappers (anything that isn't Match / Self /\n  // NotFound) to flatten the slot tree. No explicit depth guard: typical\n  // RouteView shape is `<RouteView><Match/>...<NotFound/></RouteView>` —\n  // depth ≤ 3 in real apps. A pathological hand-written tree of N Fragments\n  // recurses N times; the call stack, not this function, is the bound.\n  //\n  // `Children.forEach` iterates without `Children.toArray`'s array allocation\n  // and per-child clone-with-synthetic-key step. We don't read child.key here\n  // (Match/Self/NotFound carry their own segment-derived keys further down),\n  // so the cheaper iterator is functionally equivalent.\n  // eslint-disable-next-line @eslint-react/no-children-for-each -- intentional: collectElements is a render-hot pipeline; toArray's array+key clone is wasteful here\n  Children.forEach(children, (child) => {\n    if (!isValidElement(child)) {\n      return;\n    }\n\n    if (MARKER_TYPES.has(child.type)) {\n      result.push(child);\n    } else {\n      collectElements(\n        (child.props as { readonly children: ReactNode }).children,\n        result,\n      );\n    }\n  });\n}\n\nfunction renderSlotElement(\n  slotChildren: ReactNode,\n  key: string,\n  keepAlive: boolean,\n  mode: \"visible\" | \"hidden\",\n  fallback?: ReactNode,\n): ReactElement {\n  const content =\n    fallback === undefined ? (\n      slotChildren\n    ) : (\n      <Suspense fallback={fallback}>{slotChildren}</Suspense>\n    );\n\n  if (keepAlive) {\n    return (\n      <Activity mode={mode} key={key}>\n        {content}\n      </Activity>\n    );\n  }\n\n  return <Fragment key={key}>{content}</Fragment>;\n}\n\nfunction recordFallback(child: ReactElement, slots: FallbackSlots): boolean {\n  if (child.type === NotFound) {\n    // First-wins: subsequent <NotFound> elements are ignored, symmetric with\n    // <Self> below (#1220). Before this guard the assignment was unconditional\n    // (last-wins), contradicting the documented Self-symmetric contract.\n    if (!slots.notFoundFound) {\n      slots.notFoundChildren = (child.props as NotFoundProps).children;\n      slots.notFoundFound = true;\n    }\n\n    return true;\n  }\n\n  if (child.type === Self) {\n    // First-wins: subsequent <Self> elements are ignored, mirroring NotFound.\n    if (!slots.selfFound) {\n      slots.selfChildren = (child.props as SelfProps).children;\n      slots.selfFallback = (child.props as SelfProps).fallback;\n      slots.selfFound = true;\n    }\n\n    return true;\n  }\n\n  return false;\n}\n\nfunction processMatch(\n  child: ReactElement,\n  routeName: string,\n  nodeName: string,\n  hasBeenActivated: ReadonlySet<string>,\n  alreadyActive: boolean,\n): { rendered: ReactElement | null; activatedName: string | null } {\n  const matchProps = child.props as MatchProps;\n  const { segment, exact = false, keepAlive = false, fallback } = matchProps;\n  const fullSegmentName = nodeName ? `${nodeName}.${segment}` : segment;\n  const isActive =\n    !alreadyActive && isSegmentMatch(routeName, fullSegmentName, exact);\n\n  if (isActive) {\n    // The keepAlive Set is NOT mutated here — RouteView commits the activation\n    // in a post-render effect (#1251). Mutating during render coupled the pure\n    // winner computation to a side effect (blocking memoization) and, under\n    // concurrent rendering, a discarded render would leave a phantom entry that\n    // later renders an un-committed match as a hidden keepAlive subtree.\n    return {\n      rendered: renderSlotElement(\n        matchProps.children,\n        fullSegmentName,\n        keepAlive,\n        \"visible\",\n        fallback,\n      ),\n      activatedName: fullSegmentName,\n    };\n  }\n\n  if (keepAlive && hasBeenActivated.has(fullSegmentName)) {\n    return {\n      rendered: renderSlotElement(\n        matchProps.children,\n        fullSegmentName,\n        keepAlive,\n        \"hidden\",\n        fallback,\n      ),\n      activatedName: null,\n    };\n  }\n\n  return { rendered: null, activatedName: null };\n}\n\nfunction appendFallback(\n  rendered: ReactElement[],\n  routeName: string,\n  nodeName: string,\n  slots: FallbackSlots,\n): void {\n  if (slots.selfFound && routeName === nodeName) {\n    rendered.push(\n      renderSlotElement(\n        slots.selfChildren,\n        SELF_KEY,\n        false,\n        \"visible\",\n        slots.selfFallback,\n      ),\n    );\n\n    return;\n  }\n\n  if (routeName === UNKNOWN_ROUTE && slots.notFoundChildren !== null) {\n    rendered.push(\n      <Fragment key={NOT_FOUND_KEY}>{slots.notFoundChildren}</Fragment>,\n    );\n  }\n}\n\nexport function buildRenderList(\n  elements: ReactElement[],\n  routeName: string,\n  nodeName: string,\n  hasBeenActivated: ReadonlySet<string>,\n): {\n  rendered: ReactElement[];\n  activeMatchFound: boolean;\n  activatedName: string | null;\n} {\n  const slots: FallbackSlots = {\n    selfChildren: null,\n    selfFallback: undefined,\n    selfFound: false,\n    notFoundChildren: null,\n    notFoundFound: false,\n  };\n  // The segment that activated this render, or null. Reported to the caller so\n  // RouteView can commit it to the keepAlive Set post-render (#1251) — this pure\n  // walk no longer mutates the Set. At most one match activates (first-wins via\n  // the `alreadyActive` short-circuit), so a single name suffices.\n  let activatedName: string | null = null;\n  const rendered: ReactElement[] = [];\n\n  for (const child of elements) {\n    if (recordFallback(child, slots)) {\n      continue;\n    }\n\n    const result = processMatch(\n      child,\n      routeName,\n      nodeName,\n      hasBeenActivated,\n      activatedName !== null,\n    );\n\n    if (result.activatedName !== null) {\n      activatedName = result.activatedName;\n    }\n\n    if (result.rendered !== null) {\n      rendered.push(result.rendered);\n    }\n  }\n\n  if (activatedName === null) {\n    appendFallback(rendered, routeName, nodeName, slots);\n  }\n\n  return { rendered, activeMatchFound: activatedName !== null, activatedName };\n}\n","import { useEffect, useMemo, useRef } from \"react\";\n\nimport { Match, NotFound, Self } from \"./components\";\nimport { buildRenderList, collectElements } from \"./helpers\";\nimport { useRouteNode } from \"../../../hooks/useRouteNode\";\n\nimport type { RouteViewProps } from \"./types\";\nimport type { ReactElement } from \"react\";\n\nfunction RouteViewRoot({\n  nodeName,\n  children,\n}: Readonly<RouteViewProps>): ReactElement | null {\n  const { route } = useRouteNode(nodeName);\n  const hasBeenActivatedRef = useRef<Set<string> | null>(null);\n\n  // eslint-disable-next-line @eslint-react/refs -- lazy init: assign once when null to avoid `new Set()` allocation on every render\n  hasBeenActivatedRef.current ??= new Set();\n  // eslint-disable-next-line @eslint-react/refs -- stable render cache; the ref is never reassigned after lazy init\n  const hasBeenActivated = hasBeenActivatedRef.current;\n\n  // Skip the Children.forEach + collectElements traversal when the children\n  // reference is unchanged. The common SPA case is a stable JSX tree across\n  // re-renders, so the cache hits on every render except the first.\n  //\n  // Streaming SSR caveat: with `renderToReadableStream`, React may invoke\n  // RouteView multiple times across chunks with a fresh `children` reference\n  // each time. The useMemo misses on each new render and the traversal runs\n  // again — this is the expected SSR cost; the alternative would be build-\n  // time codegen of the static route tree, which is out of scope here.\n  const elements = useMemo(() => {\n    const collected: ReactElement[] = [];\n\n    collectElements(children, collected);\n\n    return collected;\n  }, [children]);\n\n  const routeName = route?.name ?? null;\n\n  // Memoize the render walk by its pure inputs. Previously `buildRenderList` ran\n  // on EVERY render because `processMatch` mutated the keepAlive Set inline —\n  // coupling the pure winner/`rendered` computation to a side effect, so a\n  // parent re-render with no route change re-walked every Match and re-diffed\n  // the identical output (preact, whose `buildRenderList` is a 3-arg pure\n  // function, already memoizes here). The Set mutation now lives in the effect\n  // below, so the walk memoizes on `[elements, routeName, nodeName]` too. #1251.\n  const { rendered, activatedName } = useMemo(() => {\n    if (routeName === null) {\n      return { rendered: [] as ReactElement[], activatedName: null };\n    }\n\n    const result = buildRenderList(\n      elements,\n      routeName,\n      nodeName,\n      hasBeenActivated,\n    );\n\n    return { rendered: result.rendered, activatedName: result.activatedName };\n  }, [elements, routeName, nodeName, hasBeenActivated]);\n\n  // Commit the keepAlive activation AFTER render, not during it. A render that\n  // React discards under concurrent rendering must not record an activation\n  // that never committed — otherwise a later render would show that\n  // never-mounted match as a hidden keepAlive subtree. Adding the same name\n  // twice is a no-op, so the effect is safe to re-run.\n  // `hasBeenActivated` is a ref-held Set (L15-20), mutated post-commit in this\n  // effect — the concurrent-safe pattern #1251 established. @eslint-react's\n  // experimental `immutability` rule (detection widened in v5.17) flags the ref\n  // mutation and suggests state, which would defeat the ref's whole purpose\n  // (re-render-free activation tracking). Intentional exception, scoped here.\n  /* eslint-disable @eslint-react/immutability -- intentional post-commit ref-Set mutation, see #1251 */\n  useEffect(() => {\n    if (activatedName !== null) {\n      hasBeenActivated.add(activatedName);\n    }\n  }, [activatedName, hasBeenActivated]);\n  /* eslint-enable @eslint-react/immutability */\n\n  if (rendered.length > 0) {\n    return <>{rendered}</>;\n  }\n\n  return null;\n}\n\nRouteViewRoot.displayName = \"RouteView\";\n\nexport const RouteView = Object.assign(RouteViewRoot, {\n  Match,\n  Self,\n  NotFound,\n});\n\nexport type {\n  RouteViewProps,\n  MatchProps as RouteViewMatchProps,\n  SelfProps as RouteViewSelfProps,\n  NotFoundProps as RouteViewNotFoundProps,\n} from \"./types\";\n","import { guardLeaveListener } from \"@real-router/sources\";\nimport { useEffect, useLayoutEffect, useRef } from \"react\";\n\nimport { useRouter } from \"./useRouter\";\n\nimport type { State } from \"@real-router/core\";\n\nexport interface RouteExitContext {\n  /** The route being left. */\n  route: State;\n  /** The route being navigated to. */\n  nextRoute: State;\n  /**\n   * AbortSignal that fires when this navigation is superseded by a later\n   * one (rapid clicks). Already filtered: when the handler runs,\n   * `signal.aborted` is guaranteed to be `false`. Use\n   * `signal.addEventListener(\"abort\", cleanup, { once: true })` for\n   * cleanup that must run on cancellation.\n   */\n  signal: AbortSignal;\n}\n\nexport interface UseRouteExitOptions {\n  /**\n   * Skip the handler when `route.name === nextRoute.name`\n   * (sort/filter/query-only navigations on the same route). Default:\n   * `true`.\n   */\n  skipSameRoute?: boolean;\n}\n\nexport type RouteExitHandler = (\n  context: RouteExitContext,\n) => void | Promise<void>;\n\n/**\n * Subscribe to the router's leave-window with the universal guards baked\n * in. Wraps `router.subscribeLeave` so consumers don't repeat the same\n * boilerplate every time:\n *\n *   - **Reentrant abort pre-check**: if `signal.aborted` is already `true`\n *     when the handler would run (rapid navigation superseded a slower\n *     one), the handler is skipped entirely. `signal.addEventListener(\n *     \"abort\", ...)` does not fire retroactively, so without this guard\n *     downstream cleanup would never trigger.\n *   - **Same-route skip**: by default, `route.name === nextRoute.name`\n *     short-circuits the handler — query-only navigations (sort, filter,\n *     pagination) skip the work. Opt out with `skipSameRoute: false`.\n *   - **Stable handler reference**: the handler can change identity on\n *     every render without causing resubscription — internal ref keeps\n *     the latest handler accessible to the long-lived subscription.\n *\n * Returns nothing — the subscription's lifecycle is bound to the\n * component's mount.\n *\n * If the handler returns a Promise, the router blocks on it. If the\n * Promise resolves, navigation proceeds. If it **rejects**, the router\n * rejects `navigate()` with the handler's **original error** and emits\n * `TRANSITION_ERROR` — it is NOT re-coded to `TRANSITION_CANCELLED`\n * (that arises only when the navigation's `signal` aborts: a superseding\n * navigation, `stop()`, `dispose()`, or an external `opts.signal`).\n *\n * **Reentrancy — no synchronous `navigate()` from the handler.** The handler\n * runs inside the transition's leave-dispatch window, so calling\n * `router.navigate(...)` (or `navigateToDefault` / `navigateToState` /\n * `navigateToNotFound`) **synchronously** in the handler body throws\n * `REENTRANT_NAVIGATION` — core bans reentrant navigation from a transition\n * listener (RFC navigation-cancellation-unification §4). To redirect on exit,\n * defer past the sync dispatch: `await` your exit work first, or\n * `queueMicrotask(() => router.navigate(...))`. A navigate issued after the\n * handler's first `await` runs once the transition settles and is allowed.\n * (Guards — `canDeactivate` — are the intended place to *block* or gate a\n * departure; `useRouteExit` is for side effects, not redirection.)\n *\n * @example Animation\n * ```tsx\n * const ref = useRef<HTMLDivElement>(null);\n *\n * useRouteExit(async ({ signal }) => {\n *   const el = ref.current;\n *   if (!el) return;\n *   el.classList.add(\"fade-out\");\n *   const cleanup = () => el.classList.remove(\"fade-out\");\n *   signal.addEventListener(\"abort\", cleanup, { once: true });\n *   try {\n *     el.getBoundingClientRect();   // style flush\n *     await Promise.allSettled(el.getAnimations().map((a) => a.finished));\n *   } finally {\n *     cleanup();\n *   }\n * });\n * ```\n *\n * @example Auto-save form draft\n * ```tsx\n * useRouteExit(async ({ signal }) => {\n *   if (formState.dirty) await api.saveDraft(formState, { signal });\n * });\n * ```\n *\n * @example Cancel inflight requests\n * ```tsx\n * useRouteExit(() => {\n *   inflightController.abort();\n * });\n * ```\n *\n * @example Library-coordinated exit (motion / framer-motion)\n * ```tsx\n * const exitResolverRef = useRef<(() => void) | null>(null);\n *\n * useRouteExit(({ signal }) => {\n *   return new Promise<void>((resolve) => {\n *     exitResolverRef.current = resolve;\n *     signal.addEventListener(\"abort\", () => resolve(), { once: true });\n *   });\n * });\n *\n * const onExitComplete = () => exitResolverRef.current?.();\n * // pass onExitComplete to <AnimatePresence>\n * ```\n *\n * @example Detecting that you are leaving a subtree\n * ```tsx\n * const inProducts = (name: string) =>\n *   name === \"products\" || name.startsWith(\"products.\");\n *\n * useRouteExit(({ route, nextRoute }) => {\n *   if (inProducts(route.name) && !inProducts(nextRoute.name)) {\n *     // leaving the products subtree entirely — flush product-related caches\n *     productCache.clear();\n *   }\n * });\n * ```\n *\n * ⚠ Do NOT read `nextRoute.transition` here. `nextRoute` is the PENDING target,\n * and the pipeline gives it the neutral default — empty `segments`, optional\n * flags `undefined` — so the example this replaced read `[]` and `undefined`\n * whatever the navigation was. (It threw outright until real-router#1976\n * attached the field.) The real metadata is written at the COMMIT: read it in\n * `router.subscribe`, `onTransitionSuccess`, or `getState()`.\n */\nexport function useRouteExit(\n  handler: RouteExitHandler,\n  options?: UseRouteExitOptions,\n): void {\n  const router = useRouter();\n  const handlerRef = useRef(handler);\n  const skipSameRoute = options?.skipSameRoute ?? true;\n\n  // Keep the latest handler accessible to the subscription without\n  // resubscribing on every render — the subscription registers the\n  // wrapper once and dispatches to whatever ref points to.\n  // useLayoutEffect (synchronous, post-render, pre-paint) updates the\n  // ref before the browser can dispatch any router events that could\n  // observe a stale closure.\n  useLayoutEffect(() => {\n    handlerRef.current = handler;\n  });\n\n  useEffect(() => {\n    // The same-route + reentrant-abort guards and the Promise passthrough\n    // live in the shared listener (@real-router/sources, #1435). The ref-thunk\n    // keeps the latest handler reachable from the long-lived subscription\n    // without resubscribing on every render.\n    return router.subscribeLeave(\n      guardLeaveListener((context) => handlerRef.current(context), {\n        skipSameRoute,\n      }),\n    );\n  }, [router, skipSameRoute]);\n}\n","import { createRouteEnterGate } from \"@real-router/sources\";\nimport { useEffect, useLayoutEffect, useRef, useState } from \"react\";\n\nimport { useRoute } from \"./useRoute\";\n\nimport type { State } from \"@real-router/core\";\n\nexport interface RouteEnterContext {\n  /** The route that was just activated. */\n  route: State;\n  /** The route that was active immediately before this navigation. */\n  previousRoute: State;\n}\n\nexport type RouteEnterHandler = (context: RouteEnterContext) => void;\n\nexport interface UseRouteEnterOptions {\n  /**\n   * Skip the handler when `route.name === previousRoute.name`\n   * (sort/filter/query-only navigations on the same route). Default:\n   * `true`. Symmetric with `useRouteExit`'s same-name option.\n   */\n  skipSameRoute?: boolean;\n}\n\n/**\n * Fire `handler` once when the component mounts as a result of a\n * navigation. Mirror of `useRouteExit` for the entry side.\n *\n * What this hook covers that ad-hoc `useEffect` + `useRoute()` doesn't:\n *\n *   - **Skip-initial**: handler is skipped when there is no\n *     `previousRoute` (i.e. first-load mount). Most consumers want to\n *     fire side effects only on real navigations, not on hydration.\n *   - **Same-route skip** (default): handler is skipped when\n *     `route.name === previousRoute.name`. Sort/filter/query-only\n *     navigations re-run the effect (because `route` reference changes\n *     in `useRoute`'s snapshot), but they are not \"entries\" in the\n *     animation / analytics sense — the component instance has stayed\n *     mounted throughout. Opt out with `skipSameRoute: false` when\n *     the handler legitimately needs to fire on every navigation\n *     (e.g. analytics tracking each query-param flip).\n *   - **StrictMode double-mount immunity**: in dev, React's StrictMode\n *     runs every effect twice to surface bugs. Without a guard,\n *     analytics fire twice, animations restart, focus jumps. The hook\n *     tracks the last-handled `route` reference and short-circuits the\n *     second pass.\n *   - **Latest-handler ref**: the handler can change identity on every\n *     render without re-running the effect — the registered wrapper\n *     dispatches to whatever `handlerRef.current` points to.\n *   - **Mount-time `route` / `previousRoute` snapshot**: the handler\n *     receives the values that were live at the moment of mount, not\n *     the latest ones (which may have moved on if the user navigated\n *     again before the effect drained).\n *\n * Race-safety: `useRoute()` is wired through `useSyncExternalStore` from\n * `@real-router/sources`, so by the time the new component's effect\n * runs, the snapshot is the post-commit one. This is the reason we can\n * read mount-time context from `useRoute()` instead of subscribing to\n * `router.subscribe` directly (which fires before React schedules a\n * re-render — the well-known race in distributed components).\n *\n * @example Direction-aware entry animation\n * ```tsx\n * useRouteEnter(({ route }) => {\n *   const direction = route.context.browser?.direction;\n *   ref.current?.classList.add(\n *     direction === \"back\" ? \"slide-from-left\" : \"slide-from-right\",\n *   );\n * });\n * ```\n *\n * @example Source-aware focus management\n * ```tsx\n * useRouteEnter(({ route }) => {\n *   if (route.context.browser?.source === \"navigate\") {\n *     headingRef.current?.focus();\n *   }\n * });\n * ```\n *\n * @example Analytics page-enter event (skip-initial built-in)\n * ```tsx\n * useRouteEnter(({ route, previousRoute }) => {\n *   analytics.track(\"page_enter\", {\n *     route: route.name,\n *     from: previousRoute.name,\n *   });\n * });\n * ```\n *\n * @example Reading rich transition metadata via `route.transition`\n * ```tsx\n * useRouteEnter(({ route }) => {\n *   // route.transition: TransitionMeta — populated by core for every state\n *   // ⚠ NOT `transition.redirected` — the router never sets it (it only ever\n *   // carries what a caller passed as `{ redirected: true }`), so branching on\n *   // it here is silently dead for a `forwardTo` or a guard redirect.\n *   // `from` needs no check: this hook does not fire without one.\n *   showToast(`Arrived from ${route.transition.from}`);\n *   if (route.transition.segments.activated.includes(\"products\")) {\n *     // products subtree just became active (could be products or\n *     // products.detail). Useful for subtree-scoped side effects.\n *   }\n * });\n * ```\n */\nexport function useRouteEnter(\n  handler: RouteEnterHandler,\n  options?: UseRouteEnterOptions,\n): void {\n  const { route, previousRoute } = useRoute();\n  const handlerRef = useRef(handler);\n  // The canonical enter-guard set + `lastHandledRoute` dedupe live in the\n  // shared gate (@real-router/sources, #1435). Created once via useState's\n  // lazy initializer so its dedupe state is stable across StrictMode's effect\n  // re-runs — `skipSameRoute` is threaded per-call, so an options flip never\n  // forces a fresh gate (which would reset the dedupe). `useState`, not a ref:\n  // the gate must be created once and stay stable, and a ref write during\n  // render is disallowed (@eslint-react/refs); the gate is never re-set, so no\n  // re-render is triggered. The gate owns skip-initial / same-route /\n  // StrictMode-dedupe / the `!previousRoute` guard — the sole defense of the\n  // non-nullable `RouteEnterContext.previousRoute` contract (#1218 PC1/PC2).\n  const [gate] = useState(() => createRouteEnterGate());\n  const skipSameRoute = options?.skipSameRoute ?? true;\n\n  // Keep the latest handler reference accessible without re-running\n  // the effect. useLayoutEffect (synchronous, post-render, pre-paint)\n  // updates the ref before the effect can read it.\n  useLayoutEffect(() => {\n    handlerRef.current = handler;\n  });\n\n  useEffect(() => {\n    const context = gate(route, previousRoute, skipSameRoute);\n\n    if (context) {\n      handlerRef.current(context);\n    }\n  }, [gate, route, previousRoute, skipSameRoute]);\n}\n"],"mappings":"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"}