{"version":3,"file":"registryTransfer.cjs","names":[],"sources":["../../src/registryTransfer.ts"],"sourcesContent":["import { isHydratable } from 'solid-js'\nimport { getHydrationWriter, takeHydrationValue } from '@solidjs/web'\nimport type { AnyRouteMatch, AnyRouter } from '@tanstack/router-core'\n\n/**\n * Match-state transfer over Solid's hydration registry — the bare pairing's\n * native SSR channel (no `__TSR_SSR__` script injection, no router-owned\n * stream protocol). Entries are content-addressed (`tsr:<matchId>`; match\n * ids are deterministic route-id + interpolated params, so both sides\n * derive the same key from the same URL), the identical mechanism\n * solid-query v6 ships query payloads through (`sq:<queryHash>`).\n *\n * Server half: while the render takes values (`getHydrationWriter`), the\n * provider writes each settled match's transferable state. Client half: the\n * hydration-claiming boot — matching is synchronous, so the provider primes\n * match state from the registry and commits before rendering, without\n * running loaders or resolving route chunks up front. Route components\n * resolve at the read point under the boundaries the server actually\n * rendered (Solid `lazy` semantics), and staleness rules own any\n * post-hydration refetching.\n *\n * Both halves no-op under TanStack Start: `router.serverSsr` marks the\n * Start contract (`attachRouterServerSsrUtils` / `dehydrate` / `hydrate`),\n * which owns transfer there — and a Start-hydrated router reaches the\n * client boot with matches already committed, which skips it.\n */\nexport const MATCH_KEY_PREFIX = 'tsr:'\n\n/** The slice of a match that transfers; mirrors the single-flight match\n * shape (`createSolidStartFlightMatch`) — state, not identity: the client\n * re-derives identity by matching, then merges this in. */\ninterface TransferredMatch {\n  status: AnyRouteMatch['status']\n  updatedAt: number\n  loaderData?: unknown\n  error?: unknown\n  notFound?: true\n  beforeLoadContext?: Record<string, unknown>\n  ssr?: AnyRouteMatch['ssr']\n}\n\ntype MatchWithBeforeLoadContext = AnyRouteMatch & {\n  __beforeLoadContext?: Record<string, unknown>\n}\n\nexport function serializeMatchTransfer(router: AnyRouter): void {\n  if (router.serverSsr) return\n  const writer = getHydrationWriter()\n  if (!writer || !isHydratable()) return\n\n  for (const match of router.stores.matches.get()) {\n    // Pending matches are skipped, not deferred: this core has no per-match\n    // settle promise to hand seroval, so promise-valued entries (streaming\n    // SSR, loaders landing after first flush) need a dispatch-time hook —\n    // the next increment. A missing entry makes the client boot fall\n    // through to today's behavior rather than half-prime.\n    if (match.status === 'pending') continue\n    const entry: TransferredMatch = {\n      status: match.status,\n      updatedAt: match.updatedAt,\n    }\n    if (match.loaderData !== undefined) entry.loaderData = match.loaderData\n    if (match.error !== undefined) entry.error = match.error\n    if (match._notFound) entry.notFound = true\n    const beforeLoadContext = (match as MatchWithBeforeLoadContext)\n      .__beforeLoadContext\n    if (beforeLoadContext !== undefined)\n      entry.beforeLoadContext = beforeLoadContext\n    if (match.ssr !== undefined) entry.ssr = match.ssr\n    writer.write(MATCH_KEY_PREFIX + match.id, entry)\n  }\n}\n\n/**\n * The hydration-claiming boot. Returns true when every synchronously\n * matched route found its registry entry and the matches were committed;\n * false falls back to the caller's existing behavior (no entries — a\n * non-registry server, `<NoHydration>`, or a pending match the server\n * skipped).\n *\n * Entries arrive as inline scripts that execute at document parse, so they\n * are complete before any client code runs and `takeHydrationValue` reads\n * them before `hydrate()` — the boot must commit BEFORE the hydration\n * render (store writes inside it are owned-scope writes). Each matched\n * route takes its own key: a page without entries (SPA, or Start's own\n * channel) falls through at the first match.\n */\nexport function primeRouterFromRegistry(router: AnyRouter): boolean {\n  if (router.stores.matches.get().length > 0) return false\n\n  const matches = router.matchRoutes(router.latestLocation)\n  if (matches.length === 0) return false\n\n  const primed: Array<AnyRouteMatch> = []\n  for (const match of matches) {\n    const taken = takeHydrationValue<TransferredMatch | null>(\n      MATCH_KEY_PREFIX + match.id,\n    )\n    if (taken?.status !== 'resolved') return false\n    const entry = taken.value\n    if (!entry || typeof entry.status !== 'string') return false\n    primed.push(applyTransferredMatch(match, entry))\n  }\n\n  // Commit the way the single-flight client publishes hydrated matches.\n  router._committed = primed\n  router.batch(() => {\n    router.stores.setMatches(primed)\n    router.stores.status.set('idle')\n    router.stores.resolvedLocation.set(router.stores.location.get())\n  })\n  return true\n}\n\nfunction applyTransferredMatch(\n  match: AnyRouteMatch,\n  entry: TransferredMatch,\n): AnyRouteMatch {\n  const next = {\n    ...match,\n    status: entry.status,\n    updatedAt: entry.updatedAt,\n    error: entry.error,\n    invalid: false,\n    isFetching: false,\n    preload: false,\n    _notFound: entry.notFound,\n  } as AnyRouteMatch\n  if ('loaderData' in entry) next.loaderData = entry.loaderData\n  if ('beforeLoadContext' in entry) {\n    ;(next as MatchWithBeforeLoadContext).__beforeLoadContext =\n      entry.beforeLoadContext\n    next.context = {\n      ...next.context,\n      ...entry.beforeLoadContext,\n    }\n  }\n  if ('ssr' in entry) next.ssr = entry.ssr\n  return next\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,IAAa,mBAAmB;AAmBhC,SAAgB,uBAAuB,QAAyB;CAC9D,IAAI,OAAO,WAAW;CACtB,MAAM,UAAA,GAAA,aAAA,oBAA4B;CAClC,IAAI,CAAC,UAAU,EAAA,GAAA,SAAA,cAAc,GAAG;CAEhC,KAAK,MAAM,SAAS,OAAO,OAAO,QAAQ,IAAI,GAAG;EAM/C,IAAI,MAAM,WAAW,WAAW;EAChC,MAAM,QAA0B;GAC9B,QAAQ,MAAM;GACd,WAAW,MAAM;EACnB;EACA,IAAI,MAAM,eAAe,KAAA,GAAW,MAAM,aAAa,MAAM;EAC7D,IAAI,MAAM,UAAU,KAAA,GAAW,MAAM,QAAQ,MAAM;EACnD,IAAI,MAAM,WAAW,MAAM,WAAW;EACtC,MAAM,oBAAqB,MACxB;EACH,IAAI,sBAAsB,KAAA,GACxB,MAAM,oBAAoB;EAC5B,IAAI,MAAM,QAAQ,KAAA,GAAW,MAAM,MAAM,MAAM;EAC/C,OAAO,MAAM,mBAAmB,MAAM,IAAI,KAAK;CACjD;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,wBAAwB,QAA4B;CAClE,IAAI,OAAO,OAAO,QAAQ,IAAI,EAAE,SAAS,GAAG,OAAO;CAEnD,MAAM,UAAU,OAAO,YAAY,OAAO,cAAc;CACxD,IAAI,QAAQ,WAAW,GAAG,OAAO;CAEjC,MAAM,SAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,SAAA,GAAA,aAAA,oBACJ,mBAAmB,MAAM,EAC3B;EACA,IAAI,OAAO,WAAW,YAAY,OAAO;EACzC,MAAM,QAAQ,MAAM;EACpB,IAAI,CAAC,SAAS,OAAO,MAAM,WAAW,UAAU,OAAO;EACvD,OAAO,KAAK,sBAAsB,OAAO,KAAK,CAAC;CACjD;CAGA,OAAO,aAAa;CACpB,OAAO,YAAY;EACjB,OAAO,OAAO,WAAW,MAAM;EAC/B,OAAO,OAAO,OAAO,IAAI,MAAM;EAC/B,OAAO,OAAO,iBAAiB,IAAI,OAAO,OAAO,SAAS,IAAI,CAAC;CACjE,CAAC;CACD,OAAO;AACT;AAEA,SAAS,sBACP,OACA,OACe;CACf,MAAM,OAAO;EACX,GAAG;EACH,QAAQ,MAAM;EACd,WAAW,MAAM;EACjB,OAAO,MAAM;EACb,SAAS;EACT,YAAY;EACZ,SAAS;EACT,WAAW,MAAM;CACnB;CACA,IAAI,gBAAgB,OAAO,KAAK,aAAa,MAAM;CACnD,IAAI,uBAAuB,OAAO;EAC/B,KAAqC,sBACpC,MAAM;EACR,KAAK,UAAU;GACb,GAAG,KAAK;GACR,GAAG,MAAM;EACX;CACF;CACA,IAAI,SAAS,OAAO,KAAK,MAAM,MAAM;CACrC,OAAO;AACT"}