@adventurelabs/scout-core 1.0.84 → 1.0.86

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.
@@ -4,6 +4,7 @@ export interface CacheMetadata {
4
4
  timestamp: number;
5
5
  ttl: number;
6
6
  version: string;
7
+ dbVersion: number;
7
8
  etag?: string;
8
9
  lastModified?: number;
9
10
  }
@@ -30,11 +31,16 @@ export interface TimingStats {
30
31
  dataProcessing: number;
31
32
  localStorage: number;
32
33
  }
34
+ export interface DatabaseHealth {
35
+ healthy: boolean;
36
+ issues: string[];
37
+ }
33
38
  export declare class ScoutCache {
34
39
  private db;
35
40
  private initPromise;
36
41
  private stats;
37
42
  private init;
43
+ private validateDatabaseSchema;
38
44
  setHerdModules(herdModules: IHerdModule[], ttlMs?: number, etag?: string): Promise<void>;
39
45
  getHerdModules(): Promise<CacheResult<IHerdModule[]>>;
40
46
  clearHerdModules(): Promise<void>;
@@ -48,5 +54,9 @@ export declare class ScoutCache {
48
54
  }>;
49
55
  preloadCache(loadFunction: () => Promise<IHerdModule[]>, ttlMs?: number): Promise<void>;
50
56
  getDefaultTtl(): number;
57
+ getCurrentDbVersion(): number;
58
+ isCacheVersionCompatible(): Promise<boolean>;
59
+ resetDatabase(): Promise<void>;
60
+ checkDatabaseHealth(): Promise<DatabaseHealth>;
51
61
  }
52
62
  export declare const scoutCache: ScoutCache;
@@ -1,5 +1,5 @@
1
1
  const DB_NAME = "ScoutCache";
2
- const DB_VERSION = 1;
2
+ const DB_VERSION = 2; // Increment to invalidate old cache versions
3
3
  const HERD_MODULES_STORE = "herd_modules";
4
4
  const CACHE_METADATA_STORE = "cache_metadata";
5
5
  // Default TTL: 24 hours (1 day)
@@ -22,39 +22,78 @@ export class ScoutCache {
22
22
  const request = indexedDB.open(DB_NAME, DB_VERSION);
23
23
  request.onerror = () => {
24
24
  console.error("[ScoutCache] Failed to open IndexedDB:", request.error);
25
+ this.db = null;
26
+ this.initPromise = null;
25
27
  reject(request.error);
26
28
  };
27
29
  request.onsuccess = () => {
28
30
  this.db = request.result;
29
31
  console.log("[ScoutCache] IndexedDB initialized successfully");
32
+ // Add error handler for runtime database errors
33
+ this.db.onerror = (event) => {
34
+ console.error("[ScoutCache] Database error:", event);
35
+ };
30
36
  resolve();
31
37
  };
32
38
  request.onupgradeneeded = (event) => {
33
39
  const db = event.target.result;
34
- // Create herd modules store
35
- if (!db.objectStoreNames.contains(HERD_MODULES_STORE)) {
40
+ try {
41
+ console.log(`[ScoutCache] Upgrading database to version ${DB_VERSION}`);
42
+ // Remove all existing object stores to ensure clean slate
43
+ const existingStores = Array.from(db.objectStoreNames);
44
+ for (const storeName of existingStores) {
45
+ console.log(`[ScoutCache] Removing existing object store: ${storeName}`);
46
+ db.deleteObjectStore(storeName);
47
+ }
48
+ // Create herd modules store (unified storage for all herd data)
36
49
  const herdModulesStore = db.createObjectStore(HERD_MODULES_STORE, {
37
50
  keyPath: "herdId",
38
51
  });
39
52
  herdModulesStore.createIndex("timestamp", "timestamp", {
40
53
  unique: false,
41
54
  });
42
- }
43
- // Create cache metadata store
44
- if (!db.objectStoreNames.contains(CACHE_METADATA_STORE)) {
55
+ herdModulesStore.createIndex("dbVersion", "dbVersion", {
56
+ unique: false,
57
+ });
58
+ console.log("[ScoutCache] Created herd_modules object store");
59
+ // Create cache metadata store
45
60
  const metadataStore = db.createObjectStore(CACHE_METADATA_STORE, {
46
61
  keyPath: "key",
47
62
  });
63
+ console.log("[ScoutCache] Created cache_metadata object store");
64
+ console.log(`[ScoutCache] Database schema upgrade to version ${DB_VERSION} completed`);
65
+ }
66
+ catch (error) {
67
+ console.error("[ScoutCache] Error during database upgrade:", error);
68
+ reject(error);
48
69
  }
49
- console.log("[ScoutCache] Database schema upgraded");
70
+ };
71
+ request.onblocked = () => {
72
+ console.warn("[ScoutCache] Database upgrade blocked - other connections may need to be closed");
50
73
  };
51
74
  });
