@adventurelabs/scout-core 1.0.130 → 1.0.132

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,4 +1,5 @@
1
1
  import { IHerdModule } from "../types/herd_module";
2
+ import { IVersionsSoftware } from "../types/db";
2
3
  export interface CacheMetadata {
3
4
  key: string;
4
5
  timestamp: number;
@@ -45,6 +46,7 @@ export declare class ScoutCache {
45
46
  getHerdModules(): Promise<CacheResult<IHerdModule[]>>;
46
47
  clearHerdModules(): Promise<void>;
47
48
  invalidateHerdModules(): Promise<void>;
49
+ invalidateVersionsSoftware(): Promise<void>;
48
50
  getCacheStats(): Promise<CacheStats>;
49
51
  isCacheValid(ttlMs?: number): Promise<boolean>;
50
52
  getCacheAge(): Promise<number>;
@@ -58,5 +60,8 @@ export declare class ScoutCache {
58
60
  isCacheVersionCompatible(): Promise<boolean>;
59
61
  resetDatabase(): Promise<void>;
60
62
  checkDatabaseHealth(): Promise<DatabaseHealth>;
63
+ setVersionsSoftware(versionsSoftware: IVersionsSoftware[], ttlMs?: number, etag?: string): Promise<void>;
64
+ getVersionsSoftware(): Promise<CacheResult<IVersionsSoftware[]>>;
65
+ clearVersionsSoftware(): Promise<void>;
61
66
  }
62
67
  export declare const scoutCache: ScoutCache;
@@ -1,9 +1,11 @@
1
1
  const DB_NAME = "ScoutCache";
2
- const DB_VERSION = 2; // Increment to invalidate old cache versions
2
+ const DB_VERSION = 3; // Increment to invalidate old cache versions
3
+ const CACHE_VERSION = "3.0.0"; // Cache metadata version string
3
4
  const HERD_MODULES_STORE = "herd_modules";
4
5
  const CACHE_METADATA_STORE = "cache_metadata";
5
- // Default TTL: 24 hours (1 day)
6
- const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
6
+ const VERSIONS_SOFTWARE_STORE = "versions_software";
7
+ // Default TTL: 24 hours (2 days)
8
+ const DEFAULT_TTL_MS = 2 * 24 * 60 * 60 * 1000;
7
9
  export class ScoutCache {
8
10
  constructor() {
9
11
  this.db = null;
@@ -61,6 +63,17 @@ export class ScoutCache {
61
63
  keyPath: "key",
62
64
  });
63
65
  console.log("[ScoutCache] Created cache_metadata object store");
66
+ // Create versions_software store
67
+ const versionsSoftwareStore = db.createObjectStore(VERSIONS_SOFTWARE_STORE, {
68
+ keyPath: "id",
69
+ });
70
+ versionsSoftwareStore.createIndex("system", "system", {
71
+ unique: false,
72
+ });
73
+ versionsSoftwareStore.createIndex("timestamp", "timestamp", {
74
+ unique: false,
75
+ });
76
+ console.log("[ScoutCache] Created versions_software object store");
64
77
  console.log(`[ScoutCache] Database schema upgrade to version ${DB_VERSION} completed`);
65
78
  }
66
79
  catch (error) {
@@ -79,13 +92,17 @@ export class ScoutCache {
79
92
  return false;
80
93
  const hasHerdModulesStore = this.db.objectStoreNames.contains(HERD_MODULES_STORE);
81
94
  const hasMetadataStore = this.db.objectStoreNames.contains(CACHE_METADATA_STORE);
95
+ const hasVersionsSoftwareStore = this.db.objectStoreNames.contains(VERSIONS_SOFTWARE_STORE);
82
96
  if (!hasHerdModulesStore) {
83
97
  console.error("[ScoutCache] Missing herd_modules object store");
84
98
  }
85
99
  if (!hasMetadataStore) {
86
100
  console.error("[ScoutCache] Missing cache_metadata object store");
87
101
  }
88
- return hasHerdModulesStore && hasMetadataStore;
102
+ if (!hasVersionsSoftwareStore) {
103
+ console.error("[ScoutCache] Missing versions_software object store");
104
+ }
105
+ return hasHerdModulesStore && hasMetadataStore && hasVersionsSoftwareStore;
89
106
  }
90
107
  async setHerdModules(herdModules, ttlMs = DEFAULT_TTL_MS, etag) {
91
108
  await this.init();
@@ -101,7 +118,7 @@ export class ScoutCache {
101
118
  const herdModulesStore = transaction.objectStore(HERD_MODULES_STORE);
102
119
  const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
103
120
  const timestamp = Date.now();
104
- const version = "2.0.0";
121
+ const version = CACHE_VERSION;
105
122
  // Store each herd module (contains all nested data - devices, events, zones, etc.)
106
123
  herdModules.forEach((herdModule) => {
107
124
  const cacheEntry = {
@@ -221,6 +238,21 @@ export class ScoutCache {
221
238
  metadataStore.delete("providers");
222
239
  });
223
240
  }
241
+ async invalidateVersionsSoftware() {
242
+ await this.init();
243
+ if (!this.db)
244
+ throw new Error("Database not initialized");
245
+ if (!this.validateDatabaseSchema()) {
246
+ throw new Error("Database schema validation failed - required object stores not found");
247
+ }
248
+ const transaction = this.db.transaction([CACHE_METADATA_STORE], "readwrite");
249
+ return new Promise((resolve, reject) => {
250
+ transaction.onerror = () => reject(transaction.error);
251
+ transaction.oncomplete = () => resolve();
252
+ const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
253
+ metadataStore.delete("versions_software");
254
+ });
255
+ }
224
256
  async getCacheStats() {
225
257
  const result = await this.getHerdModules();
226
258
  const totalRequests = this.stats.hits + this.stats.misses;
@@ -365,6 +397,135 @@ export class ScoutCache {
365
397
  issues,
366
398
  };
367
399
  }
400
+ async setVersionsSoftware(versionsSoftware, ttlMs = DEFAULT_TTL_MS, etag) {
401
+ await this.init();
402
+ if (!this.db)
403
+ throw new Error("Database not initialized");
404
+ if (!this.validateDatabaseSchema()) {
405
+ throw new Error("Database schema validation failed - required object stores not found");
406
+ }
407
+ const transaction = this.db.transaction([VERSIONS_SOFTWARE_STORE, CACHE_METADATA_STORE], "readwrite");
408
+ return new Promise((resolve, reject) => {
409
+ transaction.onerror = () => reject(transaction.error);
410
+ transaction.oncomplete = () => resolve();
411
+ const versionsSoftwareStore = transaction.objectStore(VERSIONS_SOFTWARE_STORE);
412
+ const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
413
+ const timestamp = Date.now();
414
+ const version = CACHE_VERSION;
415
+ // Clear existing versions_software data first
416
+ versionsSoftwareStore.clear();
417
+ // Store each software version with timestamp
418
+ versionsSoftware.forEach((version) => {
419
+ const cacheEntry = {
420
+ ...version,
421
+ timestamp,
422
+ dbVersion: DB_VERSION,
423
+ };
424
+ versionsSoftwareStore.put(cacheEntry);
425
+ });
426
+ // Store cache metadata
427
+ const metadata = {
428
+ key: "versions_software",
429
+ timestamp,
430
+ ttl: ttlMs,
431
+ version,
432
+ dbVersion: DB_VERSION,
433
+ etag,
434
+ lastModified: timestamp,
435
+ };
436
+ metadataStore.put(metadata);
437
+ });
438
+ }
439
+ async getVersionsSoftware() {
440
+ await this.init();
441
+ if (!this.db)
442
+ throw new Error("Database not initialized");
443
+ if (!this.validateDatabaseSchema()) {
444
+ throw new Error("Database schema validation failed - required object stores not found");
445
+ }
446
+ const transaction = this.db.transaction([VERSIONS_SOFTWARE_STORE, CACHE_METADATA_STORE], "readonly");
447
+ return new Promise((resolve, reject) => {
448
+ transaction.onerror = () => reject(transaction.error);
449
+ const versionsSoftwareStore = transaction.objectStore(VERSIONS_SOFTWARE_STORE);
450
+ const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
451
+ // Get metadata first
452
+ const metadataRequest = metadataStore.get("versions_software");
453
+ metadataRequest.onsuccess = () => {
454
+ const metadata = metadataRequest.result;
455
+ const now = Date.now();
456
+ if (!metadata) {
457
+ this.stats.misses++;
458
+ resolve({ data: null, isStale: true, age: 0, metadata: null });
459
+ return;
460
+ }
461
+ // Check if cache is from an incompatible DB version
462
+ if (!metadata.dbVersion || metadata.dbVersion !== DB_VERSION) {
463
+ console.log(`[ScoutCache] Versions software cache from incompatible DB version (${metadata.dbVersion || "unknown"} !== ${DB_VERSION}), invalidating`);
464
+ this.stats.misses++;
465
+ resolve({ data: null, isStale: true, age: 0, metadata });
466
+ return;
467
+ }
468
+ const age = now - metadata.timestamp;
469
+ const isStale = age > metadata.ttl;
470
+ if (isStale) {
471
+ this.stats.misses++;
472
+ console.log(`[ScoutCache] Versions software cache is stale (${Math.round(age / 1000)}s old, TTL: ${Math.round(metadata.ttl / 1000)}s)`);
473
+ resolve({ data: null, isStale: true, age, metadata });
474
+ return;
475
+ }
476
+ // Get all versions_software
477
+ const getAllRequest = versionsSoftwareStore.getAll();
478
+ getAllRequest.onsuccess = () => {
479
+ const cacheEntries = getAllRequest.result;
480
+ if (!cacheEntries || cacheEntries.length === 0) {
481
+ this.stats.misses++;
482
+ resolve({ data: null, isStale: false, age, metadata });
483
+ return;
484
+ }
485
+ // Extract versions_software data and remove cache metadata
486
+ const versionsSoftware = cacheEntries.map((entry) => {
487
+ const { timestamp, dbVersion, ...versionData } = entry;
488
+ return versionData;
489
+ });
490
+ this.stats.hits++;
491
+ console.log(`[ScoutCache] Found ${versionsSoftware.length} cached software versions (${Math.round(age / 1000)}s old)`);
492
+ resolve({
493
+ data: versionsSoftware,
494
+ isStale: false,
495
+ age,
496
+ metadata,
497
+ });
498
+ };
499
+ getAllRequest.onerror = () => {
500
+ this.stats.misses++;
501
+ console.error("[ScoutCache] Failed to get versions software:", getAllRequest.error);
502
+ resolve({ data: null, isStale: true, age, metadata });
503
+ };
504
+ };
505
+ metadataRequest.onerror = () => {
506
+ this.stats.misses++;
507
+ console.error("[ScoutCache] Failed to get versions software metadata:", metadataRequest.error);
508
+ resolve({ data: null, isStale: true, age: 0, metadata: null });
509
+ };
510
+ });
511
+ }
512
+ async clearVersionsSoftware() {
513
+ await this.init();
514
+ if (!this.db)
515
+ throw new Error("Database not initialized");
516
+ if (!this.validateDatabaseSchema()) {
517
+ throw new Error("Database schema validation failed - required object stores not found");
518
+ }
519
+ const transaction = this.db.transaction([VERSIONS_SOFTWARE_STORE, CACHE_METADATA_STORE], "readwrite");
520
+ return new Promise((resolve, reject) => {
521
+ transaction.onerror = () => reject(transaction.error);
522
+ transaction.oncomplete = () => resolve();
523
+ const versionsSoftwareStore = transaction.objectStore(VERSIONS_SOFTWARE_STORE);
524
+ const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
525
+ versionsSoftwareStore.clear();
526
+ metadataStore.delete("versions_software");
527
+ });
528
+ }
368
529
  }
369
530
  // Singleton instance
370
531
  export const scoutCache = new ScoutCache();
@@ -3,8 +3,10 @@ import { IVersionsSoftware, VersionsSoftwareInsert } from "../types/db";
3
3
  import { IWebResponseCompatible } from "../types/requests";
4
4
  import { SupabaseClient } from "@supabase/supabase-js";
5
5
  export declare function get_versions_software(client: SupabaseClient<Database>): Promise<IWebResponseCompatible<IVersionsSoftware[]>>;
6
+ export declare function get_versions_software_with_cache(client: SupabaseClient<Database>): Promise<IWebResponseCompatible<IVersionsSoftware[]>>;
6
7
  export declare function get_versions_software_by_system(client: SupabaseClient<Database>, system: string): Promise<IWebResponseCompatible<IVersionsSoftware[]>>;
7
8
  export declare function create_version_software(client: SupabaseClient<Database>, newVersionSoftware: VersionsSoftwareInsert): Promise<IWebResponseCompatible<IVersionsSoftware | null>>;
8
9
  export declare function update_version_software(client: SupabaseClient<Database>, version_id: number, updatedVersionSoftware: Partial<VersionsSoftwareInsert>): Promise<IWebResponseCompatible<IVersionsSoftware | null>>;
10
+ export declare function server_get_versions_software(): Promise<IWebResponseCompatible<IVersionsSoftware[]>>;
9
11
  export declare function delete_version_software(client: SupabaseClient<Database>, version_id: number): Promise<IWebResponseCompatible<IVersionsSoftware | null>>;
10
12
  export declare function get_versions_software_by_created_by(client: SupabaseClient<Database>, user_id: string): Promise<IWebResponseCompatible<IVersionsSoftware[]>>;
@@ -1,4 +1,5 @@
1
1
  import { IWebResponse } from "../types/requests";
2
+ import { scoutCache } from "./cache";
2
3
  export async function get_versions_software(client) {
3
4
  const { data, error } = await client
4
5
  .from("versions_software")
@@ -12,6 +13,35 @@ export async function get_versions_software(client) {
12
13
  }
13
14
  return IWebResponse.success(data).to_compatible();
14
15
  }
16
+ export async function get_versions_software_with_cache(client) {
17
+ try {
18
+ // Try to get from cache first
19
+ const cacheResult = await scoutCache.getVersionsSoftware();
20
+ if (cacheResult.data && !cacheResult.isStale) {
21
+ console.log(`[VersionsSoftware] Using cached data (${Math.round(cacheResult.age / 1000)}s old)`);
22
+ return IWebResponse.success(cacheResult.data).to_compatible();
23
+ }
24
+ // Cache miss or stale data - fetch from API
25
+ console.log("[VersionsSoftware] Cache miss or stale, fetching from API");
26
+ const apiResponse = await get_versions_software(client);
27
+ // If API request was successful, cache the result
28
+ if (apiResponse.status === "success" && apiResponse.data) {
29
+ try {
30
+ await scoutCache.setVersionsSoftware(apiResponse.data);
31
+ console.log(`[VersionsSoftware] Cached ${apiResponse.data.length} software versions`);
32
+ }
33
+ catch (cacheError) {
34
+ console.warn("[VersionsSoftware] Failed to cache data:", cacheError);
35
+ // Continue anyway, we still have the API data
36
+ }
37
+ }
38
+ return apiResponse;
39
+ }
40
+ catch (cacheError) {
41
+ console.warn("[VersionsSoftware] Cache error, falling back to API:", cacheError);
42
+ return get_versions_software(client);
43
+ }
44
+ }
15
45
  export async function get_versions_software_by_system(client, system) {
16
46
  const { data, error } = await client
17
47
  .from("versions_software")
@@ -38,6 +68,14 @@ export async function create_version_software(client, newVersionSoftware) {
38
68
  if (!data) {
39
69
  return IWebResponse.error("Failed to create software version").to_compatible();
40
70
  }
71
+ // Invalidate cache after successful creation
72
+ try {
73
+ await scoutCache.invalidateVersionsSoftware();
74
+ console.log("[VersionsSoftware] Cache invalidated after creation");
75
+ }
76
+ catch (cacheError) {
77
+ console.warn("[VersionsSoftware] Failed to invalidate cache:", cacheError);
78
+ }
41
79
  return IWebResponse.success(data).to_compatible();
42
80
  }
43
81
  export async function update_version_software(client, version_id, updatedVersionSoftware) {
@@ -58,8 +96,47 @@ export async function update_version_software(client, version_id, updatedVersion
58
96
  if (!data) {
59
97
  return IWebResponse.error("Software version not found or update failed").to_compatible();
60
98
  }
99
+ // Invalidate cache after successful update
100
+ try {
101
+ await scoutCache.invalidateVersionsSoftware();
102
+ console.log("[VersionsSoftware] Cache invalidated after update");
103
+ }
104
+ catch (cacheError) {
105
+ console.warn("[VersionsSoftware] Failed to invalidate cache:", cacheError);
106
+ }
61
107
  return IWebResponse.success(data).to_compatible();
62
108
  }
109
+ export async function server_get_versions_software() {
110
+ const { newServerClient } = await import("../supabase/server");
111
+ const client = await newServerClient();
112
+ try {
113
+ // Try to get from cache first
114
+ const cacheResult = await scoutCache.getVersionsSoftware();
115
+ if (cacheResult.data && !cacheResult.isStale) {
116
+ console.log(`[VersionsSoftware] Server using cached data (${Math.round(cacheResult.age / 1000)}s old)`);
117
+ return IWebResponse.success(cacheResult.data).to_compatible();
118
+ }
119
+ // Cache miss or stale data - fetch from API
120
+ console.log("[VersionsSoftware] Server cache miss or stale, fetching from API");
121
+ const apiResponse = await get_versions_software(client);
122
+ // If API request was successful, cache the result
123
+ if (apiResponse.status === "success" && apiResponse.data) {
124
+ try {
125
+ await scoutCache.setVersionsSoftware(apiResponse.data);
126
+ console.log(`[VersionsSoftware] Server cached ${apiResponse.data.length} software versions`);
127
+ }
128
+ catch (cacheError) {
129
+ console.warn("[VersionsSoftware] Server failed to cache data:", cacheError);
130
+ // Continue anyway, we still have the API data
131
+ }
132
+ }
133
+ return apiResponse;
134
+ }
135
+ catch (cacheError) {
136
+ console.warn("[VersionsSoftware] Server cache error, falling back to API:", cacheError);
137
+ return get_versions_software(client);
138
+ }
139
+ }
63
140
  export async function delete_version_software(client, version_id) {
64
141
  const { data, error } = await client
65
142
  .from("versions_software")
@@ -73,6 +150,14 @@ export async function delete_version_software(client, version_id) {
73
150
  if (!data) {
74
151
  return IWebResponse.error("Software version not found or deletion failed").to_compatible();
75
152
  }
153
+ // Invalidate cache after successful deletion
154
+ try {
155
+ await scoutCache.invalidateVersionsSoftware();
156
+ console.log("[VersionsSoftware] Cache invalidated after deletion");
157
+ }
158
+ catch (cacheError) {
159
+ console.warn("[VersionsSoftware] Failed to invalidate cache:", cacheError);
160
+ }
76
161
  return IWebResponse.success(data).to_compatible();
77
162
  }
78
163
  export async function get_versions_software_by_created_by(client, user_id) {
@@ -1,3 +1,4 @@
1
1
  export { useScoutRefresh, type UseScoutRefreshOptions, } from "./useScoutRefresh";
2
2
  export { useScoutRealtimeConnectivity } from "./useScoutRealtimeConnectivity";
3
3
  export { useScoutRealtimeDevices } from "./useScoutRealtimeDevices";
4
+ export { useScoutRealtimeVersionsSoftware } from "./useScoutRealtimeVersionsSoftware";
@@ -1,3 +1,4 @@
1
1
  export { useScoutRefresh, } from "./useScoutRefresh";
2
2
  export { useScoutRealtimeConnectivity } from "./useScoutRealtimeConnectivity";
3
3
  export { useScoutRealtimeDevices } from "./useScoutRealtimeDevices";
4
+ export { useScoutRealtimeVersionsSoftware } from "./useScoutRealtimeVersionsSoftware";
@@ -0,0 +1,5 @@
1
+ import { SupabaseClient } from "@supabase/supabase-js";
2
+ import { Database } from "../types/supabase";
3
+ import { IVersionsSoftware } from "../types/db";
4
+ import { RealtimeData } from "../types/realtime";
5
+ export declare function useScoutRealtimeVersionsSoftware(scoutSupabase: SupabaseClient<Database>): RealtimeData<IVersionsSoftware>[];
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import { useAppDispatch } from "../store/hooks";
3
+ import { useEffect, useRef, useCallback, useState } from "react";
4
+ import { upsertVersionSoftware, deleteVersionSoftwareById, } from "../store/scout";
5
+ import { EnumRealtimeOperation } from "../types/realtime";
6
+ export function useScoutRealtimeVersionsSoftware(scoutSupabase) {
7
+ const channels = useRef([]);
8
+ const dispatch = useAppDispatch();
9
+ const [newVersionsItems, setNewVersionsItems] = useState([]);
10
+ // Handle versions software broadcasts
11
+ const handleVersionsSoftwareBroadcast = useCallback((payload) => {
12
+ console.log("[VersionsSoftware] Broadcast received:", payload.payload.operation);
13
+ const data = payload.payload;
14
+ const versionData = data.record || data.old_record;
15
+ if (!versionData)
16
+ return;
17
+ let operation;
18
+ switch (data.operation) {
19
+ case "INSERT":
20
+ operation = EnumRealtimeOperation.INSERT;
21
+ if (data.record) {
22
+ console.log("[VersionsSoftware] New version received:", data.record);
23
+ dispatch(upsertVersionSoftware(data.record));
24
+ }
25
+ break;
26
+ case "UPDATE":
27
+ operation = EnumRealtimeOperation.UPDATE;
28
+ if (data.record) {
29
+ dispatch(upsertVersionSoftware(data.record));
30
+ }
31
+ break;
32
+ case "DELETE":
33
+ operation = EnumRealtimeOperation.DELETE;
34
+ if (data.old_record) {
35
+ dispatch(deleteVersionSoftwareById(data.old_record.id));
36
+ }
37
+ break;
38
+ default:
39
+ return;
40
+ }
41
+ console.log(`[VERSIONS_SOFTWARE] ${data.operation} received for version ${versionData.system}@${versionData.version}:`, JSON.stringify(versionData));
42
+ const realtimeData = {
43
+ data: versionData,
44
+ operation,
45
+ };
46
+ setNewVersionsItems((prev) => [realtimeData, ...prev]);
47
+ }, [dispatch]);
48
+ // Clear new items
49
+ const clearNewItems = useCallback(() => {
50
+ setNewVersionsItems([]);
51
+ }, []);
52
+ useEffect(() => {
53
+ if (!scoutSupabase)
54
+ return;
55
+ // Clean up existing channels
56
+ channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
57
+ channels.current = [];
58
+ // Clear previous items when setting up
59
+ clearNewItems();
60
+ // Create versions_software channel
61
+ const channel = scoutSupabase
62
+ .channel("versions_software_changes", { config: { private: true } })
63
+ .on("broadcast", { event: "*" }, handleVersionsSoftwareBroadcast)
64
+ .subscribe((status) => {
65
+ if (status === "SUBSCRIBED") {
66
+ console.log("[VERSIONS_SOFTWARE] ✅ Connected to software versions broadcasts");
67
+ }
68
+ else if (status === "CHANNEL_ERROR") {
69
+ console.warn("[VERSIONS_SOFTWARE] 🟡 Failed to connect to software versions broadcasts");
70
+ }
71
+ });
72
+ channels.current.push(channel);
73
+ return () => {
74
+ channels.current.forEach((ch) => scoutSupabase.removeChannel(ch));
75
+ channels.current = [];
76
+ };
77
+ }, [scoutSupabase, handleVersionsSoftwareBroadcast, clearNewItems]);
78
+ return newVersionsItems;
79
+ }
@@ -1,9 +1,10 @@
1
1
  import { useEffect, useCallback, useRef, useMemo } from "react";
2
2
  import { useAppDispatch } from "../store/hooks";
3
3
  import { useStore } from "react-redux";
4
- import { EnumScoutStateStatus, setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiServerProcessingDuration, setHerdModulesApiTotalRequestDuration, setUserApiDuration, setDataProcessingDuration, setCacheLoadDuration, setUser, setDataSource, setDataSourceInfo, } from "../store/scout";
4
+ import { EnumScoutStateStatus, setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiServerProcessingDuration, setHerdModulesApiTotalRequestDuration, setUserApiDuration, setDataProcessingDuration, setCacheLoadDuration, setUser, setDataSource, setDataSourceInfo, setVersionsSoftware, } from "../store/scout";
5
5
  import { EnumHerdModulesLoadingState } from "../types/herd_module";
6
6
  import { server_load_herd_modules } from "../helpers/herds";
7
+ import { server_get_versions_software } from "../helpers/versions_software";
7
8
  import { scoutCache } from "../helpers/cache";
8
9
  import { EnumDataSource } from "../types/data_source";
9
10
  import { createBrowserClient } from "@supabase/ssr";
@@ -50,158 +51,6 @@ export function useScoutRefresh(options = {}) {
50
51
  cacheSaveDuration: 0,
51
52
  });
52
53
  // Helper function for deep comparison of objects
53
- const deepEqual = useCallback((obj1, obj2, visited = new WeakMap()) => {
54
- if (obj1 === obj2)
55
- return true;
56
- if (obj1 == null || obj2 == null)
57
- return obj1 === obj2;
58
- if (typeof obj1 !== typeof obj2)
59
- return false;
60
- if (typeof obj1 !== "object")
61
- return obj1 === obj2;
62
- // Handle circular references
63
- if (visited.has(obj1)) {
64
- return visited.get(obj1) === obj2;
65
- }
66
- visited.set(obj1, obj2);
67
- // Handle Date objects
68
- if (obj1 instanceof Date && obj2 instanceof Date) {
69
- return obj1.getTime() === obj2.getTime();
70
- }
71
- if (Array.isArray(obj1) !== Array.isArray(obj2))
72
- return false;
73
- if (Array.isArray(obj1)) {
74
- if (obj1.length !== obj2.length)
75
- return false;
76
- for (let i = 0; i < obj1.length; i++) {
77
- if (!deepEqual(obj1[i], obj2[i], visited))
78
- return false;
79
- }
80
- return true;
81
- }
82
- const keys1 = Object.keys(obj1);
83
- const keys2 = Object.keys(obj2);
84
- if (keys1.length !== keys2.length)
85
- return false;
86
- for (const key of keys1) {
87
- if (!keys2.includes(key))
88
- return false;
89
- if (!deepEqual(obj1[key], obj2[key], visited))
90
- return false;
91
- }
92
- return true;
93
- }, []);
94
- // Helper function to sort herd modules consistently by ID
95
- const sortHerdModulesById = useCallback((herdModules) => {
96
- if (!Array.isArray(herdModules))
97
- return herdModules;
98
- return [...herdModules].sort((a, b) => {
99
- const aId = a?.herd?.id || 0;
100
- const bId = b?.herd?.id || 0;
101
- return aId - bId;
102
- });
103
- }, []);
104
- // Helper function to normalize herd modules for comparison (excludes timestamp metadata)
105
- const normalizeHerdModulesForComparison = useCallback((herdModules) => {
106
- if (!Array.isArray(herdModules))
107
- return herdModules;
108
- return herdModules.map((hm) => {
109
- if (!hm || typeof hm !== "object")
110
- return hm;
111
- // Create a copy without timestamp metadata that doesn't represent business data changes
112
- const { timestamp_last_refreshed, ...businessData } = hm;
113
- return businessData;
114
- });
115
- }, []);
116
- // Helper function to find what specifically changed for debugging
117
- const findBusinessDataChanges = useCallback((newData, currentData) => {
118
- if (!Array.isArray(newData) || !Array.isArray(currentData)) {
119
- return `Array type mismatch: new=${Array.isArray(newData)}, current=${Array.isArray(currentData)}`;
120
- }
121
- if (newData.length !== currentData.length) {
122
- return `Array length: ${currentData.length} → ${newData.length}`;
123
- }
124
- // Sort and normalize both for consistent comparison
125
- const sortedNew = normalizeHerdModulesForComparison(sortHerdModulesById(newData));
126
- const sortedCurrent = normalizeHerdModulesForComparison(sortHerdModulesById(currentData));
127
- const changes = [];
128
- for (let i = 0; i < sortedNew.length; i++) {
129
- const newHerd = sortedNew[i];
130
- const currentHerd = sortedCurrent[i];
131
- if (!newHerd || !currentHerd)
132
- continue;
133
- const herdName = newHerd.herd?.name || newHerd.name || `herd-${newHerd.herd?.id || i}`;
134
- // Check key business fields
135
- const businessFields = [
136
- "total_events",
137
- "total_events_with_filters",
138
- "events_page_index",
139
- ];
140
- businessFields.forEach((field) => {
141
- if (newHerd[field] !== currentHerd[field]) {
142
- changes.push(`${herdName}.${field}: ${currentHerd[field]} → ${newHerd[field]}`);
143
- }
144
- });
145
- // Check array lengths
146
- const arrayFields = [
147
- "devices",
148
- "events",
149
- "plans",
150
- "zones",
151
- "sessions",
152
- "layers",
153
- "providers",
154
- ];
155
- arrayFields.forEach((field) => {
156
- const newArray = newHerd[field];
157
- const currentArray = currentHerd[field];
158
- if (Array.isArray(newArray) && Array.isArray(currentArray)) {
159
- if (newArray.length !== currentArray.length) {
160
- changes.push(`${herdName}.${field}[]: ${currentArray.length} → ${newArray.length}`);
161
- }
162
- }
163
- });
164
- }
165
- return changes.length > 0
166
- ? changes.join(", ")
167
- : "No specific changes identified";
168
- }, [normalizeHerdModulesForComparison, sortHerdModulesById]);
169
- // Helper function to conditionally dispatch only if business data has changed
170
- const conditionalDispatch = useCallback((newData, currentData, actionCreator, dataType, skipTimestampOnlyUpdates = true) => {
171
- // For herd modules, sort both datasets by ID before comparison
172
- let dataToCompare = newData;
173
- let currentToCompare = currentData;
174
- if (dataType.includes("Herd modules")) {
175
- dataToCompare = sortHerdModulesById(newData);
176
- currentToCompare = sortHerdModulesById(currentData);
177
- // If we want to skip timestamp-only updates, normalize the data for comparison
178
- if (skipTimestampOnlyUpdates) {
179
- dataToCompare = normalizeHerdModulesForComparison(dataToCompare);
180
- currentToCompare =
181
- normalizeHerdModulesForComparison(currentToCompare);
182
- }
183
- }
184
- if (!deepEqual(dataToCompare, currentToCompare)) {
185
- console.log(`[useScoutRefresh] ${dataType} business data changed, updating store`);
186
- // Add debugging for unexpected business changes
187
- if (skipTimestampOnlyUpdates && dataType.includes("Herd modules")) {
188
- const changes = findBusinessDataChanges(dataToCompare, currentToCompare);
189
- console.log(`[useScoutRefresh] ${dataType} changes: ${changes}`);
190
- }
191
- dispatch(actionCreator(newData)); // Always dispatch original unsorted data
192
- return true;
193
- }
194
- else {
195
- console.log(`[useScoutRefresh] ${dataType} business data unchanged, skipping store update`);
196
- return false;
197
- }
198
- }, [
199
- dispatch,
200
- deepEqual,
201
- sortHerdModulesById,
202
- normalizeHerdModulesForComparison,
203
- findBusinessDataChanges,
204
- ]);
205
54
  // Helper function to handle IndexedDB errors - memoized for stability
206
55
  const handleIndexedDbError = useCallback(async (error, operation, retryFn) => {
207
56
  if (error instanceof Error &&
@@ -239,7 +88,10 @@ export function useScoutRefresh(options = {}) {
239
88
  if (cacheFirst) {
240
89
  const cacheStartTime = Date.now();
241
90
  try {
242
- const cacheResult = await scoutCache.getHerdModules();
91
+ const [cacheResult, versionsCacheResult] = await Promise.all([
92
+ scoutCache.getHerdModules(),
93
+ scoutCache.getVersionsSoftware(),
94
+ ]);
243
95
  cacheLoadDuration = Date.now() - cacheStartTime;
244
96
  timingRefs.current.cacheLoadDuration = cacheLoadDuration;
245
97
  dispatch(setCacheLoadDuration(cacheLoadDuration));
@@ -254,12 +106,14 @@ export function useScoutRefresh(options = {}) {
254
106
  cacheAge: cacheResult.age,
255
107
  isStale: cacheResult.isStale,
256
108
  }));
257
- // Conditionally update the store with cached data if business data is different
258
- // Get current state at execution time to avoid dependency issues
259
- const currentHerdModules = store.getState().scout.herd_modules;
260
- const herdModulesChanged = conditionalDispatch(cachedHerdModules, currentHerdModules, setHerdModules, "Herd modules (cache)", true);
261
- if (herdModulesChanged) {
262
- dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
109
+ // Update the store with cached data
110
+ console.log(`[useScoutRefresh] Updating store with cached herd modules`);
111
+ dispatch(setHerdModules(cachedHerdModules));
112
+ dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
113
+ // Load cached software versions if available
114
+ if (versionsCacheResult.data && !versionsCacheResult.isStale) {
115
+ console.log(`[useScoutRefresh] Loaded ${versionsCacheResult.data.length} software versions from cache`);
116
+ dispatch(setVersionsSoftware(versionsCacheResult.data));
263
117
  }
264
118
  // If cache is fresh, we still background fetch but don't wait
265
119
  if (!cacheResult.isStale) {
@@ -267,7 +121,7 @@ export function useScoutRefresh(options = {}) {
267
121
  (async () => {
268
122
  try {
269
123
  const backgroundStartTime = Date.now();
270
- const [backgroundHerdModulesResult, backgroundUserResult] = await Promise.all([
124
+ const [backgroundHerdModulesResult, backgroundUserResult, backgroundVersionsResult,] = await Promise.all([
271
125
  (async () => {
272
126
  const start = Date.now();
273
127
  const result = await server_load_herd_modules();
@@ -291,31 +145,61 @@ export function useScoutRefresh(options = {}) {
291
145
  dispatch(setUserApiDuration(duration));
292
146
  return { data: data.user, status: "success" };
293
147
  })(),
148
+ (async () => {
149
+ const start = Date.now();
150
+ const result = await server_get_versions_software();
151
+ const duration = Date.now() - start;
152
+ return { ...result, duration };
153
+ })(),
294
154
  ]);
295
155
  const backgroundDuration = Date.now() - backgroundStartTime;
296
156
  // Validate background responses
297
- if (backgroundHerdModulesResult.data &&
157
+ if (backgroundHerdModulesResult.status === "success" &&
158
+ backgroundHerdModulesResult.data &&
298
159
  Array.isArray(backgroundHerdModulesResult.data) &&
299
160
  backgroundUserResult &&
300
161
  backgroundUserResult.data) {
301
162
  // Update cache with fresh data
302
163
  try {
303
- await scoutCache.setHerdModules(backgroundHerdModulesResult.data, cacheTtlMs);
164
+ await Promise.all([
165
+ scoutCache.setHerdModules(backgroundHerdModulesResult.data, cacheTtlMs),
166
+ backgroundVersionsResult &&
167
+ backgroundVersionsResult.status === "success" &&
168
+ backgroundVersionsResult.data
169
+ ? scoutCache.setVersionsSoftware(backgroundVersionsResult.data, cacheTtlMs)
170
+ : Promise.resolve(),
171
+ ]);
304
172
  }
305
173
  catch (cacheError) {
306
174
  console.warn("[useScoutRefresh] Background cache save failed:", cacheError);
307
175
  await handleIndexedDbError(cacheError, "background cache save", async () => {
176
+ const promises = [];
308
177
  if (backgroundHerdModulesResult.data) {
309
- await scoutCache.setHerdModules(backgroundHerdModulesResult.data, cacheTtlMs);
178
+ promises.push(scoutCache.setHerdModules(backgroundHerdModulesResult.data, cacheTtlMs));
179
+ }
180
+ if (backgroundVersionsResult &&
181
+ backgroundVersionsResult.status === "success" &&
182
+ backgroundVersionsResult.data) {
183
+ promises.push(scoutCache.setVersionsSoftware(backgroundVersionsResult.data, cacheTtlMs));
184
+ }
185
+ if (promises.length > 0) {
186
+ await Promise.all(promises);
310
187
  }
311
188
  });
312
189
  }
313
- // Conditionally update store with fresh background data, skip timestamp-only changes
314
- const currentHerdModules = store.getState().scout.herd_modules;
315
- const currentUser = store.getState().scout.user;
316
- conditionalDispatch(backgroundHerdModulesResult.data, currentHerdModules, setHerdModules, "Herd modules (background)", true);
190
+ // Update store with fresh data from background request
191
+ console.log(`[useScoutRefresh] Updating store with background herd modules`);
192
+ dispatch(setHerdModules(backgroundHerdModulesResult.data));
317
193
  if (backgroundUserResult && backgroundUserResult.data) {
318
- conditionalDispatch(backgroundUserResult.data, currentUser, setUser, "User (background)");
194
+ console.log(`[useScoutRefresh] Updating store with background user data`);
195
+ dispatch(setUser(backgroundUserResult.data));
196
+ }
197
+ // Update software versions if successful
198
+ if (backgroundVersionsResult &&
199
+ backgroundVersionsResult.status === "success" &&
200
+ backgroundVersionsResult.data) {
201
+ console.log(`[useScoutRefresh] Updating store with background software versions`);
202
+ dispatch(setVersionsSoftware(backgroundVersionsResult.data));
319
203
  }
320
204
  // Update data source to DATABASE
321
205
  dispatch(setDataSource(EnumDataSource.DATABASE));
@@ -350,7 +234,7 @@ export function useScoutRefresh(options = {}) {
350
234
  }
351
235
  // Step 2: Load fresh data from API
352
236
  const parallelStartTime = Date.now();
353
- const [herdModulesResult, userResult] = await Promise.all([
237
+ const [herdModulesResult, userResult, versionsResult] = await Promise.all([
354
238
  (async () => {
355
239
  const start = Date.now();
356
240
  const result = await server_load_herd_modules();
@@ -368,6 +252,16 @@ export function useScoutRefresh(options = {}) {
368
252
  start,
369
253
  };
370
254
  })(),
255
+ (async () => {
256
+ const start = Date.now();
257
+ const result = await server_get_versions_software();
258
+ const duration = Date.now() - start;
259
+ return {
260
+ result,
261
+ duration,
262
+ start,
263
+ };
264
+ })(),
371
265
  ]);
372
266
  const parallelDuration = Date.now() - parallelStartTime;
373
267
  console.log(`[useScoutRefresh] Parallel API requests completed in ${parallelDuration}ms`);
@@ -411,7 +305,15 @@ export function useScoutRefresh(options = {}) {
411
305
  // Step 3: Update cache with fresh data
412
306
  const cacheSaveStartTime = Date.now();
413
307
  try {
414
- await scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs);
308
+ const cachePromises = [
309
+ scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs),
310
+ ];
311
+ // Cache software versions if available
312
+ if (versionsResult.result.status === "success" &&
313
+ versionsResult.result.data) {
314
+ cachePromises.push(scoutCache.setVersionsSoftware(versionsResult.result.data, cacheTtlMs));
315
+ }
316
+ await Promise.all(cachePromises);
415
317
  const cacheSaveDuration = Date.now() - cacheSaveStartTime;
416
318
  timingRefs.current.cacheSaveDuration = cacheSaveDuration;
417
319
  console.log(`[useScoutRefresh] Cache updated in ${cacheSaveDuration}ms with TTL: ${Math.round(cacheTtlMs / 1000)}s`);
@@ -419,18 +321,30 @@ export function useScoutRefresh(options = {}) {
419
321
  catch (cacheError) {
420
322
  console.warn("[useScoutRefresh] Cache save failed:", cacheError);
421
323
  await handleIndexedDbError(cacheError, "cache save", async () => {
422
- await scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs);
324
+ const retryPromises = [
325
+ scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs),
326
+ ];
327
+ if (versionsResult.result.status === "success" &&
328
+ versionsResult.result.data) {
329
+ retryPromises.push(scoutCache.setVersionsSoftware(versionsResult.result.data, cacheTtlMs));
330
+ }
331
+ await Promise.all(retryPromises);
423
332
  });
424
333
  }
425
334
  // Step 4: Conditionally update store with fresh data, skip timestamp-only changes
426
335
  const dataProcessingStartTime = Date.now();
427
- const currentHerdModules = store.getState().scout.herd_modules;
428
- const currentUser = store.getState().scout.user;
429
- const herdModulesChanged = conditionalDispatch(compatible_new_herd_modules, currentHerdModules, setHerdModules, "Herd modules (fresh API)", true);
430
- const userChanged = conditionalDispatch(res_new_user.data, currentUser, setUser, "User (fresh API)");
431
- if (herdModulesChanged) {
432
- dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
336
+ // Update store with new data
337
+ console.log(`[useScoutRefresh] Updating store with fresh herd modules`);
338
+ dispatch(setHerdModules(compatible_new_herd_modules));
339
+ console.log(`[useScoutRefresh] Updating store with fresh user data`);
340
+ dispatch(setUser(res_new_user.data));
341
+ // Update software versions
342
+ if (versionsResult.result.status === "success" &&
343
+ versionsResult.result.data) {
344
+ console.log(`[useScoutRefresh] Updating store with fresh software versions`);
345
+ dispatch(setVersionsSoftware(versionsResult.result.data));
433
346
  }
347
+ dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
434
348
  const dataProcessingDuration = Date.now() - dataProcessingStartTime;
435
349
  timingRefs.current.dataProcessingDuration = dataProcessingDuration;
436
350
  dispatch(setDataProcessingDuration(dataProcessingDuration));
@@ -469,7 +383,6 @@ export function useScoutRefresh(options = {}) {
469
383
  onRefreshComplete,
470
384
  cacheFirst,
471
385
  cacheTtlMs,
472
- conditionalDispatch,
473
386
  handleIndexedDbError,
474
387
  ]);
475
388
  useEffect(() => {
package/dist/index.d.ts CHANGED
@@ -40,6 +40,7 @@ export * from "./helpers/versions_software";
40
40
  export * from "./helpers/components";
41
41
  export * from "./hooks/useScoutRealtimeConnectivity";
42
42
  export * from "./hooks/useScoutRealtimeDevices";
43
+ export * from "./hooks/useScoutRealtimeVersionsSoftware";
43
44
  export * from "./hooks/useScoutRefresh";
44
45
  export * from "./providers";
45
46
  export * from "./store/scout";
package/dist/index.js CHANGED
@@ -44,6 +44,7 @@ export * from "./helpers/components";
44
44
  // Hooks
45
45
  export * from "./hooks/useScoutRealtimeConnectivity";
46
46
  export * from "./hooks/useScoutRealtimeDevices";
47
+ export * from "./hooks/useScoutRealtimeVersionsSoftware";
47
48
  export * from "./hooks/useScoutRefresh";
48
49
  // Providers
49
50
  export * from "./providers";
@@ -221,6 +221,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
221
221
  id: number;
222
222
  inserted_at: string;
223
223
  location: unknown;
224
+ mode: string | null;
224
225
  noise: number;
225
226
  session_id: number | null;
226
227
  signal: number;
@@ -241,6 +242,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
241
242
  id?: number;
242
243
  inserted_at?: string;
243
244
  location: unknown;
245
+ mode?: string | null;
244
246
  noise: number;
245
247
  session_id?: number | null;
246
248
  signal: number;
@@ -261,6 +263,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
261
263
  id?: number;
262
264
  inserted_at?: string;
263
265
  location?: unknown;
266
+ mode?: string | null;
264
267
  noise?: number;
265
268
  session_id?: number | null;
266
269
  signal?: number;
@@ -1,4 +1,4 @@
1
- import { IUser } from "../types/db";
1
+ import { IUser, IVersionsSoftware } from "../types/db";
2
2
  import { IHerdModule, EnumHerdModulesLoadingState } from "../types/herd_module";
3
3
  import { EnumDataSource, IDataSourceInfo } from "../types/data_source";
4
4
  import { MapDeviceIdToConnectivity } from "../types/connectivity";
@@ -23,6 +23,7 @@ export interface ScoutState {
23
23
  data_source: EnumDataSource;
24
24
  data_source_info: IDataSourceInfo | null;
25
25
  active_herd_gps_trackers_connectivity: MapDeviceIdToConnectivity;
26
+ versions_software: IVersionsSoftware[];
26
27
  }
27
28
  export interface RootState {
28
29
  scout: ScoutState;
@@ -172,7 +173,19 @@ export declare const scoutSlice: import("@reduxjs/toolkit").Slice<ScoutState, {
172
173
  payload: any;
173
174
  type: string;
174
175
  }) => void;
176
+ setVersionsSoftware: (state: import("immer").WritableDraft<ScoutState>, action: {
177
+ payload: any;
178
+ type: string;
179
+ }) => void;
180
+ upsertVersionSoftware: (state: import("immer").WritableDraft<ScoutState>, action: {
181
+ payload: any;
182
+ type: string;
183
+ }) => void;
184
+ deleteVersionSoftwareById: (state: import("immer").WritableDraft<ScoutState>, action: {
185
+ payload: any;
186
+ type: string;
187
+ }) => void;
175
188
  }, "scout", "scout", import("@reduxjs/toolkit").SliceSelectors<ScoutState>>;
176
- export declare const setHerdModules: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModules">, setStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setStatus">, setHerdModulesLoadingState: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesLoadingState">, setHerdModulesLoadedInMs: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesLoadedInMs">, setHerdModulesApiServerProcessingDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesApiServerProcessingDuration">, setHerdModulesApiTotalRequestDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesApiTotalRequestDuration">, setUserApiDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUserApiDuration">, setDataProcessingDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataProcessingDuration">, setCacheLoadDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setCacheLoadDuration">, setActiveHerdId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveHerdId">, setActiveDeviceId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveDeviceId">, setDataSource: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataSource">, setDataSourceInfo: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataSourceInfo">, appendEventsToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendEventsToHerdModule">, replaceEventsForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/replaceEventsForHerdModule">, appendArtifactsToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendArtifactsToHerdModule">, replaceArtifactsForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/replaceArtifactsForHerdModule">, updateEventValuesForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateEventValuesForHerdModule">, updatePageIndexForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updatePageIndexForHerdModule">, appendPlansToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendPlansToHerdModule">, setUser: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUser">, addTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addTag">, deleteTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteTag">, updateTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateTag">, addNewDeviceToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addNewDeviceToHerdModule">, updateDeviceForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateDeviceForHerdModule">, addDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addDevice">, deleteDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteDevice">, updateDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateDevice">, addPlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addPlan">, deletePlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deletePlan">, updatePlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updatePlan">, addSessionToStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addSessionToStore">, deleteSessionFromStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteSessionFromStore">, updateSessionInStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateSessionInStore">, setActiveHerdGpsTrackersConnectivity: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveHerdGpsTrackersConnectivity">;
189
+ export declare const setHerdModules: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModules">, setStatus: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setStatus">, setHerdModulesLoadingState: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesLoadingState">, setHerdModulesLoadedInMs: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesLoadedInMs">, setHerdModulesApiServerProcessingDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesApiServerProcessingDuration">, setHerdModulesApiTotalRequestDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesApiTotalRequestDuration">, setUserApiDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUserApiDuration">, setDataProcessingDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataProcessingDuration">, setCacheLoadDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setCacheLoadDuration">, setActiveHerdId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveHerdId">, setActiveDeviceId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveDeviceId">, setDataSource: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataSource">, setDataSourceInfo: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataSourceInfo">, appendEventsToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendEventsToHerdModule">, replaceEventsForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/replaceEventsForHerdModule">, appendArtifactsToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendArtifactsToHerdModule">, replaceArtifactsForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/replaceArtifactsForHerdModule">, updateEventValuesForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateEventValuesForHerdModule">, updatePageIndexForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updatePageIndexForHerdModule">, appendPlansToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendPlansToHerdModule">, setUser: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUser">, addTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addTag">, deleteTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteTag">, updateTag: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateTag">, addNewDeviceToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addNewDeviceToHerdModule">, updateDeviceForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateDeviceForHerdModule">, addDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addDevice">, deleteDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteDevice">, updateDevice: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateDevice">, addPlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addPlan">, deletePlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deletePlan">, updatePlan: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updatePlan">, addSessionToStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/addSessionToStore">, deleteSessionFromStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteSessionFromStore">, updateSessionInStore: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/updateSessionInStore">, setActiveHerdGpsTrackersConnectivity: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveHerdGpsTrackersConnectivity">, setVersionsSoftware: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setVersionsSoftware">, upsertVersionSoftware: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/upsertVersionSoftware">, deleteVersionSoftwareById: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/deleteVersionSoftwareById">;
177
190
  declare const _default: import("redux").Reducer<ScoutState>;
178
191
  export default _default;
@@ -25,6 +25,7 @@ const initialState = {
25
25
  data_source: EnumDataSource.UNKNOWN,
26
26
  data_source_info: null,
27
27
  active_herd_gps_trackers_connectivity: {},
28
+ versions_software: [],
28
29
  };
29
30
  export const scoutSlice = createSlice({
30
31
  name: "scout",
@@ -284,8 +285,27 @@ export const scoutSlice = createSlice({
284
285
  setActiveHerdGpsTrackersConnectivity: (state, action) => {
285
286
  state.active_herd_gps_trackers_connectivity = action.payload;
286
287
  },
288
+ setVersionsSoftware: (state, action) => {
289
+ state.versions_software = action.payload;
290
+ },
291
+ upsertVersionSoftware: (state, action) => {
292
+ const version = action.payload;
293
+ const existingIndex = state.versions_software.findIndex((v) => v.id === version.id);
294
+ if (existingIndex !== -1) {
295
+ // Update existing version
296
+ state.versions_software[existingIndex] = version;
297
+ }
298
+ else {
299
+ // Add new version
300
+ state.versions_software = [version, ...state.versions_software];
301
+ }
302
+ },
303
+ deleteVersionSoftwareById: (state, action) => {
304
+ const versionId = action.payload;
305
+ state.versions_software = state.versions_software.filter((version) => version.id !== versionId);
306
+ },
287
307
  },
288
308
  });
289
309
  // Action creators are generated for each case reducer function
290
- export const { setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiServerProcessingDuration, setHerdModulesApiTotalRequestDuration, setUserApiDuration, setDataProcessingDuration, setCacheLoadDuration, setActiveHerdId, setActiveDeviceId, setDataSource, setDataSourceInfo, appendEventsToHerdModule, replaceEventsForHerdModule, appendArtifactsToHerdModule, replaceArtifactsForHerdModule, updateEventValuesForHerdModule, updatePageIndexForHerdModule, appendPlansToHerdModule, setUser, addTag, deleteTag, updateTag, addNewDeviceToHerdModule, updateDeviceForHerdModule, addDevice, deleteDevice, updateDevice, addPlan, deletePlan, updatePlan, addSessionToStore, deleteSessionFromStore, updateSessionInStore, setActiveHerdGpsTrackersConnectivity, } = scoutSlice.actions;
310
+ export const { setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiServerProcessingDuration, setHerdModulesApiTotalRequestDuration, setUserApiDuration, setDataProcessingDuration, setCacheLoadDuration, setActiveHerdId, setActiveDeviceId, setDataSource, setDataSourceInfo, appendEventsToHerdModule, replaceEventsForHerdModule, appendArtifactsToHerdModule, replaceArtifactsForHerdModule, updateEventValuesForHerdModule, updatePageIndexForHerdModule, appendPlansToHerdModule, setUser, addTag, deleteTag, updateTag, addNewDeviceToHerdModule, updateDeviceForHerdModule, addDevice, deleteDevice, updateDevice, addPlan, deletePlan, updatePlan, addSessionToStore, deleteSessionFromStore, updateSessionInStore, setActiveHerdGpsTrackersConnectivity, setVersionsSoftware, upsertVersionSoftware, deleteVersionSoftwareById, } = scoutSlice.actions;
291
311
  export default scoutSlice.reducer;
@@ -230,6 +230,7 @@ export type Database = {
230
230
  id: number;
231
231
  inserted_at: string;
232
232
  location: unknown;
233
+ mode: string | null;
233
234
  noise: number;
234
235
  session_id: number | null;
235
236
  signal: number;
@@ -250,6 +251,7 @@ export type Database = {
250
251
  id?: number;
251
252
  inserted_at?: string;
252
253
  location: unknown;
254
+ mode?: string | null;
253
255
  noise: number;
254
256
  session_id?: number | null;
255
257
  signal: number;
@@ -270,6 +272,7 @@ export type Database = {
270
272
  id?: number;
271
273
  inserted_at?: string;
272
274
  location?: unknown;
275
+ mode?: string | null;
273
276
  noise?: number;
274
277
  session_id?: number | null;
275
278
  signal?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.130",
3
+ "version": "1.0.132",
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",