{"version":3,"file":"registryTransfer.cjs","names":[],"sources":["../../src/registryTransfer.ts"],"sourcesContent":["import * as Solid from 'solid-js'\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's serialization context is live, 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\ninterface SerializationContext {\n  noHydrate?: boolean\n  serialize: (key: string, value: unknown) => void\n}\n\nexport function serializeMatchTransfer(router: AnyRouter): void {\n  if (router.serverSsr) return\n  const ctx = (\n    Solid.sharedConfig as unknown as { context?: SerializationContext }\n  ).context\n  if (!ctx || typeof ctx.serialize !== 'function' || ctx.noHydrate) 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    ctx.serialize(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, `noHydrate`, or a pending match the server skipped).\n *\n * Reads the raw registry rather than sharedConfig's accessors: entries\n * arrive as inline scripts that execute at document parse, so they are\n * complete before any client code runs — and the boot must commit BEFORE\n * the hydration render (store writes inside it are owned-scope writes).\n */\nexport function primeRouterFromRegistry(router: AnyRouter): boolean {\n  if (router.stores.matches.get().length > 0) return false\n  const registry = (\n    globalThis as unknown as { _$HY?: { r: Record<string, unknown> } }\n  )._$HY?.r\n  if (!registry) return false\n  // A page with no match entries (SPA, or Start's own channel) skips before\n  // paying for a match pass.\n  let hasMatchEntries = false\n  for (const key in registry) {\n    if (key.startsWith(MATCH_KEY_PREFIX)) {\n      hasMatchEntries = true\n      break\n    }\n  }\n  if (!hasMatchEntries) 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 key = MATCH_KEY_PREFIX + match.id\n    if (!(key in registry)) return false\n    const raw = registry[key] as\n      | TransferredMatch\n      | { s: number; v?: TransferredMatch }\n      | null\n    delete registry[key]\n    // Settled serialization refs are stamped `s`/`v`; sync-serialized\n    // entries are the value itself.\n    const entry =\n      raw != null && typeof raw === 'object' && 's' in raw && raw.s === 1\n        ? raw.v\n        : (raw as TransferredMatch | null)\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":";;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAa,mBAAmB;AAwBhC,SAAgB,uBAAuB,QAAyB;CAC9D,IAAI,OAAO,WAAW;CACtB,MAAM,MACJ,SAAM,aACN;CACF,IAAI,CAAC,OAAO,OAAO,IAAI,cAAc,cAAc,IAAI,WAAW;CAElE,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,IAAI,UAAU,mBAAmB,MAAM,IAAI,KAAK;CAClD;AACF;;;;;;;;;;;;AAaA,SAAgB,wBAAwB,QAA4B;CAClE,IAAI,OAAO,OAAO,QAAQ,IAAI,EAAE,SAAS,GAAG,OAAO;CACnD,MAAM,WACJ,WACA,MAAM;CACR,IAAI,CAAC,UAAU,OAAO;CAGtB,IAAI,kBAAkB;CACtB,KAAK,MAAM,OAAO,UAChB,IAAI,IAAI,WAAA,MAA2B,GAAG;EACpC,kBAAkB;EAClB;CACF;CAEF,IAAI,CAAC,iBAAiB,OAAO;CAE7B,MAAM,UAAU,OAAO,YAAY,OAAO,cAAc;CACxD,IAAI,QAAQ,WAAW,GAAG,OAAO;CAEjC,MAAM,SAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,MAAM,mBAAmB,MAAM;EACrC,IAAI,EAAE,OAAO,WAAW,OAAO;EAC/B,MAAM,MAAM,SAAS;EAIrB,OAAO,SAAS;EAGhB,MAAM,QACJ,OAAO,QAAQ,OAAO,QAAQ,YAAY,OAAO,OAAO,IAAI,MAAM,IAC9D,IAAI,IACH;EACP,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"}