heliumts 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,6 +8,8 @@ import type { MethodStub } from "./types.js";
8
8
  * window focus/visibility change and re-run the fetch.
9
9
  * - showLoaderOnRefocus: when false (default), refetches triggered by window
10
10
  * focus/visibility will update data silently without showing the loading state.
11
+ * - showLoaderOnInvalidate: when false (default), refetches triggered by cache
12
+ * invalidation will update data silently without showing the loading state.
11
13
  * - enabled: disable automatic fetching (defaults to true) — useful when
12
14
  * you only want to fetch when a required value (e.g. id) is present.
13
15
  */
@@ -15,6 +17,7 @@ export interface UseFetchOptions {
15
17
  ttl?: number;
16
18
  refetchOnWindowFocus?: boolean;
17
19
  showLoaderOnRefocus?: boolean;
20
+ showLoaderOnInvalidate?: boolean;
18
21
  enabled?: boolean;
19
22
  }
20
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"useFetch.d.ts","sourceRoot":"","sources":["../../src/client/useFetch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAIvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAsED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,eAAe;;;;;2BA0OvE,OAAO;EAGnD"}
1
+ {"version":3,"file":"useFetch.d.ts","sourceRoot":"","sources":["../../src/client/useFetch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAIvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,eAAe;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAsED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,eAAe;;;;;2BA4OvE,OAAO;EAGnD"}
@@ -69,7 +69,7 @@ function setupFocusListeners() {
69
69
  export function useFetch(method, args, options) {
70
70
  // Compute cache key
71
71
  const key = cacheKey(method.__id, args);
72
- const { ttl, refetchOnWindowFocus = true, showLoaderOnRefocus = false, enabled = true } = options ?? {};
72
+ const { ttl, refetchOnWindowFocus = true, showLoaderOnRefocus = false, showLoaderOnInvalidate = false, enabled = true } = options ?? {};
73
73
  // Use refs to store latest values without causing effect re-runs
74
74
  const methodIdRef = useRef(method.__id);
75
75
  const argsRef = useRef(args);
@@ -77,6 +77,7 @@ export function useFetch(method, args, options) {
77
77
  const ttlRef = useRef(ttl);
78
78
  const enabledRef = useRef(enabled);
79
79
  const showLoaderOnRefocusRef = useRef(showLoaderOnRefocus);
80
+ const showLoaderOnInvalidateRef = useRef(showLoaderOnInvalidate);
80
81
  // Update refs on each render
81
82
  methodIdRef.current = method.__id;
82
83
  argsRef.current = args;
@@ -84,6 +85,7 @@ export function useFetch(method, args, options) {
84
85
  ttlRef.current = ttl;
85
86
  enabledRef.current = enabled;
86
87
  showLoaderOnRefocusRef.current = showLoaderOnRefocus;
88
+ showLoaderOnInvalidateRef.current = showLoaderOnInvalidate;
87
89
  // Track if component is mounted
88
90
  const isMountedRef = useRef(true);
89
91
  const [data, setData] = useState(() => (has(key) ? get(key) : undefined));
@@ -238,7 +240,7 @@ export function useFetch(method, args, options) {
238
240
  useEffect(() => {
239
241
  const unsubscribe = subscribeInvalidations((methodId) => {
240
242
  if (methodId === methodIdRef.current && enabledRef.current && isMountedRef.current && !isPending(keyRef.current)) {
241
- doFetch(true);
243
+ doFetch(showLoaderOnInvalidateRef.current);
242
244
  }
243
245
  });
244
246
  return unsubscribe;
@@ -1 +1 @@
1
- {"version":3,"file":"useFetch.js","sourceRoot":"","sources":["../../src/client/useFetch.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type { RpcStats } from \"../runtime/protocol.js\";\nimport { cacheKey, get, getPendingFetch, has, isPending, set, setPendingFetch, subscribeInvalidations } from \"./cache.js\";\nimport { rpcCall } from \"./rpcClient.js\";\nimport { RpcError } from \"./RpcError.js\";\nimport type { MethodStub } from \"./types.js\";\n\n/**\n * Options controlling `useFetch` behaviour.\n *\n * - ttl: optional time-to-live for the cached response (milliseconds).\n * - refetchOnWindowFocus: when true the hook will invalidate cache on\n * window focus/visibility change and re-run the fetch.\n * - showLoaderOnRefocus: when false (default), refetches triggered by window\n * focus/visibility will update data silently without showing the loading state.\n * - enabled: disable automatic fetching (defaults to true) — useful when\n * you only want to fetch when a required value (e.g. id) is present.\n */\nexport interface UseFetchOptions {\n ttl?: number; // TTL in milliseconds\n refetchOnWindowFocus?: boolean; // Whether to refetch when tab becomes visible\n showLoaderOnRefocus?: boolean; // Whether to show loader when refetching on focus (defaults to false)\n enabled?: boolean; // Whether to fetch data. Defaults to true. Useful for conditional fetching (e.g., only fetch when an ID exists)\n}\n\n// Store focus refetch callbacks globally (survives HMR)\ntype FocusCallback = (showLoader: boolean) => void;\n\nfunction getFocusCallbacksSet(): Set<FocusCallback> {\n if (typeof window === \"undefined\") {\n return new Set();\n }\n const win = window as typeof window & {\n __heliumFocusCallbacks?: Set<FocusCallback>;\n };\n if (!win.__heliumFocusCallbacks) {\n win.__heliumFocusCallbacks = new Set();\n }\n return win.__heliumFocusCallbacks;\n}\n\nfunction getVisibilityState(): { registered: boolean; lastTrigger: number } {\n if (typeof window === \"undefined\") {\n return { registered: false, lastTrigger: 0 };\n }\n const win = window as typeof window & {\n __heliumVisibilityState?: { registered: boolean; lastTrigger: number };\n };\n if (!win.__heliumVisibilityState) {\n win.__heliumVisibilityState = { registered: false, lastTrigger: 0 };\n }\n return win.__heliumVisibilityState;\n}\n\n// Minimum time between focus-triggered refetches (debounce)\nconst FOCUS_DEBOUNCE_MS = 2000;\n\nfunction setupFocusListeners() {\n const state = getVisibilityState();\n if (state.registered || typeof document === \"undefined\") {\n return;\n }\n state.registered = true;\n\n const triggerRefetch = () => {\n const now = Date.now();\n // Debounce to prevent rapid refetches during HMR\n if (now - state.lastTrigger < FOCUS_DEBOUNCE_MS) {\n return;\n }\n state.lastTrigger = now;\n\n // Get all registered callbacks and call them\n const callbacks = getFocusCallbacksSet();\n callbacks.forEach((cb) => {\n try {\n cb(false); // Silent refetch on focus\n } catch {\n // Ignore errors from stale callbacks\n }\n });\n };\n\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n triggerRefetch();\n }\n };\n\n document.addEventListener(\"visibilitychange\", handleVisibilityChange, { passive: true });\n window.addEventListener(\"focus\", triggerRefetch, { passive: true });\n}\n\n/**\n * React hook for fetching and caching the result of a server method.\n *\n * @template TArgs - method argument type\n * @template TResult - expected return type\n * @param method - a MethodStub representing the server method to call\n * @param args - optional argument object passed to the server method\n * @param options - controls caching and refetch behavior (see UseFetchOptions)\n * @returns { data, isLoading, error, stats, refetch } — `data` is the cached or latest value; `refetch` triggers an immediate request\n */\nexport function useFetch<TArgs, TResult>(method: MethodStub<TArgs, TResult>, args?: TArgs, options?: UseFetchOptions) {\n // Compute cache key\n const key = cacheKey(method.__id, args);\n\n const { ttl, refetchOnWindowFocus = true, showLoaderOnRefocus = false, enabled = true } = options ?? {};\n\n // Use refs to store latest values without causing effect re-runs\n const methodIdRef = useRef(method.__id);\n const argsRef = useRef(args);\n const keyRef = useRef(key);\n const ttlRef = useRef(ttl);\n const enabledRef = useRef(enabled);\n const showLoaderOnRefocusRef = useRef(showLoaderOnRefocus);\n\n // Update refs on each render\n methodIdRef.current = method.__id;\n argsRef.current = args;\n keyRef.current = key;\n ttlRef.current = ttl;\n enabledRef.current = enabled;\n showLoaderOnRefocusRef.current = showLoaderOnRefocus;\n\n // Track if component is mounted\n const isMountedRef = useRef(true);\n\n const [data, setData] = useState<TResult | undefined>(() => (has(key) ? get<TResult>(key) : undefined));\n const [isLoading, setLoading] = useState(!has(key) && enabled);\n const [error, setError] = useState<string | null>(null);\n const [stats, setStats] = useState<RpcStats | null>(null);\n\n // Core fetch function using refs (stable reference)\n // Uses global deduplication to prevent multiple fetches for the same key\n const doFetch = useCallback(async (showLoader: boolean = true): Promise<TResult | undefined> => {\n if (!isMountedRef.current) {\n return undefined;\n }\n\n const currentKey = keyRef.current;\n\n // Check if there's already a pending fetch for this key (global deduplication)\n const existingFetch = getPendingFetch<{ data: TResult; stats: RpcStats }>(currentKey);\n if (existingFetch) {\n // Wait for the existing fetch and use its result\n if (showLoader) {\n setLoading(true);\n }\n try {\n const result = await existingFetch;\n if (isMountedRef.current) {\n setData(result.data);\n setStats(result.stats);\n setError(null);\n }\n return result.data;\n } catch (err: unknown) {\n if (isMountedRef.current) {\n const rpcError = err instanceof RpcError ? err : new RpcError(err instanceof Error ? err.message : \"Unknown error\");\n setError(rpcError.message);\n setStats(rpcError.stats);\n }\n return undefined;\n } finally {\n if (isMountedRef.current && showLoader) {\n setLoading(false);\n }\n }\n }\n\n if (showLoader) {\n setLoading(true);\n }\n setError(null);\n\n // Create the fetch promise and register it globally\n const fetchPromise = rpcCall<TResult, TArgs>(methodIdRef.current, argsRef.current as TArgs);\n const dedupedPromise = setPendingFetch(currentKey, fetchPromise);\n\n try {\n const result = await dedupedPromise;\n if (!isMountedRef.current) {\n return undefined;\n }\n set(currentKey, result.data, ttlRef.current);\n setData(result.data);\n setStats(result.stats);\n return result.data;\n } catch (err: unknown) {\n if (!isMountedRef.current) {\n return undefined;\n }\n const rpcError = err instanceof RpcError ? err : new RpcError(err instanceof Error ? err.message : \"Unknown error\");\n setError(rpcError.message);\n setStats(rpcError.stats);\n return undefined;\n } finally {\n if (isMountedRef.current && showLoader) {\n setLoading(false);\n }\n }\n }, []); // No dependencies - uses refs\n\n // Track mounted state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n // Sync data from cache when key changes\n useEffect(() => {\n if (has(key)) {\n const cachedData = get<TResult>(key);\n if (cachedData !== undefined) {\n setData(cachedData);\n setLoading(false);\n }\n }\n }, [key]);\n\n // Initial fetch on mount or when key/enabled changes\n useEffect(() => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n\n // Only fetch if not in cache and not already pending globally\n if (!has(key) && !isPending(key)) {\n doFetch(true);\n } else if (isPending(key)) {\n // There's a pending fetch - wait for it\n setLoading(true);\n const pendingFetch = getPendingFetch<{ data: TResult; stats: RpcStats }>(key);\n if (pendingFetch) {\n pendingFetch\n .then((result) => {\n if (isMountedRef.current) {\n setData(result.data);\n setStats(result.stats);\n setError(null);\n }\n })\n .catch((err: unknown) => {\n if (isMountedRef.current) {\n const rpcError = err instanceof RpcError ? err : new RpcError(err instanceof Error ? err.message : \"Unknown error\");\n setError(rpcError.message);\n setStats(rpcError.stats);\n }\n })\n .finally(() => {\n if (isMountedRef.current) {\n setLoading(false);\n }\n });\n }\n }\n }, [key, enabled, doFetch]);\n\n // Register for focus/visibility refetch\n useEffect(() => {\n if (!refetchOnWindowFocus) {\n return;\n }\n\n // Setup global focus listeners once\n setupFocusListeners();\n\n // Create a stable callback for this hook instance\n const focusCallback: FocusCallback = (showLoader: boolean) => {\n if (enabledRef.current && isMountedRef.current && !isPending(keyRef.current)) {\n doFetch(showLoaderOnRefocusRef.current || showLoader);\n }\n };\n\n // Register this callback\n const callbacks = getFocusCallbacksSet();\n callbacks.add(focusCallback);\n\n return () => {\n callbacks.delete(focusCallback);\n };\n }, [refetchOnWindowFocus, doFetch]);\n\n // Subscribe to cache invalidations (from useCall or manual invalidation)\n useEffect(() => {\n const unsubscribe = subscribeInvalidations((methodId) => {\n if (methodId === methodIdRef.current && enabledRef.current && isMountedRef.current && !isPending(keyRef.current)) {\n doFetch(true);\n }\n });\n\n return unsubscribe;\n }, [doFetch]);\n\n // TTL-based auto-refetch\n useEffect(() => {\n if (!enabled || !ttl) {\n return;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let isActive = true;\n\n const scheduleRefetch = () => {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n\n timeoutId = setTimeout(async () => {\n if (!isActive || !enabledRef.current || !isMountedRef.current) {\n return;\n }\n await doFetch(false); // Silent refetch for TTL\n if (isActive) {\n scheduleRefetch();\n }\n }, ttl);\n };\n\n // Only schedule if data is already cached\n if (has(key)) {\n scheduleRefetch();\n }\n\n return () => {\n isActive = false;\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n };\n }, [key, ttl, enabled, doFetch]);\n\n // Public refetch function\n const refetch = useCallback((showLoader: boolean = true) => doFetch(showLoader), [doFetch]);\n\n return { data, isLoading, error, stats, refetch };\n}\n"]}
1
+ {"version":3,"file":"useFetch.js","sourceRoot":"","sources":["../../src/client/useFetch.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type { RpcStats } from \"../runtime/protocol.js\";\nimport { cacheKey, get, getPendingFetch, has, isPending, set, setPendingFetch, subscribeInvalidations } from \"./cache.js\";\nimport { rpcCall } from \"./rpcClient.js\";\nimport { RpcError } from \"./RpcError.js\";\nimport type { MethodStub } from \"./types.js\";\n\n/**\n * Options controlling `useFetch` behaviour.\n *\n * - ttl: optional time-to-live for the cached response (milliseconds).\n * - refetchOnWindowFocus: when true the hook will invalidate cache on\n * window focus/visibility change and re-run the fetch.\n * - showLoaderOnRefocus: when false (default), refetches triggered by window\n * focus/visibility will update data silently without showing the loading state.\n * - showLoaderOnInvalidate: when false (default), refetches triggered by cache\n * invalidation will update data silently without showing the loading state.\n * - enabled: disable automatic fetching (defaults to true) — useful when\n * you only want to fetch when a required value (e.g. id) is present.\n */\nexport interface UseFetchOptions {\n ttl?: number; // TTL in milliseconds\n refetchOnWindowFocus?: boolean; // Whether to refetch when tab becomes visible\n showLoaderOnRefocus?: boolean; // Whether to show loader when refetching on focus (defaults to false)\n showLoaderOnInvalidate?: boolean; // Whether to show loader when refetching on cache invalidation (defaults to false)\n enabled?: boolean; // Whether to fetch data. Defaults to true. Useful for conditional fetching (e.g., only fetch when an ID exists)\n}\n\n// Store focus refetch callbacks globally (survives HMR)\ntype FocusCallback = (showLoader: boolean) => void;\n\nfunction getFocusCallbacksSet(): Set<FocusCallback> {\n if (typeof window === \"undefined\") {\n return new Set();\n }\n const win = window as typeof window & {\n __heliumFocusCallbacks?: Set<FocusCallback>;\n };\n if (!win.__heliumFocusCallbacks) {\n win.__heliumFocusCallbacks = new Set();\n }\n return win.__heliumFocusCallbacks;\n}\n\nfunction getVisibilityState(): { registered: boolean; lastTrigger: number } {\n if (typeof window === \"undefined\") {\n return { registered: false, lastTrigger: 0 };\n }\n const win = window as typeof window & {\n __heliumVisibilityState?: { registered: boolean; lastTrigger: number };\n };\n if (!win.__heliumVisibilityState) {\n win.__heliumVisibilityState = { registered: false, lastTrigger: 0 };\n }\n return win.__heliumVisibilityState;\n}\n\n// Minimum time between focus-triggered refetches (debounce)\nconst FOCUS_DEBOUNCE_MS = 2000;\n\nfunction setupFocusListeners() {\n const state = getVisibilityState();\n if (state.registered || typeof document === \"undefined\") {\n return;\n }\n state.registered = true;\n\n const triggerRefetch = () => {\n const now = Date.now();\n // Debounce to prevent rapid refetches during HMR\n if (now - state.lastTrigger < FOCUS_DEBOUNCE_MS) {\n return;\n }\n state.lastTrigger = now;\n\n // Get all registered callbacks and call them\n const callbacks = getFocusCallbacksSet();\n callbacks.forEach((cb) => {\n try {\n cb(false); // Silent refetch on focus\n } catch {\n // Ignore errors from stale callbacks\n }\n });\n };\n\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n triggerRefetch();\n }\n };\n\n document.addEventListener(\"visibilitychange\", handleVisibilityChange, { passive: true });\n window.addEventListener(\"focus\", triggerRefetch, { passive: true });\n}\n\n/**\n * React hook for fetching and caching the result of a server method.\n *\n * @template TArgs - method argument type\n * @template TResult - expected return type\n * @param method - a MethodStub representing the server method to call\n * @param args - optional argument object passed to the server method\n * @param options - controls caching and refetch behavior (see UseFetchOptions)\n * @returns { data, isLoading, error, stats, refetch } — `data` is the cached or latest value; `refetch` triggers an immediate request\n */\nexport function useFetch<TArgs, TResult>(method: MethodStub<TArgs, TResult>, args?: TArgs, options?: UseFetchOptions) {\n // Compute cache key\n const key = cacheKey(method.__id, args);\n\n const { ttl, refetchOnWindowFocus = true, showLoaderOnRefocus = false, showLoaderOnInvalidate = false, enabled = true } = options ?? {};\n\n // Use refs to store latest values without causing effect re-runs\n const methodIdRef = useRef(method.__id);\n const argsRef = useRef(args);\n const keyRef = useRef(key);\n const ttlRef = useRef(ttl);\n const enabledRef = useRef(enabled);\n const showLoaderOnRefocusRef = useRef(showLoaderOnRefocus);\n const showLoaderOnInvalidateRef = useRef(showLoaderOnInvalidate);\n\n // Update refs on each render\n methodIdRef.current = method.__id;\n argsRef.current = args;\n keyRef.current = key;\n ttlRef.current = ttl;\n enabledRef.current = enabled;\n showLoaderOnRefocusRef.current = showLoaderOnRefocus;\n showLoaderOnInvalidateRef.current = showLoaderOnInvalidate;\n\n // Track if component is mounted\n const isMountedRef = useRef(true);\n\n const [data, setData] = useState<TResult | undefined>(() => (has(key) ? get<TResult>(key) : undefined));\n const [isLoading, setLoading] = useState(!has(key) && enabled);\n const [error, setError] = useState<string | null>(null);\n const [stats, setStats] = useState<RpcStats | null>(null);\n\n // Core fetch function using refs (stable reference)\n // Uses global deduplication to prevent multiple fetches for the same key\n const doFetch = useCallback(async (showLoader: boolean = true): Promise<TResult | undefined> => {\n if (!isMountedRef.current) {\n return undefined;\n }\n\n const currentKey = keyRef.current;\n\n // Check if there's already a pending fetch for this key (global deduplication)\n const existingFetch = getPendingFetch<{ data: TResult; stats: RpcStats }>(currentKey);\n if (existingFetch) {\n // Wait for the existing fetch and use its result\n if (showLoader) {\n setLoading(true);\n }\n try {\n const result = await existingFetch;\n if (isMountedRef.current) {\n setData(result.data);\n setStats(result.stats);\n setError(null);\n }\n return result.data;\n } catch (err: unknown) {\n if (isMountedRef.current) {\n const rpcError = err instanceof RpcError ? err : new RpcError(err instanceof Error ? err.message : \"Unknown error\");\n setError(rpcError.message);\n setStats(rpcError.stats);\n }\n return undefined;\n } finally {\n if (isMountedRef.current && showLoader) {\n setLoading(false);\n }\n }\n }\n\n if (showLoader) {\n setLoading(true);\n }\n setError(null);\n\n // Create the fetch promise and register it globally\n const fetchPromise = rpcCall<TResult, TArgs>(methodIdRef.current, argsRef.current as TArgs);\n const dedupedPromise = setPendingFetch(currentKey, fetchPromise);\n\n try {\n const result = await dedupedPromise;\n if (!isMountedRef.current) {\n return undefined;\n }\n set(currentKey, result.data, ttlRef.current);\n setData(result.data);\n setStats(result.stats);\n return result.data;\n } catch (err: unknown) {\n if (!isMountedRef.current) {\n return undefined;\n }\n const rpcError = err instanceof RpcError ? err : new RpcError(err instanceof Error ? err.message : \"Unknown error\");\n setError(rpcError.message);\n setStats(rpcError.stats);\n return undefined;\n } finally {\n if (isMountedRef.current && showLoader) {\n setLoading(false);\n }\n }\n }, []); // No dependencies - uses refs\n\n // Track mounted state\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n };\n }, []);\n\n // Sync data from cache when key changes\n useEffect(() => {\n if (has(key)) {\n const cachedData = get<TResult>(key);\n if (cachedData !== undefined) {\n setData(cachedData);\n setLoading(false);\n }\n }\n }, [key]);\n\n // Initial fetch on mount or when key/enabled changes\n useEffect(() => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n\n // Only fetch if not in cache and not already pending globally\n if (!has(key) && !isPending(key)) {\n doFetch(true);\n } else if (isPending(key)) {\n // There's a pending fetch - wait for it\n setLoading(true);\n const pendingFetch = getPendingFetch<{ data: TResult; stats: RpcStats }>(key);\n if (pendingFetch) {\n pendingFetch\n .then((result) => {\n if (isMountedRef.current) {\n setData(result.data);\n setStats(result.stats);\n setError(null);\n }\n })\n .catch((err: unknown) => {\n if (isMountedRef.current) {\n const rpcError = err instanceof RpcError ? err : new RpcError(err instanceof Error ? err.message : \"Unknown error\");\n setError(rpcError.message);\n setStats(rpcError.stats);\n }\n })\n .finally(() => {\n if (isMountedRef.current) {\n setLoading(false);\n }\n });\n }\n }\n }, [key, enabled, doFetch]);\n\n // Register for focus/visibility refetch\n useEffect(() => {\n if (!refetchOnWindowFocus) {\n return;\n }\n\n // Setup global focus listeners once\n setupFocusListeners();\n\n // Create a stable callback for this hook instance\n const focusCallback: FocusCallback = (showLoader: boolean) => {\n if (enabledRef.current && isMountedRef.current && !isPending(keyRef.current)) {\n doFetch(showLoaderOnRefocusRef.current || showLoader);\n }\n };\n\n // Register this callback\n const callbacks = getFocusCallbacksSet();\n callbacks.add(focusCallback);\n\n return () => {\n callbacks.delete(focusCallback);\n };\n }, [refetchOnWindowFocus, doFetch]);\n\n // Subscribe to cache invalidations (from useCall or manual invalidation)\n useEffect(() => {\n const unsubscribe = subscribeInvalidations((methodId) => {\n if (methodId === methodIdRef.current && enabledRef.current && isMountedRef.current && !isPending(keyRef.current)) {\n doFetch(showLoaderOnInvalidateRef.current);\n }\n });\n\n return unsubscribe;\n }, [doFetch]);\n\n // TTL-based auto-refetch\n useEffect(() => {\n if (!enabled || !ttl) {\n return;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let isActive = true;\n\n const scheduleRefetch = () => {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n\n timeoutId = setTimeout(async () => {\n if (!isActive || !enabledRef.current || !isMountedRef.current) {\n return;\n }\n await doFetch(false); // Silent refetch for TTL\n if (isActive) {\n scheduleRefetch();\n }\n }, ttl);\n };\n\n // Only schedule if data is already cached\n if (has(key)) {\n scheduleRefetch();\n }\n\n return () => {\n isActive = false;\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n };\n }, [key, ttl, enabled, doFetch]);\n\n // Public refetch function\n const refetch = useCallback((showLoader: boolean = true) => doFetch(showLoader), [doFetch]);\n\n return { data, isLoading, error, stats, refetch };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heliumts",
3
- "version": "0.4.5",
3
+ "version": "0.5.0",
4
4
  "description": "A lightweight full-stack React framework with file-based routing, RPC, and SSG support",
5
5
  "keywords": [
6
6
  "react",