@adventurelabs/scout-core 1.5.4 → 2.0.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.
Files changed (219) hide show
  1. package/README.md +47 -0
  2. package/dist/api_keys/actions.d.ts +0 -1
  3. package/dist/api_keys/actions.js +0 -14
  4. package/dist/client/index.d.ts +75 -0
  5. package/dist/client/index.js +82 -0
  6. package/dist/constants/index.d.ts +1 -0
  7. package/dist/constants/index.js +1 -0
  8. package/dist/helpers/analysis_usage/jobs_server.d.ts +25 -0
  9. package/dist/helpers/analysis_usage/jobs_server.js +10 -0
  10. package/dist/helpers/analysis_usage/tasks_server.d.ts +63 -0
  11. package/dist/helpers/analysis_usage/tasks_server.js +14 -0
  12. package/dist/helpers/artifacts.d.ts +0 -1
  13. package/dist/helpers/artifacts.js +10 -147
  14. package/dist/helpers/artifacts.queries.d.ts +26 -0
  15. package/dist/helpers/{artifacts.internal.js → artifacts.queries.js} +24 -41
  16. package/dist/helpers/auth.d.ts +1 -1
  17. package/dist/helpers/auth.js +2 -3
  18. package/dist/helpers/auth_server.d.ts +2 -0
  19. package/dist/helpers/auth_server.js +10 -0
  20. package/dist/helpers/certificates_server.d.ts +101 -0
  21. package/dist/helpers/certificates_server.js +42 -0
  22. package/dist/helpers/chat.d.ts +1 -2
  23. package/dist/helpers/chat.js +3 -38
  24. package/dist/helpers/chat.queries.d.ts +13 -0
  25. package/dist/helpers/chat.queries.js +39 -0
  26. package/dist/helpers/client_abilities_jwt_keys.js +0 -1
  27. package/dist/helpers/client_abilities_token_server.d.ts +1 -0
  28. package/dist/helpers/client_abilities_token_server.js +5 -2
  29. package/dist/helpers/connectivity.d.ts +1 -5
  30. package/dist/helpers/connectivity.js +7 -140
  31. package/dist/helpers/connectivity.queries.d.ts +18 -0
  32. package/dist/helpers/connectivity.queries.js +121 -0
  33. package/dist/helpers/contacts_server.d.ts +63 -0
  34. package/dist/helpers/contacts_server.js +18 -0
  35. package/dist/helpers/deviceTransfer.js +2 -11
  36. package/dist/helpers/deviceTransfer.queries.d.ts +4 -0
  37. package/dist/helpers/deviceTransfer.queries.js +11 -0
  38. package/dist/helpers/devices.js +4 -61
  39. package/dist/helpers/devices.queries.d.ts +4 -1
  40. package/dist/helpers/devices.queries.js +52 -0
  41. package/dist/helpers/document_templates.js +6 -52
  42. package/dist/helpers/document_templates.queries.d.ts +9 -0
  43. package/dist/helpers/document_templates.queries.js +56 -0
  44. package/dist/helpers/documents_server.d.ts +226 -0
  45. package/dist/helpers/documents_server.js +38 -0
  46. package/dist/helpers/email.d.ts +0 -6
  47. package/dist/helpers/email.js +1 -19
  48. package/dist/helpers/embeddings.d.ts +0 -5
  49. package/dist/helpers/embeddings.js +2 -19
  50. package/dist/helpers/embeddings.queries.d.ts +7 -0
  51. package/dist/helpers/{embeddings.internal.js → embeddings.queries.js} +2 -2
  52. package/dist/helpers/eventUtils.d.ts +3 -12
  53. package/dist/helpers/eventUtils.js +0 -10
  54. package/dist/helpers/events.d.ts +0 -1
  55. package/dist/helpers/events.js +7 -107
  56. package/dist/helpers/events.queries.d.ts +17 -0
  57. package/dist/helpers/{events.internal.js → events.queries.js} +24 -15
  58. package/dist/helpers/health_metrics.js +3 -38
  59. package/dist/helpers/health_metrics.queries.d.ts +12 -0
  60. package/dist/helpers/health_metrics.queries.js +39 -0
  61. package/dist/helpers/heartbeats.js +4 -83
  62. package/dist/helpers/heartbeats.queries.d.ts +11 -0
  63. package/dist/helpers/heartbeats.queries.js +76 -0
  64. package/dist/helpers/herd_allowed_domains.js +2 -12
  65. package/dist/helpers/{herd_allowed_domains_internal.d.ts → herd_allowed_domains.queries.d.ts} +1 -1
  66. package/dist/helpers/{herd_allowed_domains_internal.js → herd_allowed_domains.queries.js} +2 -2
  67. package/dist/helpers/herd_credentials_server.d.ts +76 -0
  68. package/dist/helpers/herd_credentials_server.js +22 -0
  69. package/dist/helpers/herd_modules.queries.js +10 -7
  70. package/dist/helpers/herd_roles.js +13 -148
  71. package/dist/helpers/herd_roles.queries.d.ts +18 -0
  72. package/dist/helpers/{herd_roles_internal.js → herd_roles.queries.js} +28 -28
  73. package/dist/helpers/herds.js +5 -47
  74. package/dist/helpers/herds.queries.d.ts +8 -1
  75. package/dist/helpers/herds.queries.js +48 -0
  76. package/dist/helpers/invitations.js +7 -58
  77. package/dist/helpers/invitations.queries.d.ts +10 -0
  78. package/dist/helpers/{invitations_internal.js → invitations.queries.js} +12 -12
  79. package/dist/helpers/issuers.js +6 -52
  80. package/dist/helpers/issuers.queries.d.ts +9 -0
  81. package/dist/helpers/issuers.queries.js +53 -0
  82. package/dist/helpers/layers.js +2 -20
  83. package/dist/helpers/layers.queries.d.ts +2 -1
  84. package/dist/helpers/layers.queries.js +9 -13
  85. package/dist/helpers/lifecycle_server.d.ts +175 -0
  86. package/dist/helpers/lifecycle_server.js +38 -0
  87. package/dist/helpers/localizations.js +5 -42
  88. package/dist/helpers/localizations.queries.d.ts +8 -0
  89. package/dist/helpers/localizations.queries.js +45 -0
  90. package/dist/helpers/maintenance_requests_server.d.ts +124 -0
  91. package/dist/helpers/maintenance_requests_server.js +34 -0
  92. package/dist/helpers/manufacturers.js +5 -43
  93. package/dist/helpers/manufacturers.queries.d.ts +10 -0
  94. package/dist/helpers/manufacturers.queries.js +46 -0
  95. package/dist/helpers/media_urls.d.ts +13 -0
  96. package/dist/helpers/media_urls.js +21 -0
  97. package/dist/helpers/models.js +8 -80
  98. package/dist/helpers/models.queries.d.ts +13 -0
  99. package/dist/helpers/models.queries.js +79 -0
  100. package/dist/helpers/observations_server.d.ts +51 -0
  101. package/dist/helpers/observations_server.js +22 -0
  102. package/dist/helpers/operating_contexts.js +39 -154
  103. package/dist/helpers/operating_contexts.queries.d.ts +48 -0
  104. package/dist/helpers/operating_contexts.queries.js +223 -0
  105. package/dist/helpers/operators.js +4 -57
  106. package/dist/helpers/operators.queries.d.ts +7 -0
  107. package/dist/helpers/operators.queries.js +55 -0
  108. package/dist/helpers/parts.d.ts +0 -87
  109. package/dist/helpers/parts.js +4 -90
  110. package/dist/helpers/parts_server.d.ts +260 -0
  111. package/dist/helpers/parts_server.js +71 -0
  112. package/dist/helpers/pins_server.d.ts +118 -0
  113. package/dist/helpers/pins_server.js +38 -0
  114. package/dist/helpers/plans.js +4 -56
  115. package/dist/helpers/plans.queries.d.ts +4 -1
  116. package/dist/helpers/plans.queries.js +39 -12
  117. package/dist/helpers/points_for_user_server.d.ts +9 -0
  118. package/dist/helpers/points_for_user_server.js +14 -0
  119. package/dist/helpers/product_compatibilities.js +6 -78
  120. package/dist/helpers/product_compatibilities.queries.d.ts +9 -0
  121. package/dist/helpers/product_compatibilities.queries.js +83 -0
  122. package/dist/helpers/product_numbers.js +6 -55
  123. package/dist/helpers/product_numbers.queries.d.ts +9 -0
  124. package/dist/helpers/product_numbers.queries.js +58 -0
  125. package/dist/helpers/products.js +6 -54
  126. package/dist/helpers/products.queries.d.ts +11 -0
  127. package/dist/helpers/products.queries.js +58 -0
  128. package/dist/helpers/providers.js +5 -71
  129. package/dist/helpers/providers.queries.d.ts +5 -1
  130. package/dist/helpers/providers.queries.js +52 -11
  131. package/dist/helpers/pubsub_token_server.d.ts +1 -0
  132. package/dist/helpers/pubsub_token_server.js +5 -2
  133. package/dist/helpers/segmentations_sam3.js +5 -78
  134. package/dist/helpers/segmentations_sam3.queries.d.ts +16 -0
  135. package/dist/helpers/segmentations_sam3.queries.js +81 -0
  136. package/dist/helpers/sensor_poses_server.d.ts +98 -0
  137. package/dist/helpers/sensor_poses_server.js +26 -0
  138. package/dist/helpers/session_incidents_server.d.ts +106 -0
  139. package/dist/helpers/session_incidents_server.js +22 -0
  140. package/dist/helpers/session_summaries.d.ts +2 -0
  141. package/dist/helpers/session_summaries.js +7 -23
  142. package/dist/helpers/session_summaries.queries.d.ts +6 -3
  143. package/dist/helpers/session_summaries.queries.js +27 -0
  144. package/dist/helpers/sessions.js +8 -122
  145. package/dist/helpers/sessions.queries.d.ts +14 -2
  146. package/dist/helpers/sessions.queries.js +88 -1
  147. package/dist/helpers/storage.js +0 -1
  148. package/dist/helpers/supabase_auth.d.ts +6 -0
  149. package/dist/helpers/supabase_auth.js +37 -0
  150. package/dist/helpers/tags.d.ts +0 -1
  151. package/dist/helpers/tags.js +13 -559
  152. package/dist/helpers/tags.queries.d.ts +33 -0
  153. package/dist/helpers/tags.queries.js +454 -0
  154. package/dist/helpers/user_credentials_server.d.ts +76 -0
  155. package/dist/helpers/user_credentials_server.js +22 -0
  156. package/dist/helpers/users.d.ts +2 -2
  157. package/dist/helpers/users.js +8 -107
  158. package/dist/helpers/users.queries.d.ts +9 -1
  159. package/dist/helpers/users.queries.js +107 -0
  160. package/dist/helpers/versions_software.d.ts +3 -1
  161. package/dist/helpers/versions_software.js +24 -0
  162. package/dist/helpers/versions_software_server.d.ts +83 -4
  163. package/dist/helpers/versions_software_server.js +23 -50
  164. package/dist/helpers/zones.d.ts +1 -16
  165. package/dist/helpers/zones.js +4 -104
  166. package/dist/helpers/zones.queries.d.ts +6 -0
  167. package/dist/helpers/zones.queries.js +78 -0
  168. package/dist/hooks/useAuthorizedRealtimeTopics.js +8 -2
  169. package/dist/hooks/useInfiniteQuery.js +18 -14
  170. package/dist/hooks/useScoutRealtimeBroadcast.js +8 -6
  171. package/dist/hooks/useScoutRealtimeDocuments.js +3 -2
  172. package/dist/hooks/useScoutRefresh.d.ts +9 -25
  173. package/dist/hooks/useScoutRefresh.js +33 -213
  174. package/dist/providers/ScoutRefreshProvider.d.ts +1 -1
  175. package/dist/providers/ScoutRefreshProvider.js +56 -13
  176. package/dist/server/index.d.ts +77 -0
  177. package/dist/server/index.js +79 -0
  178. package/dist/server/middleware.d.ts +2 -0
  179. package/dist/server/middleware.js +2 -0
  180. package/dist/server/supabase.d.ts +2 -0
  181. package/dist/server/supabase.js +2 -0
  182. package/dist/store/api.d.ts +40 -40
  183. package/dist/store/api.js +38 -109
  184. package/dist/store/configureStore.d.ts +0 -55
  185. package/dist/store/configureStore.js +1 -8
  186. package/dist/store/hooks.js +1 -0
  187. package/dist/supabase/client.d.ts +3 -0
  188. package/dist/supabase/client.js +10 -0
  189. package/dist/supabase/middleware.d.ts +1 -0
  190. package/dist/supabase/middleware.js +6 -20
  191. package/dist/supabase/server.d.ts +7 -2
  192. package/dist/supabase/server.js +13 -1
  193. package/dist/types/db.d.ts +2 -2
  194. package/dist/types/index.d.ts +5 -0
  195. package/dist/types/index.js +5 -0
  196. package/package.json +53 -4
  197. package/dist/api_keys/index.d.ts +0 -1
  198. package/dist/api_keys/index.js +0 -1
  199. package/dist/helpers/artifacts.internal.d.ts +0 -22
  200. package/dist/helpers/client_abilities_jwt_keys_internal.d.ts +0 -9
  201. package/dist/helpers/client_abilities_jwt_keys_internal.js +0 -20
  202. package/dist/helpers/connectivity.internal.d.ts +0 -10
  203. package/dist/helpers/connectivity.internal.js +0 -47
  204. package/dist/helpers/credentials.d.ts +0 -9
  205. package/dist/helpers/credentials.js +0 -92
  206. package/dist/helpers/embeddings.internal.d.ts +0 -5
  207. package/dist/helpers/events.internal.d.ts +0 -13
  208. package/dist/helpers/herd_roles_internal.d.ts +0 -18
  209. package/dist/helpers/index.d.ts +0 -56
  210. package/dist/helpers/index.js +0 -56
  211. package/dist/helpers/invitations_internal.d.ts +0 -10
  212. package/dist/helpers/versions_software_internal.d.ts +0 -6
  213. package/dist/helpers/versions_software_internal.js +0 -53
  214. package/dist/hooks/useDeviceDisplayName.d.ts +0 -8
  215. package/dist/hooks/useDeviceDisplayName.js +0 -49
  216. package/dist/index.d.ts +0 -108
  217. package/dist/index.js +0 -114
  218. package/dist/supabase/index.d.ts +0 -2
  219. package/dist/supabase/index.js +0 -2
