{"version":3,"file":"ssr-client.cjs","sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport { batch } from '../utils/batch'\nimport { isNotFound } from '../not-found'\nimport { createControlledPromise } from '../utils'\nimport { hydrateSsrMatchId } from './ssr-match-id'\nimport type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants'\nimport type { DehydratedMatch, TsrSsrGlobal } from './types'\nimport type { AnyRouteMatch } from '../Matches'\nimport type { AnyRouter } from '../router'\nimport type { RouteContextOptions } from '../route'\nimport type { AnySerializationAdapter } from './serializer/transformer'\n\ndeclare global {\n  interface Window {\n    [GLOBAL_TSR]?: TsrSsrGlobal\n    [GLOBAL_SEROVAL]?: any\n  }\n}\n\nfunction hydrateMatch(\n  match: AnyRouteMatch,\n  deyhydratedMatch: DehydratedMatch,\n): void {\n  match.id = deyhydratedMatch.i\n  match.__beforeLoadContext = deyhydratedMatch.b\n  match.loaderData = deyhydratedMatch.l\n  match.status = deyhydratedMatch.s\n  match.ssr = deyhydratedMatch.ssr\n  match.updatedAt = deyhydratedMatch.u\n  match.error = deyhydratedMatch.e\n  // Only hydrate global-not-found when a defined value is present in the\n  // dehydrated payload. If omitted, preserve the value computed from the\n  // current client location (important for SPA fallback HTML served at unknown\n  // URLs, where dehydrated matches may come from `/` but client matching marks\n  // root as globalNotFound).\n  if (deyhydratedMatch.g !== undefined) {\n    match.globalNotFound = deyhydratedMatch.g\n  }\n}\n\nexport async function hydrate(router: AnyRouter): Promise<any> {\n  invariant(\n    window.$_TSR,\n    'Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!',\n  )\n\n  const serializationAdapters = router.options.serializationAdapters as\n    | Array<AnySerializationAdapter>\n    | undefined\n\n  if (serializationAdapters?.length) {\n    const fromSerializableMap = new Map()\n    serializationAdapters.forEach((adapter) => {\n      fromSerializableMap.set(adapter.key, adapter.fromSerializable)\n    })\n    window.$_TSR.t = fromSerializableMap\n    window.$_TSR.buffer.forEach((script) => script())\n  }\n  window.$_TSR.initialized = true\n\n  invariant(\n    window.$_TSR.router,\n    'Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',\n  )\n\n  const dehydratedRouter = window.$_TSR.router\n  dehydratedRouter.matches.forEach((dehydratedMatch) => {\n    dehydratedMatch.i = hydrateSsrMatchId(dehydratedMatch.i)\n  })\n  if (dehydratedRouter.lastMatchId) {\n    dehydratedRouter.lastMatchId = hydrateSsrMatchId(\n      dehydratedRouter.lastMatchId,\n    )\n  }\n  const { manifest, dehydratedData, lastMatchId } = dehydratedRouter\n\n  router.ssr = {\n    manifest,\n  }\n  const meta = document.querySelector('meta[property=\"csp-nonce\"]') as\n    | HTMLMetaElement\n    | undefined\n  const nonce = meta?.content\n  router.options.ssr = {\n    nonce,\n  }\n\n  // Hydrate the router state\n  const matches = router.matchRoutes(router.state.location)\n\n  // kick off loading the route chunks\n  const routeChunkPromise = Promise.all(\n    matches.map((match) =>\n      router.loadRouteChunk(router.looseRoutesById[match.routeId]!),\n    ),\n  )\n\n  function setMatchForcePending(match: AnyRouteMatch) {\n    // usually the minPendingPromise is created in the Match component if a pending match is rendered\n    // however, this might be too late if the match synchronously resolves\n    const route = router.looseRoutesById[match.routeId]!\n    const pendingMinMs =\n      route.options.pendingMinMs ?? router.options.defaultPendingMinMs\n    if (pendingMinMs) {\n      const minPendingPromise = createControlledPromise<void>()\n      match._nonReactive.minPendingPromise = minPendingPromise\n      match._forcePending = true\n\n      setTimeout(() => {\n        minPendingPromise.resolve()\n        // We've handled the minPendingPromise, so we can delete it\n        router.updateMatch(match.id, (prev) => {\n          prev._nonReactive.minPendingPromise = undefined\n          return {\n            ...prev,\n            _forcePending: undefined,\n          }\n        })\n      }, pendingMinMs)\n    }\n  }\n\n  function setRouteSsr(match: AnyRouteMatch) {\n    const route = router.looseRoutesById[match.routeId]\n    if (route) {\n      route.options.ssr = match.ssr\n    }\n  }\n  // Right after hydration and before the first render, we need to rehydrate each match\n  // First step is to reyhdrate loaderData and __beforeLoadContext\n  let firstNonSsrMatchIndex: number | undefined = undefined\n  matches.forEach((match) => {\n    const dehydratedMatch = dehydratedRouter.matches.find(\n      (d) => d.i === match.id,\n    )\n    if (!dehydratedMatch) {\n      match._nonReactive.dehydrated = false\n      match.ssr = false\n      setRouteSsr(match)\n      return\n    }\n\n    hydrateMatch(match, dehydratedMatch)\n    setRouteSsr(match)\n\n    match._nonReactive.dehydrated = match.ssr !== false\n\n    if (match.ssr === 'data-only' || match.ssr === false) {\n      if (firstNonSsrMatchIndex === undefined) {\n        firstNonSsrMatchIndex = match.index\n        setMatchForcePending(match)\n      }\n    }\n  })\n\n  router.__store.setState((s) => ({\n    ...s,\n    matches,\n  }))\n\n  // Allow the user to handle custom hydration data\n  await router.options.hydrate?.(dehydratedData)\n\n  // now that all necessary data is hydrated:\n  // 1) fully reconstruct the route context\n  // 2) execute `head()` and `scripts()` for each match\n  await Promise.all(\n    router.state.matches.map(async (match) => {\n      try {\n        const route = router.looseRoutesById[match.routeId]!\n\n        const parentMatch = router.state.matches[match.index - 1]\n        const parentContext = parentMatch?.context ?? router.options.context\n\n        // `context()` was already executed by `matchRoutes`, however route context was not yet fully reconstructed\n        // so run it again and merge route context\n        if (route.options.context) {\n          const contextFnContext: RouteContextOptions<any, any, any, any, any> =\n            {\n              deps: match.loaderDeps,\n              params: match.params,\n              context: parentContext ?? {},\n              location: router.state.location,\n              navigate: (opts: any) =>\n                router.navigate({\n                  ...opts,\n                  _fromLocation: router.state.location,\n                }),\n              buildLocation: router.buildLocation,\n              cause: match.cause,\n              abortController: match.abortController,\n              preload: false,\n              matches,\n              routeId: route.id,\n            }\n          match.__routeContext =\n            route.options.context(contextFnContext) ?? undefined\n        }\n\n        match.context = {\n          ...parentContext,\n          ...match.__routeContext,\n          ...match.__beforeLoadContext,\n        }\n\n        const assetContext = {\n          ssr: router.options.ssr,\n          matches: router.state.matches,\n          match,\n          params: match.params,\n          loaderData: match.loaderData,\n        }\n        const headFnContent = await route.options.head?.(assetContext)\n\n        const scripts = await route.options.scripts?.(assetContext)\n\n        match.meta = headFnContent?.meta\n        match.links = headFnContent?.links\n        match.headScripts = headFnContent?.scripts\n        match.styles = headFnContent?.styles\n        match.scripts = scripts\n      } catch (err) {\n        if (isNotFound(err)) {\n          match.error = { isNotFound: true }\n          console.error(\n            `NotFound error during hydration for routeId: ${match.routeId}`,\n            err,\n          )\n        } else {\n          match.error = err as any\n          console.error(\n            `Error during hydration for route ${match.routeId}:`,\n            err,\n          )\n          throw err\n        }\n      }\n    }),\n  )\n\n  const isSpaMode = matches[matches.length - 1]!.id !== lastMatchId\n  const hasSsrFalseMatches = matches.some((m) => m.ssr === false)\n  // all matches have data from the server and we are not in SPA mode so we don't need to kick of router.load()\n  if (!hasSsrFalseMatches && !isSpaMode) {\n    matches.forEach((match) => {\n      // remove the dehydrated flag since we won't run router.load() which would remove it\n      match._nonReactive.dehydrated = undefined\n    })\n    return routeChunkPromise\n  }\n\n  // schedule router.load() to run after the next tick so we can store the promise in the match before loading starts\n  const loadPromise = Promise.resolve()\n    .then(() => router.load())\n    .catch((err) => {\n      console.error('Error during router hydration:', err)\n    })\n\n  // in SPA mode we need to keep the first match below the root route pending until router.load() is finished\n  // this will prevent that other pending components are rendered but hydration is not blocked\n  if (isSpaMode) {\n    const match = matches[1]\n    invariant(\n      match,\n      'Expected to find a match below the root match in SPA mode.',\n    )\n    setMatchForcePending(match)\n\n    match._displayPending = true\n    match._nonReactive.displayPendingPromise = loadPromise\n\n    loadPromise.then(() => {\n      batch(() => {\n        // ensure router is not in status 'pending' anymore\n        // this usually happens in Transitioner but if loading synchronously resolves,\n        // Transitioner won't be rendered while loading so it cannot track the change from loading:true to loading:false\n        if (router.__store.state.status === 'pending') {\n          router.__store.setState((s) => ({\n            ...s,\n            status: 'idle',\n            resolvedLocation: s.location,\n          }))\n        }\n        // hide the pending component once the load is finished\n        router.updateMatch(match.id, (prev) => ({\n          ...prev,\n          _displayPending: undefined,\n          displayPendingPromise: undefined,\n        }))\n      })\n    })\n  }\n  return routeChunkPromise\n}\n"],"names":["hydrateSsrMatchId","createControlledPromise","isNotFound","batch"],"mappings":"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"}