{"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  // Allow the user to handle custom hydration data before matching routes.\n  // This lets hydration install router config that affects matching, e.g. rewrites.\n  await router.options.hydrate?.(dehydratedData)\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  // 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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"}