{"version":3,"file":"ssr-client.cjs","names":[],"sources":["../../../src/ssr/ssr-client.ts"],"sourcesContent":["import { invariant } from '../invariant'\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  if (!window.$_TSR) {\n    if (process.env.NODE_ENV !== 'production') {\n      throw new Error(\n        'Invariant failed: Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!',\n      )\n    }\n\n    invariant()\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  if (!window.$_TSR.router) {\n    if (process.env.NODE_ENV !== 'production') {\n      throw new Error(\n        'Invariant failed: Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',\n      )\n    }\n\n    invariant()\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.stores.location.get())\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.stores.setMatches(matches)\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  const activeMatches = router.stores.matches.get()\n  const location = router.stores.location.get()\n  await Promise.all(\n    activeMatches.map(async (match) => {\n      try {\n        const route = router.looseRoutesById[match.routeId]!\n\n        const parentMatch = activeMatches[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,\n              navigate: (opts: any) =>\n                router.navigate({\n                  ...opts,\n                  _fromLocation: 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: activeMatches,\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    // Mark the current location as resolved so that later load cycles\n    // (e.g. preloads, invalidations) don't mistakenly detect a href change\n    // (resolvedLocation defaults to undefined and router.load() is skipped\n    // in the normal SSR hydration path).\n    router.stores.resolvedLocation.set(router.stores.location.get())\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    if (!match) {\n      if (process.env.NODE_ENV !== 'production') {\n        throw new Error(\n          'Invariant failed: Expected to find a match below the root match in SPA mode.',\n        )\n      }\n\n      invariant()\n    }\n    setMatchForcePending(match)\n\n    match._displayPending = true\n    match._nonReactive.displayPendingPromise = loadPromise\n\n    loadPromise.then(() => {\n      router.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.stores.status.get() === 'pending') {\n          router.stores.status.set('idle')\n          router.stores.resolvedLocation.set(router.stores.location.get())\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"],"mappings":"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"}