{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["import {\n  dehydrate as queryDehydrate,\n  hydrate as queryHydrate,\n} from '@tanstack/query-core'\nimport { isRedirect } from '@tanstack/router-core'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport type { AnyRouter } from '@tanstack/router-core'\nimport type {\n  QueryClient,\n  DehydratedState as QueryDehydratedState,\n} from '@tanstack/query-core'\n\nexport type RouterSsrQueryOptions<TRouter extends AnyRouter> = {\n  router: TRouter\n  queryClient: QueryClient\n\n  /**\n   * If `true`, the QueryClient will handle errors thrown by `redirect()` inside of mutations and queries.\n   *\n   * @default true\n   * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/api/router/redirectFunction)\n   */\n  handleRedirects?: boolean\n}\n\ntype DehydratedRouterQueryState = {\n  dehydratedQueryClient?: QueryDehydratedState\n  queryStream: ReadableStream<QueryDehydratedState>\n}\n\nexport function setupCoreRouterSsrQueryIntegration<TRouter extends AnyRouter>({\n  router,\n  queryClient,\n  handleRedirects = true,\n}: RouterSsrQueryOptions<TRouter>) {\n  const ogHydrate = router.options.hydrate\n  const ogDehydrate = router.options.dehydrate\n\n  if (isServer ?? router.isServer) {\n    const sentQueries = new Set<string>()\n    const queryStream = createPushableStream()\n    let unsubscribe: (() => void) | undefined = undefined\n    router.options.dehydrate =\n      async (): Promise<DehydratedRouterQueryState> => {\n        router.serverSsr!.onRenderFinished(() => {\n          queryStream.close()\n          unsubscribe?.()\n          unsubscribe = undefined\n        })\n        const ogDehydrated = await ogDehydrate?.()\n\n        const dehydratedRouter = {\n          ...ogDehydrated,\n          // prepare the stream for queries coming up during rendering\n          queryStream: queryStream.stream,\n        }\n\n        const dehydratedQueryClient = queryDehydrate(queryClient)\n        if (dehydratedQueryClient.queries.length > 0) {\n          dehydratedQueryClient.queries.forEach((query) => {\n            sentQueries.add(query.queryHash)\n          })\n          dehydratedRouter.dehydratedQueryClient = dehydratedQueryClient\n        }\n\n        return dehydratedRouter\n      }\n\n    const ogClientOptions = queryClient.getDefaultOptions()\n    queryClient.setDefaultOptions({\n      ...ogClientOptions,\n      dehydrate: {\n        shouldDehydrateQuery: () => true,\n        ...ogClientOptions.dehydrate,\n      },\n    })\n\n    unsubscribe = queryClient.getQueryCache().subscribe((event) => {\n      // before rendering starts, we do not stream individual queries\n      // instead we dehydrate the entire query client in router's dehydrate()\n      // if attachRouterServerSsrUtils() has not been called yet, `router.serverSsr` will be undefined and we also do not stream\n      if (!router.serverSsr?.isDehydrated()) {\n        return\n      }\n      if (sentQueries.has(event.query.queryHash)) {\n        return\n      }\n      // promise not yet set on the query, so we cannot stream it yet\n      if (!event.query.promise) {\n        return\n      }\n      if (queryStream.isClosed()) {\n        console.warn(\n          `tried to stream query ${event.query.queryHash} after stream was already closed`,\n        )\n        return\n      }\n      sentQueries.add(event.query.queryHash)\n      queryStream.enqueue(\n        queryDehydrate(queryClient, {\n          shouldDehydrateQuery: (query) => {\n            if (query.queryHash === event.query.queryHash) {\n              return (\n                ogClientOptions.dehydrate?.shouldDehydrateQuery?.(query) ?? true\n              )\n            }\n            return false\n          },\n        }),\n      )\n    })\n    // on the client\n  } else {\n    router.options.hydrate = async (dehydrated: DehydratedRouterQueryState) => {\n      await ogHydrate?.(dehydrated)\n      // hydrate the query client with the dehydrated data (if it was dehydrated on the server)\n      if (dehydrated.dehydratedQueryClient) {\n        queryHydrate(queryClient, dehydrated.dehydratedQueryClient)\n      }\n\n      // read the query stream and hydrate the queries as they come in\n      const reader = dehydrated.queryStream.getReader()\n      reader\n        .read()\n        .then(async function handle({ done, value }) {\n          queryHydrate(queryClient, value)\n          if (done) {\n            return\n          }\n          const result = await reader.read()\n          return handle(result)\n        })\n        .catch((err) => {\n          console.error('Error reading query stream:', err)\n        })\n    }\n    if (handleRedirects) {\n      const ogMutationCacheConfig = queryClient.getMutationCache().config\n      queryClient.getMutationCache().config = {\n        ...ogMutationCacheConfig,\n        onError: (error, ...rest) => {\n          if (isRedirect(error)) {\n            error.options._fromLocation = router.state.location\n            return router.navigate(router.resolveRedirect(error).options)\n          }\n\n          return ogMutationCacheConfig.onError?.(error, ...rest)\n        },\n      }\n\n      const ogQueryCacheConfig = queryClient.getQueryCache().config\n      queryClient.getQueryCache().config = {\n        ...ogQueryCacheConfig,\n        onError: (error, ...rest) => {\n          if (isRedirect(error)) {\n            error.options._fromLocation = router.state.location\n            return router.navigate(router.resolveRedirect(error).options)\n          }\n\n          return ogQueryCacheConfig.onError?.(error, ...rest)\n        },\n      }\n    }\n  }\n}\n\ntype PushableStream = {\n  stream: ReadableStream\n  enqueue: (chunk: unknown) => void\n  close: () => void\n  isClosed: () => boolean\n  error: (err: unknown) => void\n}\n\nfunction createPushableStream(): PushableStream {\n  let controllerRef: ReadableStreamDefaultController\n  const stream = new ReadableStream({\n    start(controller) {\n      controllerRef = controller\n    },\n  })\n  let _isClosed = false\n\n  return {\n    stream,\n    enqueue: (chunk) => controllerRef.enqueue(chunk),\n    close: () => {\n      controllerRef.close()\n      _isClosed = true\n    },\n    isClosed: () => _isClosed,\n    error: (err: unknown) => controllerRef.error(err),\n  }\n}\n"],"names":["queryDehydrate","queryHydrate"],"mappings":"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"}