heliumts 0.5.9 → 0.5.11

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.
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/client/cache.ts"],"names":[],"mappings":"AAqCA,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAEhE;AASD,wBAAgB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAYjD;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,QAM5D;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAYxC;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,QAalD;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,cAK1E;AAED,wBAAgB,aAAa,SAkB5B;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAEtE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAW/E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEnD"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/client/cache.ts"],"names":[],"mappings":"AAqCA,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAEhE;AAsBD,wBAAgB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAYjD;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,QAM5D;AAED,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAYxC;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,QAqBlD;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,cAK1E;AAED,wBAAgB,aAAa,SAkB5B;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAEtE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAW/E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEnD"}
@@ -26,6 +26,19 @@ const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
26
26
  export function cacheKey(methodId, args) {
27
27
  return JSON.stringify([methodId, args ?? null]);
28
28
  }
29
+ function methodIdFromKey(key) {
30
+ try {
31
+ const parsed = JSON.parse(key);
32
+ if (!Array.isArray(parsed) || parsed.length < 1) {
33
+ return undefined;
34
+ }
35
+ const [methodId] = parsed;
36
+ return typeof methodId === "string" ? methodId : undefined;
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
29
42
  function isExpired(entry) {
30
43
  if (!entry.ttl) {
31
44
  return false;
@@ -64,11 +77,17 @@ export function has(key) {
64
77
  export function invalidateByMethod(methodId) {
65
78
  let invalidated = false;
66
79
  for (const key of store.keys()) {
67
- if (key.startsWith(`["${methodId}"`)) {
80
+ if (methodIdFromKey(key) === methodId) {
68
81
  store.delete(key);
69
82
  invalidated = true;
70
83
  }
71
84
  }
85
+ for (const key of pendingFetches.keys()) {
86
+ if (methodIdFromKey(key) === methodId) {
87
+ pendingFetches.delete(key);
88
+ invalidated = true;
89
+ }
90
+ }
72
91
  if (invalidated || listeners.size) {
73
92
  for (const listener of listeners) {
74
93
  listener(methodId);
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/client/cache.ts"],"names":[],"mappings":"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","sourcesContent":["interface CacheEntry {\n value: unknown;\n timestamp: number;\n ttl?: number; // TTL in milliseconds\n}\n\n// Preserve cache across HMR by attaching to window in dev mode\nlet store: Map<string, CacheEntry>;\nlet listeners: Set<(methodId: string) => void>;\nlet pendingFetches: Map<string, Promise<unknown>>;\n\nif (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n const globalWindow = window as typeof window & {\n __heliumCacheStore?: Map<string, CacheEntry>;\n __heliumCacheListeners?: Set<(methodId: string) => void>;\n __heliumPendingFetches?: Map<string, Promise<unknown>>;\n };\n if (!globalWindow.__heliumCacheStore) {\n globalWindow.__heliumCacheStore = new Map();\n }\n if (!globalWindow.__heliumCacheListeners) {\n globalWindow.__heliumCacheListeners = new Set();\n }\n if (!globalWindow.__heliumPendingFetches) {\n globalWindow.__heliumPendingFetches = new Map();\n }\n store = globalWindow.__heliumCacheStore;\n listeners = globalWindow.__heliumCacheListeners;\n pendingFetches = globalWindow.__heliumPendingFetches;\n} else {\n store = new Map();\n listeners = new Set();\n pendingFetches = new Map();\n}\n\nconst DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes\n\nexport function cacheKey(methodId: string, args: unknown): string {\n return JSON.stringify([methodId, args ?? null]);\n}\n\nfunction isExpired(entry: CacheEntry): boolean {\n if (!entry.ttl) {\n return false;\n }\n return Date.now() - entry.timestamp > entry.ttl;\n}\n\nexport function get<T>(key: string): T | undefined {\n const entry = store.get(key);\n if (!entry) {\n return undefined;\n }\n\n if (isExpired(entry)) {\n store.delete(key);\n return undefined;\n }\n\n return entry.value as T | undefined;\n}\n\nexport function set(key: string, value: unknown, ttl?: number) {\n store.set(key, {\n value,\n timestamp: Date.now(),\n ttl: ttl ?? DEFAULT_TTL,\n });\n}\n\nexport function has(key: string): boolean {\n const entry = store.get(key);\n if (!entry) {\n return false;\n }\n\n if (isExpired(entry)) {\n store.delete(key);\n return false;\n }\n\n return true;\n}\n\nexport function invalidateByMethod(methodId: string) {\n let invalidated = false;\n for (const key of store.keys()) {\n if (key.startsWith(`[\"${methodId}\"`)) {\n store.delete(key);\n invalidated = true;\n }\n }\n if (invalidated || listeners.size) {\n for (const listener of listeners) {\n listener(methodId);\n }\n }\n}\n\nexport function subscribeInvalidations(listener: (methodId: string) => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function invalidateAll() {\n const methodIds = new Set<string>();\n for (const key of store.keys()) {\n try {\n const parsed = JSON.parse(key);\n if (Array.isArray(parsed) && parsed.length > 0) {\n methodIds.add(parsed[0]);\n }\n } catch {\n // Ignore malformed keys\n }\n }\n store.clear();\n for (const methodId of methodIds) {\n for (const listener of listeners) {\n listener(methodId);\n }\n }\n}\n\n/**\n * Check if a fetch is currently pending for a given cache key.\n */\nexport function isPending(key: string): boolean {\n return pendingFetches.has(key);\n}\n\n/**\n * Get an existing pending fetch promise, or undefined if none.\n */\nexport function getPendingFetch<T>(key: string): Promise<T> | undefined {\n return pendingFetches.get(key) as Promise<T> | undefined;\n}\n\n/**\n * Register a pending fetch. Returns the promise.\n * If a fetch for this key is already pending, returns the existing promise.\n */\nexport function setPendingFetch<T>(key: string, promise: Promise<T>): Promise<T> {\n const existing = pendingFetches.get(key);\n if (existing) {\n return existing as Promise<T>;\n }\n pendingFetches.set(key, promise);\n // Clean up when done\n promise.finally(() => {\n pendingFetches.delete(key);\n });\n return promise;\n}\n\n/**\n * Clear a pending fetch (if you need to cancel/reset).\n */\nexport function clearPendingFetch(key: string): void {\n pendingFetches.delete(key);\n}\n"]}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/client/cache.ts"],"names":[],"mappings":"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","sourcesContent":["interface CacheEntry {\n value: unknown;\n timestamp: number;\n ttl?: number; // TTL in milliseconds\n}\n\n// Preserve cache across HMR by attaching to window in dev mode\nlet store: Map<string, CacheEntry>;\nlet listeners: Set<(methodId: string) => void>;\nlet pendingFetches: Map<string, Promise<unknown>>;\n\nif (typeof window !== \"undefined\" && import.meta.env?.DEV) {\n const globalWindow = window as typeof window & {\n __heliumCacheStore?: Map<string, CacheEntry>;\n __heliumCacheListeners?: Set<(methodId: string) => void>;\n __heliumPendingFetches?: Map<string, Promise<unknown>>;\n };\n if (!globalWindow.__heliumCacheStore) {\n globalWindow.__heliumCacheStore = new Map();\n }\n if (!globalWindow.__heliumCacheListeners) {\n globalWindow.__heliumCacheListeners = new Set();\n }\n if (!globalWindow.__heliumPendingFetches) {\n globalWindow.__heliumPendingFetches = new Map();\n }\n store = globalWindow.__heliumCacheStore;\n listeners = globalWindow.__heliumCacheListeners;\n pendingFetches = globalWindow.__heliumPendingFetches;\n} else {\n store = new Map();\n listeners = new Set();\n pendingFetches = new Map();\n}\n\nconst DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes\n\nexport function cacheKey(methodId: string, args: unknown): string {\n return JSON.stringify([methodId, args ?? null]);\n}\n\nfunction methodIdFromKey(key: string): string | undefined {\n try {\n const parsed = JSON.parse(key);\n if (!Array.isArray(parsed) || parsed.length < 1) {\n return undefined;\n }\n const [methodId] = parsed;\n return typeof methodId === \"string\" ? methodId : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction isExpired(entry: CacheEntry): boolean {\n if (!entry.ttl) {\n return false;\n }\n return Date.now() - entry.timestamp > entry.ttl;\n}\n\nexport function get<T>(key: string): T | undefined {\n const entry = store.get(key);\n if (!entry) {\n return undefined;\n }\n\n if (isExpired(entry)) {\n store.delete(key);\n return undefined;\n }\n\n return entry.value as T | undefined;\n}\n\nexport function set(key: string, value: unknown, ttl?: number) {\n store.set(key, {\n value,\n timestamp: Date.now(),\n ttl: ttl ?? DEFAULT_TTL,\n });\n}\n\nexport function has(key: string): boolean {\n const entry = store.get(key);\n if (!entry) {\n return false;\n }\n\n if (isExpired(entry)) {\n store.delete(key);\n return false;\n }\n\n return true;\n}\n\nexport function invalidateByMethod(methodId: string) {\n let invalidated = false;\n for (const key of store.keys()) {\n if (methodIdFromKey(key) === methodId) {\n store.delete(key);\n invalidated = true;\n }\n }\n\n for (const key of pendingFetches.keys()) {\n if (methodIdFromKey(key) === methodId) {\n pendingFetches.delete(key);\n invalidated = true;\n }\n }\n\n if (invalidated || listeners.size) {\n for (const listener of listeners) {\n listener(methodId);\n }\n }\n}\n\nexport function subscribeInvalidations(listener: (methodId: string) => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\nexport function invalidateAll() {\n const methodIds = new Set<string>();\n for (const key of store.keys()) {\n try {\n const parsed = JSON.parse(key);\n if (Array.isArray(parsed) && parsed.length > 0) {\n methodIds.add(parsed[0]);\n }\n } catch {\n // Ignore malformed keys\n }\n }\n store.clear();\n for (const methodId of methodIds) {\n for (const listener of listeners) {\n listener(methodId);\n }\n }\n}\n\n/**\n * Check if a fetch is currently pending for a given cache key.\n */\nexport function isPending(key: string): boolean {\n return pendingFetches.has(key);\n}\n\n/**\n * Get an existing pending fetch promise, or undefined if none.\n */\nexport function getPendingFetch<T>(key: string): Promise<T> | undefined {\n return pendingFetches.get(key) as Promise<T> | undefined;\n}\n\n/**\n * Register a pending fetch. Returns the promise.\n * If a fetch for this key is already pending, returns the existing promise.\n */\nexport function setPendingFetch<T>(key: string, promise: Promise<T>): Promise<T> {\n const existing = pendingFetches.get(key);\n if (existing) {\n return existing as Promise<T>;\n }\n pendingFetches.set(key, promise);\n // Clean up when done\n promise.finally(() => {\n pendingFetches.delete(key);\n });\n return promise;\n}\n\n/**\n * Clear a pending fetch (if you need to cancel/reset).\n */\nexport function clearPendingFetch(key: string): void {\n pendingFetches.delete(key);\n}\n"]}
@@ -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;;;;;;;;;;;;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;;;;;2BA+QvE,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;;;;;2BAoRvE,OAAO;EAGnD"}
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- import { cacheKey, get, getPendingFetch, has, isPending, set, setPendingFetch, subscribeInvalidations } from "./cache.js";
2
+ import { cacheKey, clearPendingFetch, get, getPendingFetch, has, isPending, set, setPendingFetch, subscribeInvalidations } from "./cache.js";
3
3
  import { rpcCall } from "./rpcClient.js";
4
4
  import { RpcError } from "./RpcError.js";
5
5
  function getFocusCallbacksSet() {
@@ -104,6 +104,21 @@ export function useFetch(method, args, options) {
104
104
  if (!isMountedRef.current) {
105
105
  return undefined;
106
106
  }
107
+ const replayQueuedRefetch = () => {
108
+ if (!queuedRefetchRef.current || !isMountedRef.current || !enabledRef.current) {
109
+ return;
110
+ }
111
+ const nextShowLoader = queuedRefetchShowLoaderRef.current;
112
+ queuedRefetchRef.current = false;
113
+ queuedRefetchShowLoaderRef.current = false;
114
+ clearPendingFetch(keyRef.current);
115
+ queueMicrotask(() => {
116
+ if (!isMountedRef.current || !enabledRef.current) {
117
+ return;
118
+ }
119
+ void doFetch(nextShowLoader);
120
+ });
121
+ };
107
122
  const currentKey = keyRef.current;
108
123
  // Check if there's already a pending fetch for this key (global deduplication)
109
124
  const existingFetch = getPendingFetch(currentKey);
@@ -133,12 +148,7 @@ export function useFetch(method, args, options) {
133
148
  if (isMountedRef.current && showLoader) {
134
149
  setLoading(false);
135
150
  }
136
- if (queuedRefetchRef.current && isMountedRef.current && enabledRef.current && !isPending(keyRef.current)) {
137
- const nextShowLoader = queuedRefetchShowLoaderRef.current;
138
- queuedRefetchRef.current = false;
139
- queuedRefetchShowLoaderRef.current = false;
140
- void doFetch(nextShowLoader);
141
- }
151
+ replayQueuedRefetch();
142
152
  }
143
153
  }
144
154
  if (showLoader) {
@@ -171,12 +181,7 @@ export function useFetch(method, args, options) {
171
181
  if (isMountedRef.current && showLoader) {
172
182
  setLoading(false);
173
183
  }
174
- if (queuedRefetchRef.current && isMountedRef.current && enabledRef.current && !isPending(keyRef.current)) {
175
- const nextShowLoader = queuedRefetchShowLoaderRef.current;
176
- queuedRefetchRef.current = false;
177
- queuedRefetchShowLoaderRef.current = false;
178
- void doFetch(nextShowLoader);
179
- }
184
+ replayQueuedRefetch();
180
185
  }
181
186
  }, []); // No dependencies - uses refs
182
187
  // Track mounted state
@@ -249,6 +254,7 @@ export function useFetch(method, args, options) {
249
254
  const shouldShowLoader = showLoaderOnRefocusRef.current || showLoader;
250
255
  if (isPending(keyRef.current)) {
251
256
  queueRefetch(shouldShowLoader);
257
+ void doFetch(shouldShowLoader);
252
258
  return;
253
259
  }
254
260
  void doFetch(shouldShowLoader);
@@ -268,6 +274,7 @@ export function useFetch(method, args, options) {
268
274
  }
269
275
  if (isPending(keyRef.current)) {
270
276
  queueRefetch(showLoaderOnInvalidateRef.current);
277
+ void doFetch(showLoaderOnInvalidateRef.current);
271
278
  return;
272
279
  }
273
280
  void doFetch(showLoaderOnInvalidateRef.current);
@@ -295,10 +302,7 @@ export function useFetch(method, args, options) {
295
302
  }
296
303
  }, ttl);
297
304
  };
298
- // Only schedule if data is already cached
299
- if (has(key)) {
300
- scheduleRefetch();
301
- }
305
+ scheduleRefetch();
302
306
  return () => {
303
307
  isActive = false;
304
308
  if (timeoutId !== undefined) {
@@ -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 * - 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 const queuedRefetchRef = useRef(false);\n const queuedRefetchShowLoaderRef = useRef(false);\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 const queueRefetch = useCallback((showLoader: boolean) => {\n queuedRefetchRef.current = true;\n queuedRefetchShowLoaderRef.current = queuedRefetchShowLoaderRef.current || showLoader;\n }, []);\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 if (queuedRefetchRef.current && isMountedRef.current && enabledRef.current && !isPending(keyRef.current)) {\n const nextShowLoader = queuedRefetchShowLoaderRef.current;\n queuedRefetchRef.current = false;\n queuedRefetchShowLoaderRef.current = false;\n void doFetch(nextShowLoader);\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 if (queuedRefetchRef.current && isMountedRef.current && enabledRef.current && !isPending(keyRef.current)) {\n const nextShowLoader = queuedRefetchShowLoaderRef.current;\n queuedRefetchRef.current = false;\n queuedRefetchShowLoaderRef.current = false;\n void doFetch(nextShowLoader);\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) {\n return;\n }\n\n const shouldShowLoader = showLoaderOnRefocusRef.current || showLoader;\n\n if (isPending(keyRef.current)) {\n queueRefetch(shouldShowLoader);\n return;\n }\n\n void doFetch(shouldShowLoader);\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, queueRefetch]);\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) {\n return;\n }\n\n if (isPending(keyRef.current)) {\n queueRefetch(showLoaderOnInvalidateRef.current);\n return;\n }\n\n void doFetch(showLoaderOnInvalidateRef.current);\n });\n\n return unsubscribe;\n }, [doFetch, queueRefetch]);\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, clearPendingFetch, 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 const queuedRefetchRef = useRef(false);\n const queuedRefetchShowLoaderRef = useRef(false);\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 const queueRefetch = useCallback((showLoader: boolean) => {\n queuedRefetchRef.current = true;\n queuedRefetchShowLoaderRef.current = queuedRefetchShowLoaderRef.current || showLoader;\n }, []);\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 replayQueuedRefetch = () => {\n if (!queuedRefetchRef.current || !isMountedRef.current || !enabledRef.current) {\n return;\n }\n const nextShowLoader = queuedRefetchShowLoaderRef.current;\n queuedRefetchRef.current = false;\n queuedRefetchShowLoaderRef.current = false;\n clearPendingFetch(keyRef.current);\n queueMicrotask(() => {\n if (!isMountedRef.current || !enabledRef.current) {\n return;\n }\n void doFetch(nextShowLoader);\n });\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 replayQueuedRefetch();\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 replayQueuedRefetch();\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) {\n return;\n }\n\n const shouldShowLoader = showLoaderOnRefocusRef.current || showLoader;\n\n if (isPending(keyRef.current)) {\n queueRefetch(shouldShowLoader);\n void doFetch(shouldShowLoader);\n return;\n }\n\n void doFetch(shouldShowLoader);\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, queueRefetch]);\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) {\n return;\n }\n\n if (isPending(keyRef.current)) {\n queueRefetch(showLoaderOnInvalidateRef.current);\n void doFetch(showLoaderOnInvalidateRef.current);\n return;\n }\n\n void doFetch(showLoaderOnInvalidateRef.current);\n });\n\n return unsubscribe;\n }, [doFetch, queueRefetch]);\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 scheduleRefetch();\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.5.9",
3
+ "version": "0.5.11",
4
4
  "description": "A lightweight full-stack React framework with file-based routing, RPC, and SSG support",
5
5
  "keywords": [
6
6
  "react",