52
75
  return this.initPromise;
53
76
  }
77
+ validateDatabaseSchema() {
78
+ if (!this.db)
79
+ return false;
80
+ const hasHerdModulesStore = this.db.objectStoreNames.contains(HERD_MODULES_STORE);
81
+ const hasMetadataStore = this.db.objectStoreNames.contains(CACHE_METADATA_STORE);
82
+ if (!hasHerdModulesStore) {
83
+ console.error("[ScoutCache] Missing herd_modules object store");
84
+ }
85
+ if (!hasMetadataStore) {
86
+ console.error("[ScoutCache] Missing cache_metadata object store");
87
+ }
88
+ return hasHerdModulesStore && hasMetadataStore;
89
+ }
54
90
  async setHerdModules(herdModules, ttlMs = DEFAULT_TTL_MS, etag) {
55
91
  await this.init();
56
92
  if (!this.db)
57
93
  throw new Error("Database not initialized");
94
+ if (!this.validateDatabaseSchema()) {
95
+ throw new Error("Database schema validation failed - required object stores not found");
96
+ }
58
97
  const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readwrite");
59
98
  return new Promise((resolve, reject) => {
60
99
  transaction.onerror = () => reject(transaction.error);
@@ -62,13 +101,14 @@ export class ScoutCache {
62
101
  const herdModulesStore = transaction.objectStore(HERD_MODULES_STORE);
63
102
  const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
64
103
  const timestamp = Date.now();
65
- const version = "1.0.0";
66
- // Store each herd module
104
+ const version = "2.0.0";
105
+ // Store each herd module (contains all nested data - devices, events, zones, etc.)
67
106
  herdModules.forEach((herdModule) => {
68
107
  const cacheEntry = {
69
108
  herdId: herdModule.herd.id.toString(),
70
109
  data: herdModule,
71
110
  timestamp,
111
+ dbVersion: DB_VERSION,
72
112
  };
73
113
  herdModulesStore.put(cacheEntry);
74
114
  });
@@ -78,6 +118,7 @@ export class ScoutCache {
78
118
  timestamp,
79
119
  ttl: ttlMs,
80
120
  version,
121
+ dbVersion: DB_VERSION,
81
122
  etag,
82
123
  lastModified: timestamp,
83
124
  };
@@ -88,6 +129,9 @@ export class ScoutCache {
88
129
  await this.init();
89
130
  if (!this.db)
90
131
  throw new Error("Database not initialized");
132
+ if (!this.validateDatabaseSchema()) {
133
+ throw new Error("Database schema validation failed - required object stores not found");
134
+ }
91
135
  const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readonly");
92
136
  return new Promise((resolve, reject) => {
93
137
  transaction.onerror = () => reject(transaction.error);
@@ -103,6 +147,17 @@ export class ScoutCache {
103
147
  resolve({ data: null, isStale: true, age: 0, metadata: null });
104
148
  return;
105
149
  }
150
+ // Check if cache is from an incompatible DB version
151
+ if (!metadata.dbVersion || metadata.dbVersion !== DB_VERSION) {
152
+ console.log(`[ScoutCache] Cache from incompatible DB version (${metadata.dbVersion || "unknown"} !== ${DB_VERSION}), invalidating`);
153
+ this.stats.misses++;
154
+ // Clear old cache asynchronously
155
+ this.clearHerdModules().catch((error) => {
156
+ console.warn("[ScoutCache] Failed to clear old cache:", error);
157
+ });
158
+ resolve({ data: null, isStale: true, age: 0, metadata: null });
159
+ return;
160
+ }
106
161
  const age = now - metadata.timestamp;
107
162
  const isStale = age > metadata.ttl;
108
163
  // Get all herd modules
@@ -110,7 +165,10 @@ export class ScoutCache {
110
165
  getAllRequest.onsuccess = () => {
111
166
  const cacheEntries = getAllRequest.result;
112
167
  const herdModules = cacheEntries
113
- .filter((entry) => entry.data && entry.data.herd && entry.data.herd.slug)
168
+ .filter((entry) => entry.data &&
169
+ entry.data.herd &&
170
+ entry.data.herd.slug &&
171
+ entry.dbVersion === DB_VERSION)
114
172
  .map((entry) => entry.data)
115
173
  .sort((a, b) => (a.herd?.slug || "").localeCompare(b.herd?.slug || ""));
116
174
  // Update stats
@@ -134,6 +192,9 @@ export class ScoutCache {
134
192
  await this.init();
135
193
  if (!this.db)
136
194
  throw new Error("Database not initialized");
195
+ if (!this.validateDatabaseSchema()) {
196
+ throw new Error("Database schema validation failed - required object stores not found");
197
+ }
137
198
  const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readwrite");
138
199
  return new Promise((resolve, reject) => {
139
200
  transaction.onerror = () => reject(transaction.error);
@@ -148,12 +209,16 @@ export class ScoutCache {
148
209
  await this.init();
149
210
  if (!this.db)
150
211
  throw new Error("Database not initialized");
212
+ if (!this.validateDatabaseSchema()) {
213
+ throw new Error("Database schema validation failed - required object stores not found");
214
+ }
151
215
  const transaction = this.db.transaction([CACHE_METADATA_STORE], "readwrite");
152
216
  return new Promise((resolve, reject) => {
153
217
  transaction.onerror = () => reject(transaction.error);
154
218
  transaction.oncomplete = () => resolve();
155
219
  const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
156
220
  metadataStore.delete("herd_modules");
221
+ metadataStore.delete("providers");
157
222
  });
158
223
  }
159
224
  async getCacheStats() {
@@ -180,7 +245,6 @@ export class ScoutCache {
180
245
  const result = await this.getHerdModules();
181
246
  return result.age;
182
247
  }
183
- // Method to check if we should refresh based on various conditions
184
248
  async shouldRefresh(maxAgeMs, forceRefresh) {
185
249
  if (forceRefresh) {
186
250
  return { shouldRefresh: true, reason: "Force refresh requested" };
@@ -189,6 +253,15 @@ export class ScoutCache {
189
253
  if (!result.data || result.data.length === 0) {
190
254
  return { shouldRefresh: true, reason: "No cached data" };
191
255
  }
256
+ // Check for DB version mismatch
257
+ if (!result.metadata ||
258
+ !result.metadata.dbVersion ||
259
+ result.metadata.dbVersion !== DB_VERSION) {
260
+ return {
261
+ shouldRefresh: true,
262
+ reason: `Cache from incompatible DB version (${result.metadata?.dbVersion || "unknown"} !== ${DB_VERSION})`,
263
+ };
264
+ }
192
265
  if (result.isStale) {
193
266
  return { shouldRefresh: true, reason: "Cache is stale" };
194
267
  }
@@ -200,7 +273,6 @@ export class ScoutCache {
200
273
  }
201
274
  return { shouldRefresh: false, reason: "Cache is valid and fresh" };
202
275
  }
203
- // Method to preload cache with background refresh
204
276
  async preloadCache(loadFunction, ttlMs = DEFAULT_TTL_MS) {
205
277
  try {
206
278
  console.log("[ScoutCache] Starting background cache preload...");
@@ -214,10 +286,85 @@ export class ScoutCache {
214
286
  console.warn("[ScoutCache] Background preload failed:", error);
215
287
  }
216
288
  }
217
- // Get the default TTL value
218
289
  getDefaultTtl() {
219
290
  return DEFAULT_TTL_MS;
220
291
  }
292
+ getCurrentDbVersion() {
293
+ return DB_VERSION;
294
+ }
295
+ async isCacheVersionCompatible() {
296
+ try {
297
+ const result = await this.getHerdModules();
298
+ if (!result.metadata)
299
+ return false;
300
+ return (result.metadata.dbVersion !== undefined &&
301
+ result.metadata.dbVersion === DB_VERSION);
302
+ }
303
+ catch (error) {
304
+ console.warn("[ScoutCache] Version compatibility check failed:", error);
305
+ return false;
306
+ }
307
+ }
308
+ async resetDatabase() {
309
+ console.log("[ScoutCache] Resetting database...");
310
+ // Close existing connection
311
+ if (this.db) {
312
+ this.db.close();
313
+ this.db = null;
314
+ }
315
+ this.initPromise = null;
316
+ // Delete the database
317
+ return new Promise((resolve, reject) => {
318
+ const deleteRequest = indexedDB.deleteDatabase(DB_NAME);
319
+ deleteRequest.onsuccess = () => {
320
+ console.log("[ScoutCache] Database reset successfully");
321
+ resolve();
322
+ };
323
+ deleteRequest.onerror = () => {
324
+ console.error("[ScoutCache] Failed to reset database:", deleteRequest.error);
325
+ reject(deleteRequest.error);
326
+ };
327
+ deleteRequest.onblocked = () => {
328
+ console.warn("[ScoutCache] Database reset blocked - close all other tabs");
329
+ // Continue anyway, it will resolve when unblocked
330
+ };
331
+ });
332
+ }
333
+ async checkDatabaseHealth() {
334
+ const issues = [];
335
+ try {
336
+ await this.init();
337
+ if (!this.db) {
338
+ issues.push("Database connection not established");
339
+ return { healthy: false, issues };
340
+ }
341
+ if (!this.validateDatabaseSchema()) {
342
+ issues.push("Database schema validation failed");
343
+ }
344
+ // Check version compatibility
345
+ const isVersionCompatible = await this.isCacheVersionCompatible();
346
+ if (!isVersionCompatible) {
347
+ issues.push(`Cache version incompatible (current: ${DB_VERSION})`);
348
+ }
349
+ // Try a simple read operation
350
+ try {
351
+ const result = await this.getHerdModules();
352
+ if (result.data === null && result.age === 0) {
353
+ // This is expected for empty cache, not an error
354
+ }
355
+ }
356
+ catch (error) {
357
+ issues.push(`Read operation failed: ${error}`);
358
+ }
359
+ }
360
+ catch (error) {
361
+ issues.push(`Database initialization failed: ${error}`);
362
+ }
363
+ return {
364
+ healthy: issues.length === 0,
365
+ issues,
366
+ };
367
+ }
221
368
  }
222
369
  // Singleton instance
223
370
  export const scoutCache = new ScoutCache();
@@ -1,4 +1,4 @@
1
- import { CacheStats, TimingStats } from "../helpers/cache";
1
+ import { CacheStats, TimingStats, DatabaseHealth } from "../helpers/cache";
2
2
  export interface UseScoutRefreshOptions {
3
3
  autoRefresh?: boolean;
4
4
  onRefreshComplete?: () => void;
@@ -41,4 +41,8 @@ export declare function useScoutRefresh(options?: UseScoutRefreshOptions): {
41
41
  getTimingStats: () => TimingStats;
42
42
  clearCache: () => Promise<void>;
43
43
  getCacheStats: () => Promise<CacheStats>;
44
+ checkDatabaseHealth: () => Promise<DatabaseHealth>;
45
+ resetDatabase: () => Promise<void>;
46
+ isCacheVersionCompatible: () => Promise<boolean>;
47
+ getCurrentDbVersion: () => number;
44
48
  };
@@ -4,7 +4,7 @@ import { EnumScoutStateStatus, setActiveHerdId, setHerdModules, setStatus, setHe
4
4
  import { EnumHerdModulesLoadingState } from "../types/herd_module";
5
5
  import { server_load_herd_modules } from "../helpers/herds";
6
6
  import { server_get_user } from "../helpers/users";
7
- import { scoutCache } from "../helpers/cache";
7
+ import { scoutCache, } from "../helpers/cache";
8
8
  import { EnumDataSource } from "../types/data_source";
9
9
  /**
10
10
  * Hook for refreshing scout data with detailed timing measurements and cache-first loading
@@ -107,7 +107,7 @@ export function useScoutRefresh(options = {}) {
107
107
  const backgroundStartTime = Date.now();
108
108
  const [backgroundHerdModulesResult, backgroundUserResult] = await Promise.all([
109
109
  server_load_herd_modules(),
110
- server_get_user()
110
+ server_get_user(),
111
111
  ]);
112
112
  const backgroundDuration = Date.now() - backgroundStartTime;
113
113
  console.log(`[useScoutRefresh] Background fetch completed in ${backgroundDuration}ms`);
@@ -123,6 +123,21 @@ export function useScoutRefresh(options = {}) {
123
123
  }
124
124
  catch (cacheError) {
125
125
  console.warn("[useScoutRefresh] Background cache save failed:", cacheError);
126
+ // If it's an IndexedDB object store error, try to reset the database
127
+ if (cacheError instanceof Error &&
128
+ (cacheError.message.includes("object store") ||
129
+ cacheError.message.includes("NotFoundError"))) {
130
+ console.log("[useScoutRefresh] Attempting database reset due to schema error...");
131
+ try {
132
+ await scoutCache.resetDatabase();
133
+ console.log("[useScoutRefresh] Database reset successful, retrying cache save...");
134
+ await scoutCache.setHerdModules(backgroundHerdModulesResult.data, cacheTtlMs);
135
+ console.log("[useScoutRefresh] Cache save successful after database reset");
136
+ }
137
+ catch (resetError) {
138
+ console.error("[useScoutRefresh] Database reset and retry failed:", resetError);
139
+ }
140
+ }
126
141
  }
127
142
  // Update store with fresh data
128
143
  dispatch(setHerdModules(backgroundHerdModulesResult.data));
@@ -157,6 +172,19 @@ export function useScoutRefresh(options = {}) {
157
172
  }
158
173
  catch (cacheError) {
159
174
  console.warn("[useScoutRefresh] Cache load failed:", cacheError);
175
+ // If it's an IndexedDB object store error, try to reset the database
176
+ if (cacheError instanceof Error &&
177
+ (cacheError.message.includes("object store") ||
178
+ cacheError.message.includes("NotFoundError"))) {
179
+ console.log("[useScoutRefresh] Attempting database reset due to cache load error...");
180
+ try {
181
+ await scoutCache.resetDatabase();
182
+ console.log("[useScoutRefresh] Database reset successful");
183
+ }
184
+ catch (resetError) {
185
+ console.error("[useScoutRefresh] Database reset failed:", resetError);
186
+ }
187
+ }
160
188
  // Continue with API call
161
189
  }
162
190
  }
@@ -223,6 +251,21 @@ export function useScoutRefresh(options = {}) {
223
251
  }
224
252
  catch (cacheError) {
225
253
  console.warn("[useScoutRefresh] Cache save failed:", cacheError);
254
+ // If it's an IndexedDB object store error, try to reset the database
255
+ if (cacheError instanceof Error &&
256
+ (cacheError.message.includes("object store") ||
257
+ cacheError.message.includes("NotFoundError"))) {
258
+ console.log("[useScoutRefresh] Attempting database reset due to cache save error...");
259
+ try {
260
+ await scoutCache.resetDatabase();
261
+ console.log("[useScoutRefresh] Database reset successful, retrying cache save...");
262
+ await scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs);
263
+ console.log("[useScoutRefresh] Cache save successful after database reset");
264
+ }
265
+ catch (resetError) {
266
+ console.error("[useScoutRefresh] Database reset and retry failed:", resetError);
267
+ }
268
+ }
226
269
  }
227
270
  // Step 4: Update store with fresh data
228
271
  const dataProcessingStartTime = Date.now();
@@ -345,10 +388,52 @@ export function useScoutRefresh(options = {}) {
345
388
  };
346
389
  }
347
390
  }, []);
391
+ // Utility function to check database health
392
+ const checkDatabaseHealth = useCallback(async () => {
393
+ try {
394
+ return await scoutCache.checkDatabaseHealth();
395
+ }
396
+ catch (error) {
397
+ console.error("[useScoutRefresh] Failed to check database health:", error);
398
+ return {
399
+ healthy: false,
400
+ issues: [`Health check failed: ${error}`],
401
+ };
402
+ }
403
+ }, []);
404
+ // Utility function to reset database
405
+ const resetDatabase = useCallback(async () => {
406
+ try {
407
+ await scoutCache.resetDatabase();
408
+ console.log("[useScoutRefresh] Database reset successfully");
409
+ }
410
+ catch (error) {
411
+ console.error("[useScoutRefresh] Failed to reset database:", error);
412
+ throw error;
413
+ }
414
+ }, []);
415
+ // Utility function to check cache version compatibility
416
+ const isCacheVersionCompatible = useCallback(async () => {
417
+ try {
418
+ return await scoutCache.isCacheVersionCompatible();
419
+ }
420
+ catch (error) {
421
+ console.error("[useScoutRefresh] Failed to check cache version compatibility:", error);
422
+ return false;
423
+ }
424
+ }, []);
425
+ // Utility function to get current DB version
426
+ const getCurrentDbVersion = useCallback(() => {
427
+ return scoutCache.getCurrentDbVersion();
428
+ }, []);
348
429
  return {
349
430
  handleRefresh,
350
431
  getTimingStats,
351
432
  clearCache,
352
433
  getCacheStats,
434
+ checkDatabaseHealth,
435
+ resetDatabase,
436
+ isCacheVersionCompatible,
437
+ getCurrentDbVersion,
353
438
  };
354
439
  }
package/dist/index.d.ts CHANGED
@@ -30,6 +30,7 @@ export * from "./helpers/web";
30
30
  export * from "./helpers/zones";
31
31
  export * from "./helpers/storage";
32
32
  export * from "./helpers/eventUtils";
33
+ export * from "./helpers/cache";
33
34
  export * from "./hooks/useScoutDbListener";
34
35
  export * from "./hooks/useScoutRefresh";
35
36
  export * from "./providers";
package/dist/index.js CHANGED
@@ -33,6 +33,7 @@ export * from "./helpers/web";
33
33
  export * from "./helpers/zones";
34
34
  export * from "./helpers/storage";
35
35
  export * from "./helpers/eventUtils";
36
+ export * from "./helpers/cache";
36
37
  // Hooks
37
38
  export * from "./hooks/useScoutDbListener";
38
39
  export * from "./hooks/useScoutRefresh";
@@ -22,6 +22,7 @@ export type IHerd = Database["public"]["Tables"]["herds"]["Row"];
22
22
  export type ISession = Database["public"]["Tables"]["sessions"]["Row"];
23
23
  export type IConnectivity = Database["public"]["Tables"]["connectivity"]["Row"];
24
24
  export type IHeartbeat = Database["public"]["Tables"]["heartbeats"]["Row"];
25
+ export type IProvider = Database["public"]["Tables"]["providers"]["Row"];
25
26
  export type IEventWithTags = Database["public"]["CompositeTypes"]["event_with_tags"] & {
26
27
  earthranger_url: string | null;
27
28
  file_path: string | null;
@@ -1,5 +1,5 @@
1
1
  import { SupabaseClient } from "@supabase/supabase-js";
2
- import { IDevice, IEventWithTags, IHerd, IPlan, ILayer, IUserAndRole, IZoneWithActions, ISessionWithCoordinates } from "../types/db";
2
+ import { IDevice, IEventWithTags, IHerd, IPlan, ILayer, IProvider, IUserAndRole, IZoneWithActions, ISessionWithCoordinates } from "../types/db";
3
3
  import { EnumWebResponse } from "./requests";
4
4
  export declare enum EnumHerdModulesLoadingState {
5
5
  NOT_LOADING = "NOT_LOADING",
@@ -21,7 +21,8 @@ export declare class HerdModule {
21
21
  labels: string[];
22
22
  plans: IPlan[];
23
23
  layers: ILayer[];
24
- constructor(herd: IHerd, devices: IDevice[], events: IEventWithTags[], timestamp_last_refreshed: number, user_roles?: IUserAndRole[] | null, events_page_index?: number, total_events?: number, total_events_with_filters?: number, labels?: string[], plans?: IPlan[], zones?: IZoneWithActions[], sessions?: ISessionWithCoordinates[], layers?: ILayer[]);
24
+ providers: IProvider[];
25
+ constructor(herd: IHerd, devices: IDevice[], events: IEventWithTags[], timestamp_last_refreshed: number, user_roles?: IUserAndRole[] | null, events_page_index?: number, total_events?: number, total_events_with_filters?: number, labels?: string[], plans?: IPlan[], zones?: IZoneWithActions[], sessions?: ISessionWithCoordinates[], layers?: ILayer[], providers?: IProvider[]);
25
26
  to_serializable(): IHerdModule;
26
27
  static from_herd(herd: IHerd, client: SupabaseClient): Promise<HerdModule>;
27
28
  }
@@ -39,6 +40,7 @@ export interface IHerdModule {
39
40
  zones: IZoneWithActions[];
40
41
  sessions: ISessionWithCoordinates[];
41
42
  layers: ILayer[];
43
+ providers: IProvider[];
42
44
  }
43
45
  export interface IHerdModulesResponse {
44
46
  data: IHerdModule[];
@@ -4,6 +4,7 @@ import { server_get_total_events_by_herd } from "../helpers/events";
4
4
  import { EnumSessionsVisibility } from "./events";
5
5
  import { server_get_plans_by_herd } from "../helpers/plans";
6
6
  import { server_get_layers_by_herd } from "../helpers/layers";
7
+ import { server_get_providers_by_herd } from "../helpers/providers";
7
8
  import { server_get_events_and_tags_for_devices_batch } from "../helpers/tags";
8
9
  import { server_get_users_with_herd_access } from "../helpers/users";
9
10
  import { EnumWebResponse } from "./requests";
@@ -18,7 +19,7 @@ export var EnumHerdModulesLoadingState;
18
19
  EnumHerdModulesLoadingState["UNSUCCESSFULLY_LOADED"] = "UNSUCCESSFULLY_LOADED";
19
20
  })(EnumHerdModulesLoadingState || (EnumHerdModulesLoadingState = {}));
20
21
  export class HerdModule {
21
- constructor(herd, devices, events, timestamp_last_refreshed, user_roles = null, events_page_index = 0, total_events = 0, total_events_with_filters = 0, labels = [], plans = [], zones = [], sessions = [], layers = []) {
22
+ constructor(herd, devices, events, timestamp_last_refreshed, user_roles = null, events_page_index = 0, total_events = 0, total_events_with_filters = 0, labels = [], plans = [], zones = [], sessions = [], layers = [], providers = []) {
22
23
  this.user_roles = null;
23
24
  this.events_page_index = 0;
24
25
  this.total_events = 0;
@@ -26,6 +27,7 @@ export class HerdModule {
26
27
  this.labels = [];
27
28
  this.plans = [];
28
29
  this.layers = [];
30
+ this.providers = [];
29
31
  this.herd = herd;
30
32
  this.devices = devices;
31
33
  this.events = events;
@@ -39,6 +41,7 @@ export class HerdModule {
39
41
  this.zones = zones;
40
42
  this.sessions = sessions;
41
43
  this.layers = layers;
44
+ this.providers = providers;
42
45
  }
43
46
  to_serializable() {
44
47
  return {
@@ -55,6 +58,7 @@ export class HerdModule {
55
58
  zones: this.zones,
56
59
  sessions: this.sessions,
57
60
  layers: this.layers,
61
+ providers: this.providers,
58
62
  };
59
63
  }
60
64
  static async from_herd(herd, client) {
@@ -95,7 +99,7 @@ export class HerdModule {
95
99
  }
96
100
  }
97
101
  // Run all remaining requests in parallel with individual error handling
98
- const [res_zones, res_user_roles, total_event_count, res_plans, res_sessions, res_layers,] = await Promise.allSettled([
102
+ const [res_zones, res_user_roles, total_event_count, res_plans, res_sessions, res_layers, res_providers,] = await Promise.allSettled([
99
103
  server_get_more_zones_and_actions_for_herd(herd.id, 0, 10).catch((error) => {
100
104
  console.warn(`[HerdModule] Failed to get zones and actions:`, error);
101
105
  return { status: EnumWebResponse.ERROR, data: null };
@@ -114,12 +118,20 @@ export class HerdModule {
114
118
  }),
115
119
  server_get_sessions_by_herd_id(herd.id).catch((error) => {
116
120
  console.warn(`[HerdModule] Failed to get sessions:`, error);
117
- return { status: EnumWebResponse.ERROR, data: [], msg: error.message };
121
+ return {
122
+ status: EnumWebResponse.ERROR,
123
+ data: [],
124
+ msg: error.message,
125
+ };
118
126
  }),
119
127
  server_get_layers_by_herd(herd.id).catch((error) => {
120
128
  console.warn(`[HerdModule] Failed to get layers:`, error);
121
129
  return { status: EnumWebResponse.ERROR, data: null };
122
130
  }),
131
+ server_get_providers_by_herd(herd.id).catch((error) => {
132
+ console.warn(`[HerdModule] Failed to get providers:`, error);
133
+ return { status: EnumWebResponse.ERROR, data: null };
134
+ }),
123
135
  ]);
124
136
  // Assign recent events to devices from batch results
125
137
  for (let i = 0; i < new_devices.length; i++) {
@@ -148,23 +160,28 @@ export class HerdModule {
148
160
  const plans = res_plans.status === "fulfilled" && res_plans.value?.data
149
161
  ? res_plans.value.data
150
162
  : [];
151
- const sessions = res_sessions.status === "fulfilled" && res_sessions.value?.data ? res_sessions.value.data : [];
163
+ const sessions = res_sessions.status === "fulfilled" && res_sessions.value?.data
164
+ ? res_sessions.value.data
165
+ : [];
152
166
  const layers = res_layers.status === "fulfilled" && res_layers.value?.data
153
167
  ? res_layers.value.data
154
168
  : [];
169
+ const providers = res_providers.status === "fulfilled" && res_providers.value?.data
170
+ ? res_providers.value.data
171
+ : [];
155
172
  // TODO: store in DB and retrieve on load?
156
173
  const newLabels = LABELS;
157
174
  const endTime = Date.now();
158
175
  const loadTime = endTime - startTime;
159
176
  console.log(`[HerdModule] Loaded herd ${herd.slug} in ${loadTime}ms (${new_devices.length} devices)`);
160
- return new HerdModule(herd, new_devices, [], Date.now(), user_roles, 0, total_events, total_events, newLabels, plans, zones, sessions, layers);
177
+ return new HerdModule(herd, new_devices, [], Date.now(), user_roles, 0, total_events, total_events, newLabels, plans, zones, sessions, layers, providers);
161
178
  }
162
179
  catch (error) {
163
180
  const endTime = Date.now();
164
181
  const loadTime = endTime - startTime;
165
182
  console.error(`[HerdModule] Critical error in HerdModule.from_herd (${loadTime}ms):`, error);
166
183
  // Return a minimal but valid HerdModule instance to prevent complete failure
167
- return new HerdModule(herd, [], [], Date.now(), null, 0, 0, 0, [], [], [], [], []);
184
+ return new HerdModule(herd, [], [], Date.now(), null, 0, 0, 0, [], [], [], [], [], []);
168
185
  }
169
186
  }
170
187
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.84",
3
+ "version": "1.0.86",
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",