import { trpc } from "@/lib/trpc"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { httpBatchLink } from "@trpc/client"; import { createRoot } from "react-dom/client"; import superjson from "superjson"; import App from "./App"; import { Toaster } from "@/components/ui/sonner"; import { CodexProvider } from "@/contexts/CodexContext"; import { PlaylistProvider } from "@/contexts/PlaylistContext"; import { PlaylistBar } from "@/components/PlaylistBar"; import { ABTestProvider } from "@/contexts/ABTestContext"; import AffirmationToast from "@/components/AffirmationToast"; import InvestorIdentityModal from "@/components/InvestorIdentityModal"; import { ClerkAvailabilityProvider } from "@/contexts/ClerkAvailabilityContext"; import { ClerkProvider } from "@clerk/clerk-react"; import "./index.css"; import { initPerformanceMonitoring } from "@/utils/buildOptimizations"; import React, { useState, useEffect } from "react"; // ─── Web Vitals → Umami Analytics ──────────────────────────────────── declare global { interface Window { umami?: { track: (eventName: string, data?: Record) => void; }; } } if (import.meta.env.PROD) { window.addEventListener('load', () => { setTimeout(() => { initPerformanceMonitoring((name, value) => { if (window.umami?.track) { window.umami.track('web-vital', { metric: name, value: Math.round(value * 100) / 100, rating: getVitalRating(name, value), page: window.location.pathname, }); } }); }, 1000); }); } function getVitalRating(name: string, value: number): string { switch (name) { case 'LCP': return value <= 2500 ? 'good' : value <= 4000 ? 'needs-improvement' : 'poor'; case 'FID': return value <= 100 ? 'good' : value <= 300 ? 'needs-improvement' : 'poor'; case 'CLS': return value <= 0.1 ? 'good' : value <= 0.25 ? 'needs-improvement' : 'poor'; case 'INP': return value <= 200 ? 'good' : value <= 500 ? 'needs-improvement' : 'poor'; case 'LongTask': return value <= 50 ? 'good' : value <= 100 ? 'needs-improvement' : 'poor'; default: return 'unknown'; } } const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 3 * 60 * 1000, // 3 min — prevents redundant refetches on navigation gcTime: 15 * 60 * 1000, // 15 min — keep data in memory longer refetchOnWindowFocus: false, refetchOnReconnect: false, retry: 1, retryDelay: (attemptIndex) => Math.min(500 * 2 ** attemptIndex, 5000), // Fast retry }, }, }); queryClient.getQueryCache().subscribe(event => { if (event.type === "updated" && event.action.type === "error") { console.error("[API Query Error]", event.query.state.error); } }); queryClient.getMutationCache().subscribe(event => { if (event.type === "updated" && event.action.type === "error") { console.error("[API Mutation Error]", event.mutation.state.error); } }); const trpcClient = trpc.createClient({ links: [ httpBatchLink({ url: "/api/trpc", transformer: superjson, maxURLLength: 2048, fetch(input, init) { return globalThis.fetch(input, { ...(init ?? {}), credentials: "include", }).then(async (response) => { const contentType = response.headers.get('content-type') || ''; if (!response.ok && contentType.includes('text/html')) { const status = response.status; let msg = 'Server error'; if (status === 502 || status === 503 || status === 504) { msg = 'The request timed out. The server may be processing a large request. Please try again.'; } else if (status === 413) { msg = 'The response was too large to process.'; } return new Response( JSON.stringify([{ error: { message: msg, code: -1, data: { code: 'TIMEOUT', httpStatus: status } } }]), { status: 200, headers: { 'content-type': 'application/json' } } ); } return response; }); }, }), ], }); // Performance observer — detect long tasks in development if (typeof PerformanceObserver !== 'undefined' && !import.meta.env.PROD) { try { const longTaskObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.duration > 100) { console.warn(`[Perf] Long task: ${entry.duration.toFixed(0)}ms`, entry); } } }); longTaskObserver.observe({ type: 'longtask', buffered: true }); } catch { /* PerformanceObserver longtask not supported */ } } // Prefetch critical font files during idle time if ('requestIdleCallback' in window) { (window as any).requestIdleCallback(() => { const fontUrls = [ 'https://fonts.gstatic.com/s/inter/v18/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hiA.woff2', ]; fontUrls.forEach(url => { const link = document.createElement('link'); link.rel = 'prefetch'; link.as = 'font'; link.type = 'font/woff2'; link.crossOrigin = 'anonymous'; link.href = url; document.head.appendChild(link); }); }); } // Register service worker for offline support if ('serviceWorker' in navigator && import.meta.env.PROD) { window.addEventListener('load', () => { const register = () => { navigator.serviceWorker.register('/sw.js') .then((registration) => { setInterval(() => registration.update(), 60 * 60 * 1000); }) .catch(() => {}); }; if ('requestIdleCallback' in window) { (window as any).requestIdleCallback(register); } else { setTimeout(register, 2000); } }); } // ─── Clerk Configuration ───────────────────────────────────────────── const CLERK_PK = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY || ""; const CLERK_SIGN_IN_URL = "https://marlowekeynes.org/login"; const CLERK_PRIMARY_DOMAIN = "marlowekeynes.org"; /** * Probe the Clerk Frontend API to check if the satellite domain is reachable. * Only considers Clerk available on a 2xx response to prevent loading a broken * ClerkProvider that hangs or crashes the page. */ async function probeClerkCDN(): Promise { if (!CLERK_PK) return false; try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 3000); const res = await fetch("https://clerk.mkcompanyhistory.com/v1/environment", { method: "GET", signal: controller.signal, headers: { Authorization: `Bearer ${CLERK_PK}` }, }); clearTimeout(timeout); return res.ok; // Only 2xx counts as reachable } catch { return false; } } /** * App tree — renders the full provider stack. * * Clerk satellite integration: * - Probes the Clerk Frontend API on mount to verify reachability * - If reachable, wraps the app in ClerkProvider with satellite config * - If not reachable, falls back to cookie-based tRPC auth via useAuth */ function AppTree() { const [clerkReachable, setClerkReachable] = useState(null); useEffect(() => { probeClerkCDN().then(setClerkReachable); }, []); // While probing, render without Clerk (cookie auth still works) if (clerkReachable === null) { return ( ); } const inner = ( ); // HIDDEN: Clerk satellite login disabled for now — re-enable this block to restore Clerk SSO // if (clerkReachable && CLERK_PK) { // return ( // // // {inner} // // // ); // } return ( {inner} ); } createRoot(document.getElementById("root")!).render();