@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.
- package/dist/helpers/cache.d.ts +5 -0
- package/dist/helpers/cache.js +166 -5
- package/dist/helpers/versions_software.d.ts +2 -0
- package/dist/helpers/versions_software.js +85 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useScoutRealtimeVersionsSoftware.d.ts +5 -0
- package/dist/hooks/useScoutRealtimeVersionsSoftware.js +79 -0
- package/dist/hooks/useScoutRefresh.js +92 -179
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/providers/ScoutRefreshProvider.d.ts +3 -0
- package/dist/store/scout.d.ts +15 -2
- package/dist/store/scout.js +21 -1
- package/dist/types/supabase.d.ts +3 -0
- package/package.json +1 -1
package/dist/helpers/cache.d.ts
CHANGED
|
@@ -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;
|
package/dist/helpers/cache.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
const DB_NAME = "ScoutCache";
|
|
2
|
-
const DB_VERSION =
|
|
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
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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 =
|
|
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) {
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/hooks/index.js
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if
|
|
262
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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;
|
package/dist/store/scout.d.ts
CHANGED
|
@@ -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;
|
package/dist/store/scout.js
CHANGED
|
@@ -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;
|
package/dist/types/supabase.d.ts
CHANGED
|
@@ -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;
|