// 客户端 Providers:QueryClient + Urql + MSW + ErrorBoundary + SW + OTel + WebVitals // 依据:02-architecture-design.md §4.3 状态管理分层、§8 MSW、§12 可观测性 // - NEXT_PUBLIC_API_MOCKING=enabled 时启动 MSW worker // - QueryClient 单例(useState 保持稳定) // - Urql client 单例 // - ErrorBoundary 包裹全局渲染异常兜底 // - Service Worker 注册(PWA 离线缓存,P6-5) // - OTel browser SDK 初始化(P6-1,条件启用) // - Web Vitals 采集初始化(P6-1,生产环境) "use client"; import { useState, useEffect, type ReactNode } from "react"; import { QueryClientProvider } from "@tanstack/react-query"; import { Provider as UrqlProvider } from "urql"; import { createQueryClient } from "@/lib/query-client"; import { getGraphQLClient } from "@/lib/graphql-client"; import { ErrorBoundary } from "@/components/ErrorBoundary"; import { WebVitalsInitializer } from "@/components/WebVitalsInitializer"; const isMockingEnabled = process.env.NEXT_PUBLIC_API_MOCKING === "enabled"; interface ProvidersProps { children: ReactNode; } export function Providers({ children }: ProvidersProps) { const [queryClient] = useState(() => createQueryClient()); const [urqlClient] = useState(() => getGraphQLClient()); const [mswReady, setMswReady] = useState(!isMockingEnabled); // MSW 初始化(仅浏览器 + mock 开启时) useEffect(() => { if (!isMockingEnabled || typeof window === "undefined") return; let active = true; (async () => { try { const { worker } = await import("@/test/mocks/browser"); await worker.start({ onUnhandledRequest: "bypass", serviceWorker: { url: "/mockServiceWorker.js", }, }); if (active) setMswReady(true); } catch (err) { // MSW 启动失败不阻塞应用 console.warn("[MSW] 启动失败,将使用真实 API", err); if (active) setMswReady(true); } })(); return () => { active = false; }; }, []); // Service Worker 注册(PWA,P6-5) useEffect(() => { if (typeof window === "undefined") return; if (process.env.NODE_ENV !== "production") return; if (!("serviceWorker" in navigator)) return; const registerSW = async () => { try { // MSW 启用时不注册 PWA Service Worker(避免冲突) if (isMockingEnabled) return; await navigator.serviceWorker.register("/sw.js", { scope: "/" }); } catch { // SW 注册失败不阻塞应用 } }; registerSW(); }, []); // OTel browser SDK 初始化(P6-1,条件启用:NEXT_PUBLIC_OTEL_ENABLED=true) useEffect(() => { if (typeof window === "undefined") return; if (process.env.NODE_ENV !== "production") return; void import("@/lib/observability/otel").then(({ initOTel }) => { void initOTel(); }); }, []); // MSW 未就绪时显示加载态(避免 mock 数据未注入时闪烁) if (!mswReady) { return (