@adventurelabs/scout-core 1.0.108 → 1.0.109

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,5 +1,6 @@
1
1
  import { useEffect, useCallback, useRef } from "react";
2
- import { useAppDispatch, useHerdModules, useUser } from "../store/hooks";
2
+ import { useAppDispatch } from "../store/hooks";
3
+ import { useStore } from "react-redux";
3
4
  import { EnumScoutStateStatus, setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiDuration, setUserApiDuration, setDataProcessingDuration, setUser, setDataSource, setDataSourceInfo, } from "../store/scout";
4
5
  import { EnumHerdModulesLoadingState } from "../types/herd_module";
5
6
  import { server_load_herd_modules } from "../helpers/herds";
@@ -33,8 +34,7 @@ export function useScoutRefresh(options = {}) {
33
34
  const { autoRefresh = true, onRefreshComplete, cacheFirst = true, cacheTtlMs = 24 * 60 * 60 * 1000, // 24 hours default (1 day)
34
35
  } = options;
35
36
  const dispatch = useAppDispatch();
36
- const currentHerdModules = useHerdModules();
37
- const currentUser = useUser();
37
+ const store = useStore();
38
38
  const refreshInProgressRef = useRef(false);
39
39
  // Refs to store timing measurements
40
40
  const timingRefs = useRef({
@@ -46,7 +46,7 @@ export function useScoutRefresh(options = {}) {
46
46
  cacheSaveDuration: 0,
47
47
  });
48
48
  // Helper function for deep comparison of objects
49
- const deepEqual = useCallback((obj1, obj2) => {
49
+ const deepEqual = useCallback((obj1, obj2, visited = new WeakMap()) => {
50
50
  if (obj1 === obj2)
51
51
  return true;
52
52
  if (obj1 == null || obj2 == null)
@@ -55,13 +55,22 @@ export function useScoutRefresh(options = {}) {
55
55
  return false;
56
56
  if (typeof obj1 !== "object")
57
57
  return obj1 === obj2;
58
+ // Handle circular references
59
+ if (visited.has(obj1)) {
60
+ return visited.get(obj1) === obj2;
61
+ }
62
+ visited.set(obj1, obj2);
63
+ // Handle Date objects
64
+ if (obj1 instanceof Date && obj2 instanceof Date) {
65
+ return obj1.getTime() === obj2.getTime();
66
+ }
58
67
  if (Array.isArray(obj1) !== Array.isArray(obj2))
59
68
  return false;
60
69
  if (Array.isArray(obj1)) {
61
70
  if (obj1.length !== obj2.length)
62
71
  return false;
63
72
  for (let i = 0; i < obj1.length; i++) {
64
- if (!deepEqual(obj1[i], obj2[i]))
73
+ if (!deepEqual(obj1[i], obj2[i], visited))
65
74
  return false;
66
75
  }
67
76
  return true;
@@ -73,7 +82,7 @@ export function useScoutRefresh(options = {}) {
73
82
  for (const key of keys1) {
74
83
  if (!keys2.includes(key))
75
84
  return false;
76
- if (!deepEqual(obj1[key], obj2[key]))
85
+ if (!deepEqual(obj1[key], obj2[key], visited))
77
86
  return false;
78
87
  }
79
88
  return true;
@@ -90,8 +99,8 @@ export function useScoutRefresh(options = {}) {
90
99
  return false;
91
100
  }
92
101
  }, [dispatch, deepEqual]);
93
- // Helper function to handle IndexedDB errors
94
- const handleIndexedDbError = async (error, operation, retryFn) => {
102
+ // Helper function to handle IndexedDB errors - memoized for stability
103
+ const handleIndexedDbError = useCallback(async (error, operation, retryFn) => {
95
104
  if (error instanceof Error &&
96
105
  (error.message.includes("object store") ||
97
106
  error.message.includes("NotFoundError"))) {
@@ -108,7 +117,7 @@ export function useScoutRefresh(options = {}) {
108
117
  console.error(`[useScoutRefresh] Database reset and retry failed:`, resetError);
109
118
  }
110
119
  }
111
- };
120
+ }, []);
112
121
  const handleRefresh = useCallback(async () => {
113
122
  // Prevent concurrent refresh calls
114
123
  if (refreshInProgressRef.current) {
@@ -143,6 +152,8 @@ export function useScoutRefresh(options = {}) {
143
152
  isStale: cacheResult.isStale,
144
153
  }));
145
154
  // Conditionally update the store with cached data if different
155
+ // Get current state at execution time to avoid dependency issues
156
+ const currentHerdModules = store.getState().scout.herd_modules;
146
157
  const herdModulesChanged = conditionalDispatch(cachedHerdModules, currentHerdModules, setHerdModules, "Herd modules (cache)");
147
158
  if (herdModulesChanged) {
148
159
  dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
@@ -154,6 +165,7 @@ export function useScoutRefresh(options = {}) {
154
165
  timingRefs.current.userApiDuration = userApiDuration;
155
166
  dispatch(setUserApiDuration(userApiDuration));
156
167
  if (res_new_user && res_new_user.data) {
168
+ const currentUser = store.getState().scout.user;
157
169
  conditionalDispatch(res_new_user.data, currentUser, setUser, "User (initial)");
158
170
  }
159
171
  // If cache is fresh, we still background fetch but don't wait
@@ -189,6 +201,8 @@ export function useScoutRefresh(options = {}) {
189
201
  });
190
202
  }
191
203
  // Conditionally update store with fresh background data
204
+ const currentHerdModules = store.getState().scout.herd_modules;
205
+ const currentUser = store.getState().scout.user;
192
206
  conditionalDispatch(backgroundHerdModulesResult.data, currentHerdModules, setHerdModules, "Herd modules (background)");
193
207
  conditionalDispatch(backgroundUserResult.data, currentUser, setUser, "User (background)");
194
208
  // Update data source to DATABASE
@@ -294,6 +308,8 @@ export function useScoutRefresh(options = {}) {
294
308
  }
295
309
  // Step 4: Conditionally update store with fresh data if different
296
310
  const dataProcessingStartTime = Date.now();
311
+ const currentHerdModules = store.getState().scout.herd_modules;
312
+ const currentUser = store.getState().scout.user;
297
313
  const herdModulesChanged = conditionalDispatch(compatible_new_herd_modules, currentHerdModules, setHerdModules, "Herd modules (fresh API)");
298
314
  const userChanged = conditionalDispatch(res_new_user.data, currentUser, setUser, "User (fresh API)");
299
315
  if (herdModulesChanged) {
@@ -334,19 +350,20 @@ export function useScoutRefresh(options = {}) {
334
350
  console.log(` - Total duration: ${loadingDuration}ms`);
335
351
  console.log(` - Herd modules: ${timingRefs.current.herdModulesDuration}ms`);
336
352
  console.log(` - User API: ${timingRefs.current.userApiDuration}ms`);
353
+ // Call completion callback even on error for consistency
354
+ onRefreshComplete?.();
337
355
  }
338
356
  finally {
339
357
  refreshInProgressRef.current = false;
340
358
  }
341
359
  }, [
342
360
  dispatch,
361
+ store,
343
362
  onRefreshComplete,
344
363
  cacheFirst,
345
364
  cacheTtlMs,
346
- handleIndexedDbError,
347
- currentHerdModules,
348
- currentUser,
349
365
  conditionalDispatch,
366
+ handleIndexedDbError,
350
367
  ]);
351
368
  useEffect(() => {
352
369
  if (autoRefresh) {
@@ -1,6 +1,4 @@
1
1
  export declare const useAppDispatch: import("react-redux").UseDispatch<import("redux").Dispatch<import("redux").UnknownAction>>;
2
- export declare const useHerdModules: () => import("../types/herd_module").IHerdModule[];
3
- export declare const useUser: () => import("@supabase/auth-js").User | null;
4
2
  export declare const useHerdModulesLoadingState: () => any;
5
3
  export declare const useIsHerdModulesLoading: () => boolean;
6
4
  export declare const useIsHerdModulesLoaded: () => boolean;
@@ -2,14 +2,6 @@ import { useDispatch, useSelector } from "react-redux";
2
2
  import { EnumHerdModulesLoadingState } from "../types/herd_module";
3
3
  // Simple wrapper for useDispatch to maintain compatibility
4
4
  export const useAppDispatch = useDispatch;
5
- // Selector hook for current herd modules
6
- export const useHerdModules = () => {
7
- return useSelector((state) => state.scout.herd_modules);
8
- };
9
- // Selector hook for current user
10
- export const useUser = () => {
11
- return useSelector((state) => state.scout.user);
12
- };
13
5
  // Selector hook for herd modules loading state
14
6
  export const useHerdModulesLoadingState = () => {
15
7
  return useSelector((state) => state.scout.herd_modules_loading_state);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.108",
3
+ "version": "1.0.109",
4
4
  "description": "Core utilities and helpers for Adventure Labs Scout applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",