@@ -1,6 +1,7 @@
1
1
  "use client";
2
- import { useCallback, useEffect, useState } from "react";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
3
  import { EnumRealtimeOperation } from "../types/realtime";
4
+ import { refreshRealtimeAuth, subscribeToSupabaseAuth, } from "../helpers/supabase_auth";
4
5
  import { useAuthorizedRealtimeTopics } from "./useAuthorizedRealtimeTopics";
5
6
  const OPERATIONS = {
6
7
  INSERT: EnumRealtimeOperation.INSERT,
@@ -19,6 +20,8 @@ function backoffDelayMs(attempt, initialMs, maxMs) {
19
20
  export function useScoutRealtimeBroadcast(scoutSupabase, topics, options = {}, rowFilter) {
20
21
  const { authErrorRetry = "stop", backoffInitialMs = DEFAULT_BACKOFF_INITIAL_MS, backoffMaxMs = DEFAULT_BACKOFF_MAX_MS, backoffMaxAttempts = Number.POSITIVE_INFINITY, } = options;
21
22
  const [latestUpdate, setLatestUpdate] = useState(null);
23
+ const rowFilterRef = useRef(rowFilter);
24
+ rowFilterRef.current = rowFilter;
22
25
  const subscribedTopics = useAuthorizedRealtimeTopics(topics);
23
26
  const clearLatestUpdate = useCallback(() => setLatestUpdate(null), []);
24
27
  useEffect(() => {
@@ -63,7 +66,7 @@ export function useScoutRealtimeBroadcast(scoutSupabase, topics, options = {}, r
63
66
  const row = change.record ?? change.old_record;
64
67
  if (operation === undefined || !row)
65
68
  return;
66
- if (rowFilter && !rowFilter(row))
69
+ if (rowFilterRef.current && !rowFilterRef.current(row))
67
70
  return;
68
71
  setLatestUpdate({ data: row, operation, table: change.table });
69
72
  });
@@ -86,7 +89,7 @@ export function useScoutRealtimeBroadcast(scoutSupabase, topics, options = {}, r
86
89
  };
87
90
  async function authenticateAndJoin(topicsToJoin) {
88
91
  try {
89
- await scoutSupabase.realtime.setAuth();
92
+ await refreshRealtimeAuth(scoutSupabase);
90
93
  }
91
94
  catch (error) {
92
95
  console.warn("[scout-core realtime] failed to refresh auth", error);
@@ -106,7 +109,7 @@ export function useScoutRealtimeBroadcast(scoutSupabase, topics, options = {}, r
106
109
  if (topicsToRetry.length > 0)
107
110
  void authenticateAndJoin(topicsToRetry);
108
111
  };
109
- const { data: { subscription: authSubscription }, } = scoutSupabase.auth.onAuthStateChange((event) => {
112
+ const unsubscribeAuth = subscribeToSupabaseAuth(scoutSupabase, (event) => {
110
113
  if (event === "INITIAL_SESSION" ||
111
114
  event === "SIGNED_IN" ||
112
115
  event === "TOKEN_REFRESHED") {
@@ -116,7 +119,7 @@ export function useScoutRealtimeBroadcast(scoutSupabase, topics, options = {}, r
116
119
  void authenticateAndJoin(subscribedTopics);
117
120
  return () => {
118
121
  cancelled = true;
119
- authSubscription.unsubscribe();
122
+ unsubscribeAuth();
120
123
  retryTimers.forEach((timer) => clearTimeout(timer));
121
124
  retryTimers.clear();
122
125
  channels.forEach((channel) => {
@@ -131,7 +134,6 @@ export function useScoutRealtimeBroadcast(scoutSupabase, topics, options = {}, r
131
134
  backoffInitialMs,
132
135
  backoffMaxMs,
133
136
  backoffMaxAttempts,
134
- rowFilter,
135
137
  ]);
136
138
  return [latestUpdate, clearLatestUpdate];
137
139
  }
@@ -13,12 +13,13 @@ const ALL_DOCUMENT_CHANNELS = [
13
13
  export function useScoutRealtimeDocuments(scoutSupabase, channels = ALL_DOCUMENT_CHANNELS, options) {
14
14
  const activeHerdId = useSelector((state) => state.scout.active_herd_id);
15
15
  const userId = useSelector((state) => state.scout.user?.id ?? null);
16
- const topics = useMemo(() => channels.flatMap((channel) => {
16
+ const channelsKey = channels.join("|");
17
+ const topics = useMemo(() => channelsKey.split("|").flatMap((channel) => {
17
18
  if (channel === "product")
18
19
  return ["product_documents_changes"];
19
20
  if (channel === "own")
20
21
  return userId ? [`user_${userId}-documents`] : [];
21
22
  return activeHerdId ? [`${activeHerdId}-${channel}_documents`] : [];
22
- }), [channels, activeHerdId, userId]);
23
+ }), [channelsKey, activeHerdId, userId]);
23
24
  return useScoutRealtimeBroadcast(scoutSupabase, topics, options);
24
25
  }
@@ -1,5 +1,5 @@
1
- import { SupabaseClient } from "@supabase/supabase-js";
2
- import { Database } from "../types/supabase";
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ import type { Database } from "../types/supabase";
3
3
  export interface UseScoutRefreshOptions {
4
4
  supabase?: SupabaseClient<Database>;
5
5
  autoRefresh?: boolean;
@@ -8,32 +8,16 @@ export interface UseScoutRefreshOptions {
8
8
  cacheTtlMs?: number;
9
9
  onlineRefetchMinIntervalMs?: number;
10
10
  }
11
+ export interface ScoutRefreshRequest {
12
+ force?: boolean;
13
+ }
11
14
  /**
12
- * Hook for refreshing scout data with detailed timing measurements and cache-first loading
13
- *
14
- * @param options - Configuration options for the refresh behavior
15
- * @param options.autoRefresh - Whether to automatically refresh on mount (default: true)
16
- * @param options.onRefreshComplete - Callback function called when refresh completes
17
- * @param options.cacheFirst - Whether to load from cache first, then refresh (default: true)
18
- * @param options.cacheTtlMs - Cache time-to-live in milliseconds (default: 24 hours)
19
- * @param options.onlineRefetchMinIntervalMs - Min ms since last refresh before refetch on "online" (default: 15s)
20
- * @param options.supabase - Optional shared browser client; when omitted, the hook creates its own via `createBrowserClient`
21
- *
22
- * @returns Object containing:
23
- * - handleRefresh: Function to manually trigger a refresh
24
- * - clearCache: Function to clear the cache
25
- *
26
- * @example
27
- * ```tsx
28
- * const { handleRefresh, clearCache } = useScoutRefresh({
29
- * cacheFirst: true,
30
- * cacheTtlMs: 10 * 60 * 1000 // 10 minutes
31
- * });
15
+ * Refreshes Scout state from cache and the API.
32
16
  *
33
- * // Timing stats are available in Redux store via selectors
34
- * ```
17
+ * Manual refreshes force an API request by default. Automatic mount refreshes
18
+ * finish from a fresh cache without requesting the API.
35
19
  */
36
20
  export declare function useScoutRefresh(options?: UseScoutRefreshOptions): {
37
- handleRefresh: () => Promise<void>;
21
+ handleRefresh: ({ force }?: ScoutRefreshRequest) => Promise<void>;
38
22
  clearCache: () => Promise<void>;
39
23
  };
@@ -1,93 +1,60 @@
1
- import { useEffect, useCallback, useRef, useMemo, startTransition } from "react";
1
+ "use client";
2
+ import { useEffect, useCallback, useRef, useMemo, startTransition, } from "react";
2
3
  import { useAppDispatch } from "../store/hooks";
3
4
  import { EnumScoutStateStatus, setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiServerProcessingDuration, setHerdModulesApiTotalRequestDuration, setUserApiDuration, setDataProcessingDuration, setCacheLoadDuration, setUser, setDataSource, setDataSourceInfo, } from "../store/scout";
4
- import { EnumHerdModulesLoadingState } from "../types/herd_module";
5
- import { server_load_herd_modules } from "../helpers/herds";
5
+ import { EnumHerdModulesLoadingState, } from "../types/herd_module";
6
+ import { load_herd_modules_query } from "../helpers/herd_modules.queries";
6
7
  import { scoutCache } from "../helpers/cache";
7
8
  import { EnumDataSource } from "../types/data_source";
8
9
  import { EnumWebResponse } from "../types/requests";
9
- import { createBrowserClient } from "@supabase/ssr";
10
+ import { createScoutBrowserClient } from "../supabase/client";
10
11
  function dispatchInTransition(dispatch, action) {
11
12
  startTransition(() => {
12
13
  dispatch(action);
13
14
  });
14
15
  }
15
16
  /**
16
- * Hook for refreshing scout data with detailed timing measurements and cache-first loading
17
+ * Refreshes Scout state from cache and the API.
17
18
  *
18
- * @param options - Configuration options for the refresh behavior
19
- * @param options.autoRefresh - Whether to automatically refresh on mount (default: true)
20
- * @param options.onRefreshComplete - Callback function called when refresh completes
21
- * @param options.cacheFirst - Whether to load from cache first, then refresh (default: true)
22
- * @param options.cacheTtlMs - Cache time-to-live in milliseconds (default: 24 hours)
23
- * @param options.onlineRefetchMinIntervalMs - Min ms since last refresh before refetch on "online" (default: 15s)
24
- * @param options.supabase - Optional shared browser client; when omitted, the hook creates its own via `createBrowserClient`
25
- *
26
- * @returns Object containing:
27
- * - handleRefresh: Function to manually trigger a refresh
28
- * - clearCache: Function to clear the cache
29
- *
30
- * @example
31
- * ```tsx
32
- * const { handleRefresh, clearCache } = useScoutRefresh({
33
- * cacheFirst: true,
34
- * cacheTtlMs: 10 * 60 * 1000 // 10 minutes
35
- * });
36
- *
37
- * // Timing stats are available in Redux store via selectors
38
- * ```
19
+ * Manual refreshes force an API request by default. Automatic mount refreshes
20
+ * finish from a fresh cache without requesting the API.
39
21
  */
40
22
  export function useScoutRefresh(options = {}) {
41
- const { supabase: supabaseOption, autoRefresh = true, onRefreshComplete, cacheFirst = true, cacheTtlMs = 24 * 60 * 60 * 1000, // 24 hours default (1 day)
42
- onlineRefetchMinIntervalMs = 15 * 1000, // 15 seconds
43
- } = options;
23
+ const { supabase: supabaseOption, autoRefresh = true, onRefreshComplete, cacheFirst = true, cacheTtlMs = 24 * 60 * 60 * 1000, onlineRefetchMinIntervalMs = 15 * 1000, } = options;
44
24
  const dispatch = useAppDispatch();
45
25
  const refreshInProgressRef = useRef(false);
46
26
  const lastQueryAtRef = useRef(0);
27
+ const onRefreshCompleteRef = useRef(onRefreshComplete);
28
+ useEffect(() => {
29
+ onRefreshCompleteRef.current = onRefreshComplete;
30
+ }, [onRefreshComplete]);
47
31
  const supabase = useMemo(() => {
48
32
  if (supabaseOption) {
49
33
  return supabaseOption;
50
34
  }
51
- return createBrowserClient(process.env.NEXT_PUBLIC_SUPABASE_URL || "", process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "");
35
+ return createScoutBrowserClient();
52
36
  }, [supabaseOption]);
53
- // Refs to store timing measurements
54
- const timingRefs = useRef({
55
- startTime: 0,
56
- herdModulesDuration: 0,
57
- userApiDuration: 0,
58
- dataProcessingDuration: 0,
59
- cacheLoadDuration: 0,
60
- cacheSaveDuration: 0,
61
- });
62
- // Helper function for deep comparison of objects
63
- // Helper function to handle IndexedDB errors - memoized for stability
64
37
  const handleIndexedDbError = useCallback(async (error, operation, retryFn) => {
65
38
  if (error instanceof Error &&
66
39
  (error.message.includes("object store") ||
67
40
  error.message.includes("NotFoundError"))) {
68
- console.log(`[useScoutRefresh] Attempting database reset due to ${operation} error...`);
69
41
  try {
70
42
  await scoutCache.resetDatabase();
71
- console.log("[useScoutRefresh] Database reset successful");
72
43
  if (retryFn) {
73
44
  await retryFn();
74
- console.log(`[useScoutRefresh] ${operation} successful after database reset`);
75
45
  }
76
46
  }
77
47
  catch (resetError) {
78
- console.error(`[useScoutRefresh] Database reset and retry failed:`, resetError);
48
+ console.error(`[useScoutRefresh] ${operation} reset and retry failed:`, resetError);
79
49
  }
80
50
  }
81
51
  }, []);
82
- const handleRefresh = useCallback(async () => {
83
- // Prevent concurrent refresh calls
52
+ const handleRefresh = useCallback(async ({ force = true } = {}) => {
84
53
  if (refreshInProgressRef.current) {
85
- console.warn("[useScoutRefresh] Refresh already in progress, skipping");
86
54
  return;
87
55
  }
88
56
  refreshInProgressRef.current = true;
89
57
  const startTime = Date.now();
90
- timingRefs.current.startTime = startTime;
91
58
  const isOffline = typeof navigator === "undefined" || !navigator.onLine;
92
59
  const fetchUserWithOneRetry = async () => {
93
60
  const startTotal = Date.now();
@@ -99,7 +66,6 @@ export function useScoutRefresh(options = {}) {
99
66
  throw new Error("Invalid user response");
100
67
  }
101
68
  const totalMs = Date.now() - startTotal;
102
- timingRefs.current.userApiDuration = totalMs;
103
69
  dispatch(setUserApiDuration(totalMs));
104
70
  dispatchInTransition(dispatch, setUser(data.user));
105
71
  return data.user;
@@ -110,47 +76,20 @@ export function useScoutRefresh(options = {}) {
110
76
  }
111
77
  throw lastError;
112
78
  };
113
- const fetchUserWithOneRetryBackground = async () => {
114
- const startTotal = Date.now();
115
- for (let attempt = 0; attempt < 2; attempt++) {
116
- try {
117
- const { data } = await supabase.auth.getUser();
118
- if (data.user) {
119
- const totalMs = Date.now() - startTotal;
120
- timingRefs.current.userApiDuration = totalMs;
121
- dispatch(setUserApiDuration(totalMs));
122
- dispatchInTransition(dispatch, setUser(data.user));
123
- return data.user;
124
- }
125
- }
126
- catch {
127
- // retry once
128
- }
129
- }
130
- const totalMs = Date.now() - startTotal;
131
- timingRefs.current.userApiDuration = totalMs;
132
- dispatch(setUserApiDuration(totalMs));
133
- return null;
134
- };
135
79
  try {
136
80
  dispatch(setStatus(EnumScoutStateStatus.LOADING));
137
81
  dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.LOADING));
138
82
  let cachedHerdModules = null;
139
83
  let cachedContentHash;
140
- let cacheLoadDuration = 0;
141
- // Step 1: Load from cache first if enabled
142
84
  if (cacheFirst) {
143
85
  const cacheStartTime = Date.now();
144
86
  try {
145
87
  const cacheResult = await scoutCache.getHerdModules();
146
- cacheLoadDuration = Date.now() - cacheStartTime;
147
- timingRefs.current.cacheLoadDuration = cacheLoadDuration;
88
+ const cacheLoadDuration = Date.now() - cacheStartTime;
148
89
  dispatch(setCacheLoadDuration(cacheLoadDuration));
149
90
  if (cacheResult.data && cacheResult.data.length > 0) {
150
91
  cachedHerdModules = cacheResult.data;
151
92
  cachedContentHash = cacheResult.metadata?.etag;
152
- console.log(`[useScoutRefresh] Loaded ${cachedHerdModules.length} herd modules from cache in ${cacheLoadDuration}ms (age: ${Math.round(cacheResult.age / 1000)}s, stale: ${cacheResult.isStale})`);
153
- // Set data source to CACHE initially
154
93
  dispatch(setDataSource(EnumDataSource.CACHE));
155
94
  dispatch(setDataSourceInfo({
156
95
  source: EnumDataSource.CACHE,
@@ -158,98 +97,25 @@ export function useScoutRefresh(options = {}) {
158
97
  cacheAge: cacheResult.age,
159
98
  isStale: cacheResult.isStale,
160
99
  }));
161
- // Update the store with cached data
162
- console.log(`[useScoutRefresh] Updating store with cached herd modules`);
163
100
  dispatchInTransition(dispatch, setHerdModules({
164
101
  modules: cachedHerdModules,
165
102
  contentHash: cachedContentHash,
166
103
  }));
167
104
  dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
168
- // If cache is fresh, we still background fetch but don't wait (only when online)
169
- if (!cacheResult.isStale) {
170
- if (!isOffline) {
171
- // Background fetch fresh data without blocking
172
- (async () => {
173
- try {
174
- const userPromise = fetchUserWithOneRetryBackground();
175
- let backgroundHerdModulesResult;
176
- try {
177
- const start = Date.now();
178
- const result = await server_load_herd_modules();
179
- const totalDuration = Date.now() - start;
180
- const serverDuration = result.server_processing_time_ms || totalDuration;
181
- const clientOverhead = totalDuration - serverDuration;
182
- console.log(`[useScoutRefresh] Background API timing breakdown:`);
183
- console.log(` - Server processing: ${serverDuration}ms`);
184
- console.log(` - Client overhead: ${clientOverhead}ms`);
185
- console.log(` - Total request: ${totalDuration}ms`);
186
- timingRefs.current.herdModulesDuration = serverDuration;
187
- dispatch(setHerdModulesApiServerProcessingDuration(serverDuration));
188
- dispatch(setHerdModulesApiTotalRequestDuration(totalDuration));
189
- backgroundHerdModulesResult = result;
190
- }
191
- catch (e) {
192
- await userPromise.catch(() => { });
193
- throw e;
194
- }
195
- const backgroundUserResult = await userPromise;
196
- // Validate background responses
197
- if (backgroundHerdModulesResult.status ===
198
- EnumWebResponse.SUCCESS &&
199
- Array.isArray(backgroundHerdModulesResult.data) &&
200
- backgroundUserResult) {
201
- const backgroundContentHash = backgroundHerdModulesResult.content_hash;
202
- // Update cache with fresh data
203
- try {
204
- await scoutCache.setHerdModules(backgroundHerdModulesResult.data, cacheTtlMs, backgroundContentHash);
205
- }
206
- catch (cacheError) {
207
- console.warn("[useScoutRefresh] Background cache save failed:", cacheError);
208
- await handleIndexedDbError(cacheError, "background cache save", async () => {
209
- if (backgroundHerdModulesResult.data) {
210
- await scoutCache.setHerdModules(backgroundHerdModulesResult.data, cacheTtlMs, backgroundContentHash);
211
- }
212
- });
213
- }
214
- // Update store with fresh data from background request
215
- console.log(`[useScoutRefresh] Updating store with background herd modules`);
216
- dispatchInTransition(dispatch, setHerdModules({
217
- modules: backgroundHerdModulesResult.data,
218
- contentHash: backgroundContentHash,
219
- }));
220
- // Update data source to DATABASE
221
- dispatch(setDataSource(EnumDataSource.DATABASE));
222
- dispatch(setDataSourceInfo({
223
- source: EnumDataSource.DATABASE,
224
- timestamp: Date.now(),
225
- }));
226
- }
227
- else {
228
- console.warn("[useScoutRefresh] Background fetch returned invalid data");
229
- }
230
- }
231
- catch (backgroundError) {
232
- console.warn("[useScoutRefresh] Background fetch failed:", backgroundError);
233
- }
234
- })();
235
- }
105
+ if (!cacheResult.isStale && !force) {
236
106
  const totalDuration = Date.now() - startTime;
237
107
  dispatch(setHerdModulesLoadedInMs(totalDuration));
238
108
  dispatch(setStatus(EnumScoutStateStatus.DONE_LOADING));
239
- onRefreshComplete?.();
109
+ onRefreshCompleteRef.current?.();
240
110
  return;
241
111
  }
242
112
  }
243
- else {
244
- }
245
113
  }
246
114
  catch (cacheError) {
247
115
  console.warn("[useScoutRefresh] Cache load failed:", cacheError);
248
116
  await handleIndexedDbError(cacheError, "cache load");
249
- // Continue with API call
250
117
  }
251
118
  }
252
- // When offline, skip network/auth; use cache if available
253
119
  if (isOffline) {
254
120
  if (cachedHerdModules && cachedHerdModules.length > 0) {
255
121
  dispatch(setDataSource(EnumDataSource.CACHE));
@@ -273,22 +139,18 @@ export function useScoutRefresh(options = {}) {
273
139
  }
274
140
  dispatch(setHerdModulesLoadedInMs(Date.now() - startTime));
275
141
  dispatch(setStatus(EnumScoutStateStatus.DONE_LOADING));
276
- onRefreshComplete?.();
277
- refreshInProgressRef.current = false;
278
- lastQueryAtRef.current = Date.now();
142
+ onRefreshCompleteRef.current?.();
279
143
  return;
280
144
  }
281
- // Step 2: Load fresh data from API (user resolves independently; Redux gets user as soon as getUser returns)
282
- const parallelStartTime = Date.now();
283
145
  const userPromise = fetchUserWithOneRetry();
284
146
  let herdModulesResult;
285
147
  try {
286
148
  herdModulesResult = await (async () => {
287
149
  const start = Date.now();
288
- const result = await server_load_herd_modules();
150
+ const result = await load_herd_modules_query(supabase);
289
151
  const totalDuration = Date.now() - start;
290
152
  const serverDuration = result.server_processing_time_ms || totalDuration;
291
- return { result, totalDuration, serverDuration, start };
153
+ return { result, totalDuration, serverDuration };
292
154
  })();
293
155
  }
294
156
  catch (e) {
@@ -296,121 +158,79 @@ export function useScoutRefresh(options = {}) {
296
158
  throw e;
297
159
  }
298
160
  await userPromise;
299
- const parallelDuration = Date.now() - parallelStartTime;
300
- console.log(`[useScoutRefresh] API requests completed in ${parallelDuration}ms`);
301
- // Extract results and timing
302
161
  const herdModulesResponse = herdModulesResult.result;
303
162
  const herdModulesServerDuration = herdModulesResult.serverDuration;
304
163
  const herdModulesTotalDuration = herdModulesResult.totalDuration;
305
- const clientOverhead = herdModulesTotalDuration - herdModulesServerDuration;
306
- console.log(`[useScoutRefresh] Fresh API timing breakdown:`);
307
- console.log(` - Server processing: ${herdModulesServerDuration}ms`);
308
- console.log(` - Client overhead: ${clientOverhead}ms`);
309
- console.log(` - Total request: ${herdModulesTotalDuration}ms`);
310
- // Store timing values
311
- timingRefs.current.herdModulesDuration = herdModulesServerDuration;
312
- // Dispatch timing actions
313
164
  dispatch(setHerdModulesApiServerProcessingDuration(herdModulesServerDuration));
314
165
  dispatch(setHerdModulesApiTotalRequestDuration(herdModulesTotalDuration));
315
- // Validate API responses
316
- const validationStartTime = Date.now();
317
166
  if (herdModulesResponse.status !== EnumWebResponse.SUCCESS ||
318
167
  !Array.isArray(herdModulesResponse.data)) {
319
168
  throw new Error("Invalid herd modules response");
320
169
  }
321
- const validationDuration = Date.now() - validationStartTime;
322
- console.log(`[useScoutRefresh] Data validation took: ${validationDuration}ms`);
323
- // Use the validated data
324
- const compatible_new_herd_modules = herdModulesResponse.data;
170
+ const freshHerdModules = herdModulesResponse.data;
325
171
  const freshContentHash = herdModulesResponse.content_hash;
326
- // Set data source to DATABASE
327
172
  dispatch(setDataSource(EnumDataSource.DATABASE));
328
173
  dispatch(setDataSourceInfo({
329
174
  source: EnumDataSource.DATABASE,
330
175
  timestamp: Date.now(),
331
176
  }));
332
- // Step 3: Update cache with fresh data
333
- const cacheSaveStartTime = Date.now();
334
177
  try {
335
- await scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs, freshContentHash);
336
- const cacheSaveDuration = Date.now() - cacheSaveStartTime;
337
- timingRefs.current.cacheSaveDuration = cacheSaveDuration;
338
- console.log(`[useScoutRefresh] Cache updated in ${cacheSaveDuration}ms with TTL: ${Math.round(cacheTtlMs / 1000)}s`);
178
+ await scoutCache.setHerdModules(freshHerdModules, cacheTtlMs, freshContentHash);
339
179
  }
340
180
  catch (cacheError) {
341
181
  console.warn("[useScoutRefresh] Cache save failed:", cacheError);
342
182
  await handleIndexedDbError(cacheError, "cache save", async () => {
343
- await scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs, freshContentHash);
183
+ await scoutCache.setHerdModules(freshHerdModules, cacheTtlMs, freshContentHash);
344
184
  });
345
185
  }
346
- // Step 4: Update store with fresh data (reducer skips no-op updates)
347
186
  const dataProcessingStartTime = Date.now();
348
- // Update store with new data
349
- console.log(`[useScoutRefresh] Updating store with fresh herd modules`);
350
187
  dispatchInTransition(dispatch, setHerdModules({
351
- modules: compatible_new_herd_modules,
188
+ modules: freshHerdModules,
352
189
  contentHash: freshContentHash,
353
190
  }));
354
191
  dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
355
192
  const dataProcessingDuration = Date.now() - dataProcessingStartTime;
356
- timingRefs.current.dataProcessingDuration = dataProcessingDuration;
357
193
  dispatch(setDataProcessingDuration(dataProcessingDuration));
358
194
  const loadingDuration = Date.now() - startTime;
359
195
  dispatch(setHerdModulesLoadedInMs(loadingDuration));
360
196
  dispatch(setStatus(EnumScoutStateStatus.DONE_LOADING));
361
- // Log concise completion summary
362
- console.log(`[useScoutRefresh] Refresh completed in ${loadingDuration}ms (Server: ${herdModulesServerDuration}ms, Total API: ${herdModulesTotalDuration}ms)`);
363
- onRefreshComplete?.();
197
+ onRefreshCompleteRef.current?.();
364
198
  }
365
199
  catch (error) {
366
200
  const loadingDuration = Date.now() - startTime;
367
201
  console.error("Error refreshing scout data:", error);
368
- // Set data source to UNKNOWN on error
369
202
  dispatch(setDataSource(EnumDataSource.UNKNOWN));
370
203
  dispatch(setDataSourceInfo({
371
204
  source: EnumDataSource.UNKNOWN,
372
205
  timestamp: Date.now(),
373
206
  }));
374
- // Ensure consistent state updates on error
375
207
  dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.UNSUCCESSFULLY_LOADED));
376
208
  dispatch(setHerdModulesLoadedInMs(loadingDuration));
377
209
  dispatch(setStatus(EnumScoutStateStatus.DONE_LOADING));
378
- // Log essential error metrics
379
- console.log(`[useScoutRefresh] Refresh failed after ${loadingDuration}ms`);
380
- // Call completion callback even on error for consistency
381
- onRefreshComplete?.();
210
+ onRefreshCompleteRef.current?.();
382
211
  }
383
212
  finally {
384
213
  refreshInProgressRef.current = false;
385
214
  lastQueryAtRef.current = Date.now();
386
215
  }
387
- }, [
388
- dispatch,
389
- supabase,
390
- onRefreshComplete,
391
- cacheFirst,
392
- cacheTtlMs,
393
- handleIndexedDbError,
394
- ]);
216
+ }, [dispatch, supabase, cacheFirst, cacheTtlMs, handleIndexedDbError]);
395
217
  useEffect(() => {
396
218
  if (autoRefresh) {
397
- handleRefresh();
219
+ void handleRefresh({ force: false });
398
220
  }
399
221
  }, [autoRefresh, handleRefresh]);
400
- // Full refetch when nav goes online, only if min interval since last query has passed
401
222
  useEffect(() => {
402
- if (typeof window === "undefined")
223
+ if (!autoRefresh || typeof window === "undefined")
403
224
  return;
404
225
  const handleOnline = () => {
405
226
  const now = Date.now();
406
227
  if (now - lastQueryAtRef.current >= onlineRefetchMinIntervalMs) {
407
- handleRefresh();
228
+ void handleRefresh({ force: true });
408
229
  }
409
230
  };
410
231
  window.addEventListener("online", handleOnline);
411
232
  return () => window.removeEventListener("online", handleOnline);
412
- }, [handleRefresh, onlineRefetchMinIntervalMs]);
413
- // Utility function to clear cache
233
+ }, [autoRefresh, handleRefresh, onlineRefetchMinIntervalMs]);
414
234
  const clearCache = useCallback(async () => {
415
235
  try {
416
236
  await scoutCache.clearHerdModules();
@@ -33,4 +33,4 @@ export interface ScoutRefreshProviderProps extends UseScoutRefreshOptions {
33
33
  children: ReactNode;
34
34
  abilityParams?: ScoutRefreshAbilityParams;
35
35
  }
36
- export declare function ScoutRefreshProvider({ children, abilityParams, ...refreshOptions }: ScoutRefreshProviderProps): import("react/jsx-runtime").JSX.Element;
36
+ export declare function ScoutRefreshProvider({ children, abilityParams, supabase: supabaseOption, ...refreshOptions }: ScoutRefreshProviderProps): import("react/jsx-runtime").JSX.Element;