import { onDestroy } from 'svelte' import { writable, derived } from 'svelte/store' import { createSWR } from '../../core' import type { SWRConfig, SWRStores, SWRStore, Key, Fetcher, SWRFetcher } from './types' // Global SWR instance const globalSWR = createSWR() /** * Enhanced useSWR store for Svelte */ export function useSWR( key: Key | null, fetcher?: SWRFetcher | null, config?: Partial> ): SWRStores { // Get config from context (using a simple object for now) const contextConfig = {} as Record const mergedConfig = { ...contextConfig, ...config } // Update config if provided if (mergedConfig) { globalSWR.updateConfig(mergedConfig as any) } // Initial state logic let initialData: Data | undefined = undefined let initialError: Error | undefined = undefined let initialIsLoading = false let initialIsValidating = false if (key) { const entry = globalSWR.get(key) if (entry) { initialData = entry.data as Data initialError = entry.error as Error initialIsLoading = entry.isLoading initialIsValidating = entry.isValidating } } // Create base stores const dataStore = writable(initialData) const errorStore = writable(initialError) const isLoadingStore = writable(initialIsLoading) const isValidatingStore = writable(initialIsValidating) // Subscribe to SWR changes let unsubscribe: (() => void) | undefined const normalizedFetcher = fetcher ? ( fetcher.length === 2 ? fetcher as Fetcher : (k: Key, options: unknown) => { if (Array.isArray(k)) { return (fetcher as (...args: unknown[]) => Promise)(...k) } return (fetcher as (arg: unknown) => Promise)(k) } ) : null if (key) { unsubscribe = globalSWR.subscribe(key, (newData, newError, newIsValidating, newIsLoading) => { dataStore.set(newData as Data) errorStore.set(newError as Error) isValidatingStore.set(newIsValidating ?? false) isLoadingStore.set(newIsLoading ?? false) }, normalizedFetcher as Fetcher) } // Cleanup on destroy if inside a component try { onDestroy(() => { if (unsubscribe) unsubscribe() }) } catch (e) { // Ignore error if called outside component initialization // (e.g. in tests without a component context) } // Create mutate function const mutate = (mutateData?: Data | Promise | ((currentData?: Data) => Data | Promise), options?: unknown) => { if (!key) return Promise.resolve(undefined) return globalSWR.mutate(key, mutateData, options as any) } return { data: { subscribe: dataStore.subscribe }, error: { subscribe: errorStore.subscribe }, isLoading: { subscribe: isLoadingStore.subscribe }, isValidating: { subscribe: isValidatingStore.subscribe }, loading: { subscribe: isLoadingStore.subscribe }, validating: { subscribe: isValidatingStore.subscribe }, mutate: mutate as any } } /** * Enhanced useSWR store with all state combined */ export function useSWRStore( key: Key | null, fetcher?: SWRFetcher | null, config?: Partial> ): SWRStore { const stores = useSWR(key, fetcher, config) let initialData: Data | undefined; let initialError: Error | undefined; let initialIsLoading = false; let initialIsValidating = false; stores.data.subscribe(v => initialData = v)(); stores.error.subscribe(v => initialError = v)(); stores.isLoading.subscribe(v => initialIsLoading = v)(); stores.isValidating.subscribe(v => initialIsValidating = v)(); const combinedStore = writable<{ data?: Data error?: Error isLoading: boolean isValidating: boolean loading: boolean validating: boolean }>({ data: initialData, error: initialError, isLoading: initialIsLoading, isValidating: initialIsValidating, loading: initialIsLoading, validating: initialIsValidating }) // Update combined store when individual stores change const unsubscribeData = stores.data.subscribe((data: any) => { combinedStore.update((state: any) => ({ ...state, data })) }) const unsubscribeError = stores.error.subscribe((error: any) => { combinedStore.update((state: any) => ({ ...state, error })) }) const unsubscribeLoading = stores.isLoading.subscribe((isLoading: any) => { combinedStore.update((state: any) => ({ ...state, isLoading, loading: isLoading })) }) const unsubscribeValidating = stores.isValidating.subscribe((isValidating) => { combinedStore.update((state) => ({ ...state, isValidating, validating: isValidating })) }) // Cleanup on destroy try { onDestroy(() => { unsubscribeData() unsubscribeError() unsubscribeLoading() unsubscribeValidating() }) } catch (e) { // Ignore } return { ...combinedStore, mutate: stores.mutate } } /** * Hook for manual mutation */ export function useSWRMutate() { return (key: Key, data?: Data | Promise | ((currentData?: Data) => Data | Promise), options?: unknown) => { return globalSWR.mutate(key, data, options as any) } } /** * Hook for accessing global SWR instance */ export function useSWRInstance() { return globalSWR } /** * Hook for prefetching data */ export function useSWRPrefetch() { return (key: Key, fetcher: Fetcher) => { return globalSWR.revalidate(key, { dedupe: true }) } } export default useSWR