"use client"; import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { TRPCClientError } from "@trpc/client"; import { httpBatchLink, loggerLink } from "@trpc/client"; import { SessionProvider } from "next-auth/react"; import { useState } from "react"; import { trpc } from "./client.js"; function getBaseUrl() { if (typeof window !== "undefined") return window.location.origin; if (process.env["VERCEL_URL"]) return `https://${process.env["VERCEL_URL"]}`; return `http://localhost:${process.env["PORT"] ?? 3100}`; } function redirectToSignIn(): void { if (typeof window !== "undefined") { window.location.replace("/auth/signin"); } } function isUnauthorizedTrpcError(error: unknown): boolean { return ( error instanceof TRPCClientError && (error as unknown as { data?: { code?: string } }).data?.code === "UNAUTHORIZED" ); } function isIgnorableTransportError(error: unknown): boolean { const message = typeof error === "object" && error !== null && "message" in error ? String((error as { message?: unknown }).message ?? "") : ""; return message.includes("Failed to fetch") || message.toLowerCase().includes("aborted"); } export function TRPCProvider({ children }: { children: React.ReactNode }) { const [queryClient] = useState( () => new QueryClient({ queryCache: new QueryCache({ onError: (error) => { if (isUnauthorizedTrpcError(error)) redirectToSignIn(); }, }), mutationCache: new MutationCache({ onError: (error) => { if (isUnauthorizedTrpcError(error)) redirectToSignIn(); }, }), defaultOptions: { queries: { staleTime: 60_000, // 60 seconds — reduces refetches on navigation gcTime: 5 * 60_000, // 5 minutes — keep inactive queries in memory refetchOnWindowFocus: false, refetchOnReconnect: false, retry: (failureCount, error) => { // Never retry UNAUTHORIZED — redirect immediately instead if (isUnauthorizedTrpcError(error)) return false; // Don't retry on 4xx errors (auth, not found, bad input) if (error instanceof TRPCClientError) { const code = error.data?.httpStatus as number | undefined; if (code !== undefined && code >= 400 && code < 500) return false; } return failureCount < 2; }, }, }, }), ); const [trpcClient] = useState(() => trpc.createClient({ links: [ loggerLink({ enabled: (opts) => { const isDownError = opts.direction === "down" && isIgnorableTransportError(opts.result); if (isDownError) return false; if (process.env["NODE_ENV"] === "development") return true; return opts.direction === "down"; }, }), httpBatchLink({ url: `${getBaseUrl()}/api/trpc`, }), ], }), ); return ( {children} ); }