import { ConvexHttpClient } from 'convex/browser'; import { getContext, setContext } from 'svelte'; import type { FunctionReference, FunctionArgs, FunctionReturnType } from 'convex/server'; const POLLING_CONTEXT_KEY = 'convex-polling'; const POLL_INTERVAL = 1000; type PollingContext = { client: ConvexHttpClient; }; export function hasWebSocketSupport(): boolean { if (typeof window === 'undefined') return true; try { return 'WebSocket' in window && typeof WebSocket !== 'undefined'; } catch { return false; } } export function setupPollingConvex(url: string): void { const client = new ConvexHttpClient(url); setContext(POLLING_CONTEXT_KEY, { client }); } export function usePollingClient(): ConvexHttpClient { const ctx = getContext(POLLING_CONTEXT_KEY); if (!ctx) { throw new Error('Convex polling client not set up. Call setupPollingConvex first.'); } return ctx.client; } type QueryState = { data: T | undefined; error: Error | null; isLoading: boolean; }; export function usePollingMutation>( mutation: Mutation ): (args: FunctionArgs) => Promise> { const client = usePollingClient(); return (args: FunctionArgs) => client.mutation(mutation, args); } export function usePollingQuery>( query: Query, argsGetter: () => FunctionArgs | 'skip' ): { data: FunctionReturnType | undefined; error: Error | null; isLoading: boolean; } { const client = usePollingClient(); // eslint-disable-next-line prefer-const let state = $state>>({ data: undefined, error: null, isLoading: true }); let intervalId: ReturnType | null = null; let lastArgsJson = ''; async function poll() { const args = argsGetter(); if (args === 'skip') { state.isLoading = false; return; } const argsJson = JSON.stringify(args); if (argsJson !== lastArgsJson) { state.isLoading = true; lastArgsJson = argsJson; } try { const result = await client.query(query, args); state.data = result; state.error = null; state.isLoading = false; } catch (err) { state.error = err instanceof Error ? err : new Error(String(err)); state.isLoading = false; } } $effect(() => { poll(); intervalId = setInterval(poll, POLL_INTERVAL); return () => { if (intervalId) { clearInterval(intervalId); } }; }); return { get data() { return state.data; }, get error() { return state.error; }, get isLoading() { return state.isLoading; } }; }