"use client"; import { useMemo } from "react"; import { ApiClient } from "../api/client"; import { setApiInstance } from "../api"; import { createAuthStore, registerAuthStore } from "../auth"; import { createChatStore, registerChatStore } from "../chat"; import { WSProvider } from "../realtime"; import { QueryProvider } from "../provider"; import { createLogger } from "../logger"; import { defaultStorage } from "./storage"; import { AuthInitializer } from "./auth-initializer"; import type { CoreProviderProps } from "./types"; import type { StorageAdapter } from "../types/storage"; // Module-level singletons — created once at first render, never recreated. // Vite HMR preserves module-level state, so these survive hot reloads. let initialized = false; let authStore: ReturnType; let chatStore: ReturnType; function initCore( apiBaseUrl: string, storage: StorageAdapter, onLogin?: () => void, onLogout?: () => void, cookieAuth?: boolean, ) { if (initialized) return; const api = new ApiClient(apiBaseUrl, { logger: createLogger("api"), onUnauthorized: () => { storage.removeItem("multica_token"); }, }); setApiInstance(api); // In token mode, hydrate token from storage. if (!cookieAuth) { const token = storage.getItem("multica_token"); if (token) api.setToken(token); } // Workspace identity is URL-driven: the [workspaceSlug] layout resolves // the slug and calls setCurrentWorkspace(slug, wsId) on mount. The api // client reads the slug from that singleton for the X-Workspace-Slug // header. No boot-time hydration from storage is required. authStore = createAuthStore({ api, storage, onLogin, onLogout, cookieAuth }); registerAuthStore(authStore); chatStore = createChatStore({ storage }); registerChatStore(chatStore); initialized = true; } export function CoreProvider({ children, apiBaseUrl = "", wsUrl = "ws://localhost:8080/ws", storage = defaultStorage, cookieAuth, onLogin, onLogout, }: CoreProviderProps) { // Initialize singletons on first render only. Dependencies are read-once: // apiBaseUrl, storage, and callbacks are set at app boot and never change at runtime. // eslint-disable-next-line react-hooks/exhaustive-deps useMemo(() => initCore(apiBaseUrl, storage, onLogin, onLogout, cookieAuth), []); return ( {children} ); }