import "server-only"; import type { AppRouter } from "@openstatus/api"; import { HydrationBoundary } from "@tanstack/react-query"; import { dehydrate } from "@tanstack/react-query"; import { TRPCClientError, createTRPCClient, loggerLink } from "@trpc/client"; import { type ResolverDef, type TRPCQueryOptions, createTRPCOptionsProxy, } from "@trpc/tanstack-react-query"; import { cookies } from "next/headers"; import { notFound } from "next/navigation"; import { cache } from "react"; import { makeQueryClient } from "./query-client"; import { endingLink } from "./shared"; // IMPORTANT: Create a stable getter for the query client that // will return the same client during the same request. export const getQueryClient = cache(makeQueryClient); export const trpc = createTRPCOptionsProxy({ queryClient: getQueryClient, client: createTRPCClient({ links: [ loggerLink({ enabled: (opts) => process.env.NODE_ENV === "development" || (opts.direction === "down" && opts.result instanceof Error), }), endingLink({ headers: { "x-trpc-source": "server", }, // `typeof fetch` carries a `preconnect` static (React 19 typings) that // tRPC's link will never invoke — cast the call-signature wrapper. fetch: (async (url, options) => { const cookieStore = await cookies(); console.log("[dashboard trpc server] fetch", { hasSessionToken: !!cookieStore.get("__Secure-authjs.session-token")?.value || !!cookieStore.get("authjs.session-token")?.value, }); return fetch(url, { ...options, credentials: "include", headers: { ...options?.headers, cookie: cookieStore.toString(), }, }); }) as typeof fetch, }), ], }), }); export function HydrateClient(props: { children: React.ReactNode }) { const queryClient = getQueryClient(); return ( {props.children} ); } // oxlint-disable-next-line typescript/no-explicit-any -- FIXME: remove any export function prefetch>>( queryOptions: T, ) { const queryClient = getQueryClient(); if (queryOptions.queryKey[1]?.type === "infinite") { // oxlint-disable-next-line typescript/no-explicit-any -- FIXME: remove any void queryClient.prefetchInfiniteQuery(queryOptions as any); } else { void queryClient.prefetchQuery(queryOptions); } } // oxlint-disable-next-line typescript/no-explicit-any -- FIXME: remove any export function batchPrefetch>>( queryOptionsArray: T[], ) { const queryClient = getQueryClient(); for (const queryOptions of queryOptionsArray) { if (queryOptions.queryKey[1]?.type === "infinite") { // oxlint-disable-next-line typescript/no-explicit-any -- FIXME: remove any void queryClient.prefetchInfiniteQuery(queryOptions as any); } else { void queryClient.prefetchQuery(queryOptions); } } } /** * Fetches a query and calls `notFound()` if the server returns NOT_FOUND. * Use this for gating queries in layouts where the resource must exist. */ export async function fetchQueryOrNotFound< T extends ReturnType>, >(queryOptions: T) { const queryClient = getQueryClient(); try { return (await queryClient.fetchQuery(queryOptions)) as Awaited< ReturnType unknown>> >; } catch (error) { if (error instanceof TRPCClientError && error.data?.code === "NOT_FOUND") { notFound(); } throw error; } }