@adventurelabs/scout-core 1.0.84 → 1.0.85
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 +7 -0
- package/dist/helpers/cache.js +116 -14
- package/dist/hooks/useScoutRefresh.d.ts +3 -1
- package/dist/hooks/useScoutRefresh.js +71 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/dist/helpers/cache.d.ts
CHANGED
|
@@ -30,11 +30,16 @@ export interface TimingStats {
|
|
|
30
30
|
dataProcessing: number;
|
|
31
31
|
localStorage: number;
|
|
32
32
|
}
|
|
33
|
+
export interface DatabaseHealth {
|
|
34
|
+
healthy: boolean;
|
|
35
|
+
issues: string[];
|
|
36
|
+
}
|
|
33
37
|
export declare class ScoutCache {
|
|
34
38
|
private db;
|
|
35
39
|
private initPromise;
|
|
36
40
|
private stats;
|
|
37
41
|
private init;
|
|
42
|
+
private validateDatabaseSchema;
|
|
38
43
|
setHerdModules(herdModules: IHerdModule[], ttlMs?: number, etag?: string): Promise<void>;
|
|
39
44
|
getHerdModules(): Promise<CacheResult<IHerdModule[]>>;
|
|
40
45
|
clearHerdModules(): Promise<void>;
|
|
@@ -48,5 +53,7 @@ export declare class ScoutCache {
|
|
|
48
53
|
}>;
|
|
49
54
|
preloadCache(loadFunction: () => Promise<IHerdModule[]>, ttlMs?: number): Promise<void>;
|
|
50
55
|
getDefaultTtl(): number;
|
|
56
|
+
resetDatabase(): Promise<void>;
|
|
57
|
+
checkDatabaseHealth(): Promise<DatabaseHealth>;
|
|
51
58
|
}
|
|
52
59
|
export declare const scoutCache: ScoutCache;
|
package/dist/helpers/cache.js
CHANGED
|
@@ -22,39 +22,75 @@ export class ScoutCache {
|
|
|
22
22
|
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
23
23
|
request.onerror = () => {
|
|
24
24
|
console.error("[ScoutCache] Failed to open IndexedDB:", request.error);
|
|
25
|
+
this.db = null;
|
|
26
|
+
this.initPromise = null;
|
|
25
27
|
reject(request.error);
|
|
26
28
|
};
|
|
27
29
|
request.onsuccess = () => {
|
|
28
30
|
this.db = request.result;
|
|
31
|
+
// Validate that required object stores exist
|
|
32
|
+
if (!this.validateDatabaseSchema()) {
|
|
33
|
+
console.error("[ScoutCache] Database schema validation failed");
|
|
34
|
+
this.db.close();
|
|
35
|
+
this.db = null;
|
|
36
|
+
this.initPromise = null;
|
|
37
|
+
reject(new Error("Database schema validation failed"));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
29
40
|
console.log("[ScoutCache] IndexedDB initialized successfully");
|
|
30
41
|
resolve();
|
|
31
42
|
};
|
|
32
43
|
request.onupgradeneeded = (event) => {
|
|
33
44
|
const db = event.target.result;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
try {
|
|
46
|
+
// Create herd modules store
|
|
47
|
+
if (!db.objectStoreNames.contains(HERD_MODULES_STORE)) {
|
|
48
|
+
const herdModulesStore = db.createObjectStore(HERD_MODULES_STORE, {
|
|
49
|
+
keyPath: "herdId",
|
|
50
|
+
});
|
|
51
|
+
herdModulesStore.createIndex("timestamp", "timestamp", {
|
|
52
|
+
unique: false,
|
|
53
|
+
});
|
|
54
|
+
console.log("[ScoutCache] Created herd_modules object store");
|
|
55
|
+
}
|
|
56
|
+
// Create cache metadata store
|
|
57
|
+
if (!db.objectStoreNames.contains(CACHE_METADATA_STORE)) {
|
|
58
|
+
const metadataStore = db.createObjectStore(CACHE_METADATA_STORE, {
|
|
59
|
+
keyPath: "key",
|
|
60
|
+
});
|
|
61
|
+
console.log("[ScoutCache] Created cache_metadata object store");
|
|
62
|
+
}
|
|
63
|
+
console.log("[ScoutCache] Database schema upgraded");
|
|
42
64
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
keyPath: "key",
|
|
47
|
-
});
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error("[ScoutCache] Error during database upgrade:", error);
|
|
67
|
+
reject(error);
|
|
48
68
|
}
|
|
49
|
-
console.log("[ScoutCache] Database schema upgraded");
|
|
50
69
|
};
|
|
51
70
|
});
|
|
52
71
|
return this.initPromise;
|
|
53
72
|
}
|
|
73
|
+
validateDatabaseSchema() {
|
|
74
|
+
if (!this.db)
|
|
75
|
+
return false;
|
|
76
|
+
const hasHerdModulesStore = this.db.objectStoreNames.contains(HERD_MODULES_STORE);
|
|
77
|
+
const hasMetadataStore = this.db.objectStoreNames.contains(CACHE_METADATA_STORE);
|
|
78
|
+
if (!hasHerdModulesStore) {
|
|
79
|
+
console.error("[ScoutCache] Missing herd_modules object store");
|
|
80
|
+
}
|
|
81
|
+
if (!hasMetadataStore) {
|
|
82
|
+
console.error("[ScoutCache] Missing cache_metadata object store");
|
|
83
|
+
}
|
|
84
|
+
return hasHerdModulesStore && hasMetadataStore;
|
|
85
|
+
}
|
|
54
86
|
async setHerdModules(herdModules, ttlMs = DEFAULT_TTL_MS, etag) {
|
|
55
87
|
await this.init();
|
|
56
88
|
if (!this.db)
|
|
57
89
|
throw new Error("Database not initialized");
|
|
90
|
+
// Validate schema before creating transaction
|
|
91
|
+
if (!this.validateDatabaseSchema()) {
|
|
92
|
+
throw new Error("Database schema validation failed - required object stores not found");
|
|
93
|
+
}
|
|
58
94
|
const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readwrite");
|
|
59
95
|
return new Promise((resolve, reject) => {
|
|
60
96
|
transaction.onerror = () => reject(transaction.error);
|
|
@@ -88,6 +124,10 @@ export class ScoutCache {
|
|
|
88
124
|
await this.init();
|
|
89
125
|
if (!this.db)
|
|
90
126
|
throw new Error("Database not initialized");
|
|
127
|
+
// Validate schema before creating transaction
|
|
128
|
+
if (!this.validateDatabaseSchema()) {
|
|
129
|
+
throw new Error("Database schema validation failed - required object stores not found");
|
|
130
|
+
}
|
|
91
131
|
const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readonly");
|
|
92
132
|
return new Promise((resolve, reject) => {
|
|
93
133
|
transaction.onerror = () => reject(transaction.error);
|
|
@@ -134,6 +174,10 @@ export class ScoutCache {
|
|
|
134
174
|
await this.init();
|
|
135
175
|
if (!this.db)
|
|
136
176
|
throw new Error("Database not initialized");
|
|
177
|
+
// Validate schema before creating transaction
|
|
178
|
+
if (!this.validateDatabaseSchema()) {
|
|
179
|
+
throw new Error("Database schema validation failed - required object stores not found");
|
|
180
|
+
}
|
|
137
181
|
const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readwrite");
|
|
138
182
|
return new Promise((resolve, reject) => {
|
|
139
183
|
transaction.onerror = () => reject(transaction.error);
|
|
@@ -148,6 +192,10 @@ export class ScoutCache {
|
|
|
148
192
|
await this.init();
|
|
149
193
|
if (!this.db)
|
|
150
194
|
throw new Error("Database not initialized");
|
|
195
|
+
// Validate schema before creating transaction
|
|
196
|
+
if (!this.validateDatabaseSchema()) {
|
|
197
|
+
throw new Error("Database schema validation failed - required object stores not found");
|
|
198
|
+
}
|
|
151
199
|
const transaction = this.db.transaction([CACHE_METADATA_STORE], "readwrite");
|
|
152
200
|
return new Promise((resolve, reject) => {
|
|
153
201
|
transaction.onerror = () => reject(transaction.error);
|
|
@@ -218,6 +266,60 @@ export class ScoutCache {
|
|
|
218
266
|
getDefaultTtl() {
|
|
219
267
|
return DEFAULT_TTL_MS;
|
|
220
268
|
}
|
|
269
|
+
// Method to reset the database in case of corruption
|
|
270
|
+
async resetDatabase() {
|
|
271
|
+
console.log("[ScoutCache] Resetting database...");
|
|
272
|
+
// Close existing connection
|
|
273
|
+
if (this.db) {
|
|
274
|
+
this.db.close();
|
|
275
|
+
this.db = null;
|
|
276
|
+
}
|
|
277
|
+
this.initPromise = null;
|
|
278
|
+
// Delete the database
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
const deleteRequest = indexedDB.deleteDatabase(DB_NAME);
|
|
281
|
+
deleteRequest.onsuccess = () => {
|
|
282
|
+
console.log("[ScoutCache] Database reset successfully");
|
|
283
|
+
resolve();
|
|
284
|
+
};
|
|
285
|
+
deleteRequest.onerror = () => {
|
|
286
|
+
console.error("[ScoutCache] Failed to reset database:", deleteRequest.error);
|
|
287
|
+
reject(deleteRequest.error);
|
|
288
|
+
};
|
|
289
|
+
deleteRequest.onblocked = () => {
|
|
290
|
+
console.warn("[ScoutCache] Database reset blocked - close all other tabs");
|
|
291
|
+
// Continue anyway, it will resolve when unblocked
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
// Method to check database health
|
|
296
|
+
async checkDatabaseHealth() {
|
|
297
|
+
const issues = [];
|
|
298
|
+
try {
|
|
299
|
+
await this.init();
|
|
300
|
+
if (!this.db) {
|
|
301
|
+
issues.push("Database connection not established");
|
|
302
|
+
return { healthy: false, issues };
|
|
303
|
+
}
|
|
304
|
+
if (!this.validateDatabaseSchema()) {
|
|
305
|
+
issues.push("Database schema validation failed");
|
|
306
|
+
}
|
|
307
|
+
// Try a simple read operation
|
|
308
|
+
try {
|
|
309
|
+
await this.getHerdModules();
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
issues.push(`Read operation failed: ${error}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
issues.push(`Database initialization failed: ${error}`);
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
healthy: issues.length === 0,
|
|
320
|
+
issues,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
221
323
|
}
|
|
222
324
|
// Singleton instance
|
|
223
325
|
export const scoutCache = new ScoutCache();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CacheStats, TimingStats } from "../helpers/cache";
|
|
1
|
+
import { CacheStats, TimingStats, DatabaseHealth } from "../helpers/cache";
|
|
2
2
|
export interface UseScoutRefreshOptions {
|
|
3
3
|
autoRefresh?: boolean;
|
|
4
4
|
onRefreshComplete?: () => void;
|
|
@@ -41,4 +41,6 @@ export declare function useScoutRefresh(options?: UseScoutRefreshOptions): {
|
|
|
41
41
|
getTimingStats: () => TimingStats;
|
|
42
42
|
clearCache: () => Promise<void>;
|
|
43
43
|
getCacheStats: () => Promise<CacheStats>;
|
|
44
|
+
checkDatabaseHealth: () => Promise<DatabaseHealth>;
|
|
45
|
+
resetDatabase: () => Promise<void>;
|
|
44
46
|
};
|
|
@@ -4,7 +4,7 @@ import { EnumScoutStateStatus, setActiveHerdId, setHerdModules, setStatus, setHe
|
|
|
4
4
|
import { EnumHerdModulesLoadingState } from "../types/herd_module";
|
|
5
5
|
import { server_load_herd_modules } from "../helpers/herds";
|
|
6
6
|
import { server_get_user } from "../helpers/users";
|
|
7
|
-
import { scoutCache } from "../helpers/cache";
|
|
7
|
+
import { scoutCache, } from "../helpers/cache";
|
|
8
8
|
import { EnumDataSource } from "../types/data_source";
|
|
9
9
|
/**
|
|
10
10
|
* Hook for refreshing scout data with detailed timing measurements and cache-first loading
|
|
@@ -107,7 +107,7 @@ export function useScoutRefresh(options = {}) {
|
|
|
107
107
|
const backgroundStartTime = Date.now();
|
|
108
108
|
const [backgroundHerdModulesResult, backgroundUserResult] = await Promise.all([
|
|
109
109
|
server_load_herd_modules(),
|
|
110
|
-
server_get_user()
|
|
110
|
+
server_get_user(),
|
|
111
111
|
]);
|
|
112
112
|
const backgroundDuration = Date.now() - backgroundStartTime;
|
|
113
113
|
console.log(`[useScoutRefresh] Background fetch completed in ${backgroundDuration}ms`);
|
|
@@ -123,6 +123,21 @@ export function useScoutRefresh(options = {}) {
|
|
|
123
123
|
}
|
|
124
124
|
catch (cacheError) {
|
|
125
125
|
console.warn("[useScoutRefresh] Background cache save failed:", cacheError);
|
|
126
|
+
// If it's an IndexedDB object store error, try to reset the database
|
|
127
|
+
if (cacheError instanceof Error &&
|
|
128
|
+
(cacheError.message.includes("object store") ||
|
|
129
|
+
cacheError.message.includes("NotFoundError"))) {
|
|
130
|
+
console.log("[useScoutRefresh] Attempting database reset due to schema error...");
|
|
131
|
+
try {
|
|
132
|
+
await scoutCache.resetDatabase();
|
|
133
|
+
console.log("[useScoutRefresh] Database reset successful, retrying cache save...");
|
|
134
|
+
await scoutCache.setHerdModules(backgroundHerdModulesResult.data, cacheTtlMs);
|
|
135
|
+
console.log("[useScoutRefresh] Cache save successful after database reset");
|
|
136
|
+
}
|
|
137
|
+
catch (resetError) {
|
|
138
|
+
console.error("[useScoutRefresh] Database reset and retry failed:", resetError);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
126
141
|
}
|
|
127
142
|
// Update store with fresh data
|
|
128
143
|
dispatch(setHerdModules(backgroundHerdModulesResult.data));
|
|
@@ -157,6 +172,19 @@ export function useScoutRefresh(options = {}) {
|
|
|
157
172
|
}
|
|
158
173
|
catch (cacheError) {
|
|
159
174
|
console.warn("[useScoutRefresh] Cache load failed:", cacheError);
|
|
175
|
+
// If it's an IndexedDB object store error, try to reset the database
|
|
176
|
+
if (cacheError instanceof Error &&
|
|
177
|
+
(cacheError.message.includes("object store") ||
|
|
178
|
+
cacheError.message.includes("NotFoundError"))) {
|
|
179
|
+
console.log("[useScoutRefresh] Attempting database reset due to cache load error...");
|
|
180
|
+
try {
|
|
181
|
+
await scoutCache.resetDatabase();
|
|
182
|
+
console.log("[useScoutRefresh] Database reset successful");
|
|
183
|
+
}
|
|
184
|
+
catch (resetError) {
|
|
185
|
+
console.error("[useScoutRefresh] Database reset failed:", resetError);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
160
188
|
// Continue with API call
|
|
161
189
|
}
|
|
162
190
|
}
|
|
@@ -223,6 +251,21 @@ export function useScoutRefresh(options = {}) {
|
|
|
223
251
|
}
|
|
224
252
|
catch (cacheError) {
|
|
225
253
|
console.warn("[useScoutRefresh] Cache save failed:", cacheError);
|
|
254
|
+
// If it's an IndexedDB object store error, try to reset the database
|
|
255
|
+
if (cacheError instanceof Error &&
|
|
256
|
+
(cacheError.message.includes("object store") ||
|
|
257
|
+
cacheError.message.includes("NotFoundError"))) {
|
|
258
|
+
console.log("[useScoutRefresh] Attempting database reset due to cache save error...");
|
|
259
|
+
try {
|
|
260
|
+
await scoutCache.resetDatabase();
|
|
261
|
+
console.log("[useScoutRefresh] Database reset successful, retrying cache save...");
|
|
262
|
+
await scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs);
|
|
263
|
+
console.log("[useScoutRefresh] Cache save successful after database reset");
|
|
264
|
+
}
|
|
265
|
+
catch (resetError) {
|
|
266
|
+
console.error("[useScoutRefresh] Database reset and retry failed:", resetError);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
226
269
|
}
|
|
227
270
|
// Step 4: Update store with fresh data
|
|
228
271
|
const dataProcessingStartTime = Date.now();
|
|
@@ -345,10 +388,36 @@ export function useScoutRefresh(options = {}) {
|
|
|
345
388
|
};
|
|
346
389
|
}
|
|
347
390
|
}, []);
|
|
391
|
+
// Utility function to check database health
|
|
392
|
+
const checkDatabaseHealth = useCallback(async () => {
|
|
393
|
+
try {
|
|
394
|
+
return await scoutCache.checkDatabaseHealth();
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
console.error("[useScoutRefresh] Failed to check database health:", error);
|
|
398
|
+
return {
|
|
399
|
+
healthy: false,
|
|
400
|
+
issues: [`Health check failed: ${error}`],
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
}, []);
|
|
404
|
+
// Utility function to reset database
|
|
405
|
+
const resetDatabase = useCallback(async () => {
|
|
406
|
+
try {
|
|
407
|
+
await scoutCache.resetDatabase();
|
|
408
|
+
console.log("[useScoutRefresh] Database reset successfully");
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
console.error("[useScoutRefresh] Failed to reset database:", error);
|
|
412
|
+
throw error;
|
|
413
|
+
}
|
|
414
|
+
}, []);
|
|
348
415
|
return {
|
|
349
416
|
handleRefresh,
|
|
350
417
|
getTimingStats,
|
|
351
418
|
clearCache,
|
|
352
419
|
getCacheStats,
|
|
420
|
+
checkDatabaseHealth,
|
|
421
|
+
resetDatabase,
|
|
353
422
|
};
|
|
354
423
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export * from "./helpers/web";
|
|
|
30
30
|
export * from "./helpers/zones";
|
|
31
31
|
export * from "./helpers/storage";
|
|
32
32
|
export * from "./helpers/eventUtils";
|
|
33
|
+
export * from "./helpers/cache";
|
|
33
34
|
export * from "./hooks/useScoutDbListener";
|
|
34
35
|
export * from "./hooks/useScoutRefresh";
|
|
35
36
|
export * from "./providers";
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ export * from "./helpers/web";
|
|
|
33
33
|
export * from "./helpers/zones";
|
|
34
34
|
export * from "./helpers/storage";
|
|
35
35
|
export * from "./helpers/eventUtils";
|
|
36
|
+
export * from "./helpers/cache";
|
|
36
37
|
// Hooks
|
|
37
38
|
export * from "./hooks/useScoutDbListener";
|
|
38
39
|
export * from "./hooks/useScoutRefresh";
|