@adventurelabs/scout-core 1.0.77 → 1.0.79
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-usage-example.d.ts +17 -0
- package/dist/helpers/cache-usage-example.js +82 -0
- package/dist/helpers/cache.d.ts +52 -0
- package/dist/helpers/cache.js +223 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/hooks/data-source-usage-example.d.ts +9 -0
- package/dist/hooks/data-source-usage-example.js +68 -0
- package/dist/hooks/useDataSource.d.ts +43 -0
- package/dist/hooks/useDataSource.js +63 -0
- package/dist/hooks/useScoutRefresh.d.ts +15 -9
- package/dist/hooks/useScoutRefresh.js +134 -47
- package/dist/store/scout.d.ts +15 -1
- package/dist/store/scout.js +11 -1
- package/dist/types/data_source.d.ts +17 -0
- package/dist/types/data_source.js +9 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example usage of the ScoutCache system
|
|
3
|
+
* This demonstrates how to use the cache-first loading pattern
|
|
4
|
+
*/
|
|
5
|
+
import { CacheStats, TimingStats } from "./cache";
|
|
6
|
+
export declare function ExampleBasicUsage(): {
|
|
7
|
+
handleRefresh: () => Promise<void>;
|
|
8
|
+
clearCache: () => Promise<void>;
|
|
9
|
+
stats: () => TimingStats;
|
|
10
|
+
cacheStats: () => Promise<CacheStats>;
|
|
11
|
+
};
|
|
12
|
+
export declare function ExampleAdvancedCacheManagement(): Promise<void>;
|
|
13
|
+
export declare function ExampleBackgroundPreloading(): Promise<void>;
|
|
14
|
+
export declare function ExamplePerformanceMonitoring(): {
|
|
15
|
+
getTimingStats: () => TimingStats;
|
|
16
|
+
getCacheStats: () => Promise<CacheStats>;
|
|
17
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example usage of the ScoutCache system
|
|
3
|
+
* This demonstrates how to use the cache-first loading pattern
|
|
4
|
+
*/
|
|
5
|
+
import { useScoutRefresh } from "../hooks/useScoutRefresh";
|
|
6
|
+
import { scoutCache } from "./cache";
|
|
7
|
+
// Example 1: Basic usage with cache-first loading
|
|
8
|
+
export function ExampleBasicUsage() {
|
|
9
|
+
const { handleRefresh, getTimingStats, clearCache, getCacheStats } = useScoutRefresh({
|
|
10
|
+
autoRefresh: true,
|
|
11
|
+
cacheFirst: true,
|
|
12
|
+
cacheTtlMs: 10 * 60 * 1000, // 10 minutes
|
|
13
|
+
onRefreshComplete: () => {
|
|
14
|
+
console.log("Refresh completed!");
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
// Get timing stats
|
|
18
|
+
const stats = getTimingStats();
|
|
19
|
+
console.log("Performance stats:", stats);
|
|
20
|
+
// Get cache stats
|
|
21
|
+
const cacheStats = getCacheStats();
|
|
22
|
+
console.log("Cache stats:", cacheStats);
|
|
23
|
+
return {
|
|
24
|
+
handleRefresh,
|
|
25
|
+
clearCache,
|
|
26
|
+
stats: getTimingStats,
|
|
27
|
+
cacheStats: getCacheStats,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// Example 2: Advanced cache management
|
|
31
|
+
export async function ExampleAdvancedCacheManagement() {
|
|
32
|
+
// Check if cache is valid
|
|
33
|
+
const isValid = await scoutCache.isCacheValid(5 * 60 * 1000); // 5 minutes
|
|
34
|
+
console.log("Cache is valid:", isValid);
|
|
35
|
+
// Check if we should refresh
|
|
36
|
+
const shouldRefresh = await scoutCache.shouldRefresh(2 * 60 * 1000, // max age 2 minutes
|
|
37
|
+
false // not forcing refresh
|
|
38
|
+
);
|
|
39
|
+
console.log("Should refresh:", shouldRefresh);
|
|
40
|
+
// Get cache age
|
|
41
|
+
const age = await scoutCache.getCacheAge();
|
|
42
|
+
console.log("Cache age:", Math.round(age / 1000), "seconds");
|
|
43
|
+
// Invalidate cache
|
|
44
|
+
await scoutCache.invalidateHerdModules();
|
|
45
|
+
console.log("Cache invalidated");
|
|
46
|
+
// Clear all cache data
|
|
47
|
+
await scoutCache.clearHerdModules();
|
|
48
|
+
console.log("Cache cleared");
|
|
49
|
+
}
|
|
50
|
+
// Example 3: Background preloading
|
|
51
|
+
export async function ExampleBackgroundPreloading() {
|
|
52
|
+
// Simulate a function that loads herd modules
|
|
53
|
+
const loadHerdModules = async () => {
|
|
54
|
+
// This would be your actual API call
|
|
55
|
+
const response = await fetch("/api/herd-modules");
|
|
56
|
+
return response.json();
|
|
57
|
+
};
|
|
58
|
+
// Preload cache in background
|
|
59
|
+
await scoutCache.preloadCache(loadHerdModules, 15 * 60 * 1000); // 15 minutes TTL
|
|
60
|
+
console.log("Background preload completed");
|
|
61
|
+
}
|
|
62
|
+
// Example 4: Performance monitoring
|
|
63
|
+
export function ExamplePerformanceMonitoring() {
|
|
64
|
+
const { getTimingStats, getCacheStats } = useScoutRefresh({
|
|
65
|
+
cacheFirst: true,
|
|
66
|
+
onRefreshComplete: async () => {
|
|
67
|
+
const stats = getTimingStats();
|
|
68
|
+
const cacheStats = await getCacheStats();
|
|
69
|
+
console.log("=== Performance Report ===");
|
|
70
|
+
console.log("Total duration:", stats.totalDuration, "ms");
|
|
71
|
+
console.log("Cache load:", stats.cacheLoad, "ms");
|
|
72
|
+
console.log("API calls:", stats.herdModulesApi + stats.userApi, "ms");
|
|
73
|
+
console.log("Cache save:", stats.cacheSave, "ms");
|
|
74
|
+
console.log("Data processing:", stats.dataProcessing, "ms");
|
|
75
|
+
console.log("LocalStorage:", stats.localStorage, "ms");
|
|
76
|
+
console.log("Cache hit rate:", (cacheStats.hitRate * 100).toFixed(1) + "%");
|
|
77
|
+
console.log("Cache size:", cacheStats.size, "herd modules");
|
|
78
|
+
console.log("Cache age:", Math.round((Date.now() - cacheStats.lastUpdated) / 1000), "seconds");
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return { getTimingStats, getCacheStats };
|
|
82
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { IHerdModule } from "../types/herd_module";
|
|
2
|
+
export interface CacheMetadata {
|
|
3
|
+
key: string;
|
|
4
|
+
timestamp: number;
|
|
5
|
+
ttl: number;
|
|
6
|
+
version: string;
|
|
7
|
+
etag?: string;
|
|
8
|
+
lastModified?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface CacheResult<T> {
|
|
11
|
+
data: T | null;
|
|
12
|
+
isStale: boolean;
|
|
13
|
+
age: number;
|
|
14
|
+
metadata: CacheMetadata | null;
|
|
15
|
+
}
|
|
16
|
+
export interface CacheStats {
|
|
17
|
+
size: number;
|
|
18
|
+
lastUpdated: number;
|
|
19
|
+
isStale: boolean;
|
|
20
|
+
hitRate: number;
|
|
21
|
+
totalHits: number;
|
|
22
|
+
totalMisses: number;
|
|
23
|
+
}
|
|
24
|
+
export interface TimingStats {
|
|
25
|
+
totalDuration: number;
|
|
26
|
+
cacheLoad: number;
|
|
27
|
+
herdModulesApi: number;
|
|
28
|
+
userApi: number;
|
|
29
|
+
cacheSave: number;
|
|
30
|
+
dataProcessing: number;
|
|
31
|
+
localStorage: number;
|
|
32
|
+
}
|
|
33
|
+
export declare class ScoutCache {
|
|
34
|
+
private db;
|
|
35
|
+
private initPromise;
|
|
36
|
+
private stats;
|
|
37
|
+
private init;
|
|
38
|
+
setHerdModules(herdModules: IHerdModule[], ttlMs?: number, etag?: string): Promise<void>;
|
|
39
|
+
getHerdModules(): Promise<CacheResult<IHerdModule[]>>;
|
|
40
|
+
clearHerdModules(): Promise<void>;
|
|
41
|
+
invalidateHerdModules(): Promise<void>;
|
|
42
|
+
getCacheStats(): Promise<CacheStats>;
|
|
43
|
+
isCacheValid(ttlMs?: number): Promise<boolean>;
|
|
44
|
+
getCacheAge(): Promise<number>;
|
|
45
|
+
shouldRefresh(maxAgeMs?: number, forceRefresh?: boolean): Promise<{
|
|
46
|
+
shouldRefresh: boolean;
|
|
47
|
+
reason: string;
|
|
48
|
+
}>;
|
|
49
|
+
preloadCache(loadFunction: () => Promise<IHerdModule[]>, ttlMs?: number): Promise<void>;
|
|
50
|
+
getDefaultTtl(): number;
|
|
51
|
+
}
|
|
52
|
+
export declare const scoutCache: ScoutCache;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
const DB_NAME = "ScoutCache";
|
|
2
|
+
const DB_VERSION = 1;
|
|
3
|
+
const HERD_MODULES_STORE = "herd_modules";
|
|
4
|
+
const CACHE_METADATA_STORE = "cache_metadata";
|
|
5
|
+
// Default TTL: 24 hours (1 day)
|
|
6
|
+
const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
7
|
+
export class ScoutCache {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.db = null;
|
|
10
|
+
this.initPromise = null;
|
|
11
|
+
this.stats = {
|
|
12
|
+
hits: 0,
|
|
13
|
+
misses: 0,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
async init() {
|
|
17
|
+
if (this.db)
|
|
18
|
+
return;
|
|
19
|
+
if (this.initPromise)
|
|
20
|
+
return this.initPromise;
|
|
21
|
+
this.initPromise = new Promise((resolve, reject) => {
|
|
22
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
23
|
+
request.onerror = () => {
|
|
24
|
+
console.error("[ScoutCache] Failed to open IndexedDB:", request.error);
|
|
25
|
+
reject(request.error);
|
|
26
|
+
};
|
|
27
|
+
request.onsuccess = () => {
|
|
28
|
+
this.db = request.result;
|
|
29
|
+
console.log("[ScoutCache] IndexedDB initialized successfully");
|
|
30
|
+
resolve();
|
|
31
|
+
};
|
|
32
|
+
request.onupgradeneeded = (event) => {
|
|
33
|
+
const db = event.target.result;
|
|
34
|
+
// Create herd modules store
|
|
35
|
+
if (!db.objectStoreNames.contains(HERD_MODULES_STORE)) {
|
|
36
|
+
const herdModulesStore = db.createObjectStore(HERD_MODULES_STORE, {
|
|
37
|
+
keyPath: "herdId",
|
|
38
|
+
});
|
|
39
|
+
herdModulesStore.createIndex("timestamp", "timestamp", {
|
|
40
|
+
unique: false,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// Create cache metadata store
|
|
44
|
+
if (!db.objectStoreNames.contains(CACHE_METADATA_STORE)) {
|
|
45
|
+
const metadataStore = db.createObjectStore(CACHE_METADATA_STORE, {
|
|
46
|
+
keyPath: "key",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
console.log("[ScoutCache] Database schema upgraded");
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
return this.initPromise;
|
|
53
|
+
}
|
|
54
|
+
async setHerdModules(herdModules, ttlMs = DEFAULT_TTL_MS, etag) {
|
|
55
|
+
await this.init();
|
|
56
|
+
if (!this.db)
|
|
57
|
+
throw new Error("Database not initialized");
|
|
58
|
+
const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readwrite");
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
transaction.onerror = () => reject(transaction.error);
|
|
61
|
+
transaction.oncomplete = () => resolve();
|
|
62
|
+
const herdModulesStore = transaction.objectStore(HERD_MODULES_STORE);
|
|
63
|
+
const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
|
|
64
|
+
const timestamp = Date.now();
|
|
65
|
+
const version = "1.0.0";
|
|
66
|
+
// Store each herd module
|
|
67
|
+
herdModules.forEach((herdModule) => {
|
|
68
|
+
const cacheEntry = {
|
|
69
|
+
herdId: herdModule.herd.id.toString(),
|
|
70
|
+
data: herdModule,
|
|
71
|
+
timestamp,
|
|
72
|
+
};
|
|
73
|
+
herdModulesStore.put(cacheEntry);
|
|
74
|
+
});
|
|
75
|
+
// Store cache metadata
|
|
76
|
+
const metadata = {
|
|
77
|
+
key: "herd_modules",
|
|
78
|
+
timestamp,
|
|
79
|
+
ttl: ttlMs,
|
|
80
|
+
version,
|
|
81
|
+
etag,
|
|
82
|
+
lastModified: timestamp,
|
|
83
|
+
};
|
|
84
|
+
metadataStore.put(metadata);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async getHerdModules() {
|
|
88
|
+
await this.init();
|
|
89
|
+
if (!this.db)
|
|
90
|
+
throw new Error("Database not initialized");
|
|
91
|
+
const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readonly");
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
transaction.onerror = () => reject(transaction.error);
|
|
94
|
+
const herdModulesStore = transaction.objectStore(HERD_MODULES_STORE);
|
|
95
|
+
const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
|
|
96
|
+
// Get metadata first
|
|
97
|
+
const metadataRequest = metadataStore.get("herd_modules");
|
|
98
|
+
metadataRequest.onsuccess = () => {
|
|
99
|
+
const metadata = metadataRequest.result;
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
if (!metadata) {
|
|
102
|
+
this.stats.misses++;
|
|
103
|
+
resolve({ data: null, isStale: true, age: 0, metadata: null });
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const age = now - metadata.timestamp;
|
|
107
|
+
const isStale = age > metadata.ttl;
|
|
108
|
+
// Get all herd modules
|
|
109
|
+
const getAllRequest = herdModulesStore.getAll();
|
|
110
|
+
getAllRequest.onsuccess = () => {
|
|
111
|
+
const cacheEntries = getAllRequest.result;
|
|
112
|
+
const herdModules = cacheEntries
|
|
113
|
+
.filter((entry) => entry.data && entry.data.herd && entry.data.herd.slug)
|
|
114
|
+
.map((entry) => entry.data)
|
|
115
|
+
.sort((a, b) => (a.herd?.slug || "").localeCompare(b.herd?.slug || ""));
|
|
116
|
+
// Update stats
|
|
117
|
+
if (herdModules.length > 0) {
|
|
118
|
+
this.stats.hits++;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
this.stats.misses++;
|
|
122
|
+
}
|
|
123
|
+
resolve({
|
|
124
|
+
data: herdModules,
|
|
125
|
+
isStale,
|
|
126
|
+
age,
|
|
127
|
+
metadata,
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
async clearHerdModules() {
|
|
134
|
+
await this.init();
|
|
135
|
+
if (!this.db)
|
|
136
|
+
throw new Error("Database not initialized");
|
|
137
|
+
const transaction = this.db.transaction([HERD_MODULES_STORE, CACHE_METADATA_STORE], "readwrite");
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
transaction.onerror = () => reject(transaction.error);
|
|
140
|
+
transaction.oncomplete = () => resolve();
|
|
141
|
+
const herdModulesStore = transaction.objectStore(HERD_MODULES_STORE);
|
|
142
|
+
const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
|
|
143
|
+
herdModulesStore.clear();
|
|
144
|
+
metadataStore.delete("herd_modules");
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async invalidateHerdModules() {
|
|
148
|
+
await this.init();
|
|
149
|
+
if (!this.db)
|
|
150
|
+
throw new Error("Database not initialized");
|
|
151
|
+
const transaction = this.db.transaction([CACHE_METADATA_STORE], "readwrite");
|
|
152
|
+
return new Promise((resolve, reject) => {
|
|
153
|
+
transaction.onerror = () => reject(transaction.error);
|
|
154
|
+
transaction.oncomplete = () => resolve();
|
|
155
|
+
const metadataStore = transaction.objectStore(CACHE_METADATA_STORE);
|
|
156
|
+
metadataStore.delete("herd_modules");
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async getCacheStats() {
|
|
160
|
+
const result = await this.getHerdModules();
|
|
161
|
+
const totalRequests = this.stats.hits + this.stats.misses;
|
|
162
|
+
const hitRate = totalRequests > 0 ? this.stats.hits / totalRequests : 0;
|
|
163
|
+
return {
|
|
164
|
+
size: result.data?.length || 0,
|
|
165
|
+
lastUpdated: result.data ? Date.now() - result.age : 0,
|
|
166
|
+
isStale: result.isStale,
|
|
167
|
+
hitRate: Math.round(hitRate * 100) / 100,
|
|
168
|
+
totalHits: this.stats.hits,
|
|
169
|
+
totalMisses: this.stats.misses,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
async isCacheValid(ttlMs) {
|
|
173
|
+
const result = await this.getHerdModules();
|
|
174
|
+
if (!result.data || !result.metadata)
|
|
175
|
+
return false;
|
|
176
|
+
const effectiveTtl = ttlMs || result.metadata.ttl;
|
|
177
|
+
return !result.isStale && result.age < effectiveTtl;
|
|
178
|
+
}
|
|
179
|
+
async getCacheAge() {
|
|
180
|
+
const result = await this.getHerdModules();
|
|
181
|
+
return result.age;
|
|
182
|
+
}
|
|
183
|
+
// Method to check if we should refresh based on various conditions
|
|
184
|
+
async shouldRefresh(maxAgeMs, forceRefresh) {
|
|
185
|
+
if (forceRefresh) {
|
|
186
|
+
return { shouldRefresh: true, reason: "Force refresh requested" };
|
|
187
|
+
}
|
|
188
|
+
const result = await this.getHerdModules();
|
|
189
|
+
if (!result.data || result.data.length === 0) {
|
|
190
|
+
return { shouldRefresh: true, reason: "No cached data" };
|
|
191
|
+
}
|
|
192
|
+
if (result.isStale) {
|
|
193
|
+
return { shouldRefresh: true, reason: "Cache is stale" };
|
|
194
|
+
}
|
|
195
|
+
if (maxAgeMs && result.age > maxAgeMs) {
|
|
196
|
+
return {
|
|
197
|
+
shouldRefresh: true,
|
|
198
|
+
reason: `Cache age (${Math.round(result.age / 1000)}s) exceeds max age (${Math.round(maxAgeMs / 1000)}s)`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return { shouldRefresh: false, reason: "Cache is valid and fresh" };
|
|
202
|
+
}
|
|
203
|
+
// Method to preload cache with background refresh
|
|
204
|
+
async preloadCache(loadFunction, ttlMs = DEFAULT_TTL_MS) {
|
|
205
|
+
try {
|
|
206
|
+
console.log("[ScoutCache] Starting background cache preload...");
|
|
207
|
+
const startTime = Date.now();
|
|
208
|
+
const herdModules = await loadFunction();
|
|
209
|
+
await this.setHerdModules(herdModules, ttlMs);
|
|
210
|
+
const duration = Date.now() - startTime;
|
|
211
|
+
console.log(`[ScoutCache] Background preload completed in ${duration}ms`);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
console.warn("[ScoutCache] Background preload failed:", error);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Get the default TTL value
|
|
218
|
+
getDefaultTtl() {
|
|
219
|
+
return DEFAULT_TTL_MS;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Singleton instance
|
|
223
|
+
export const scoutCache = new ScoutCache();
|
package/dist/helpers/index.d.ts
CHANGED
package/dist/helpers/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example components demonstrating how to use data source tracking
|
|
3
|
+
*/
|
|
4
|
+
export declare function DataSourceIndicator(): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function ConditionalContent(): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare function PerformanceMetrics(): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function SmartRefreshButton(): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function HeaderWithDataSource(): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function DataSourceDebugger(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useDataSource, useDataSourceDescription } from './useDataSource';
|
|
3
|
+
import { useScoutRefresh } from './useScoutRefresh';
|
|
4
|
+
// Example 1: Basic data source display
|
|
5
|
+
export function DataSourceIndicator() {
|
|
6
|
+
const { dataSource, isFromCache, cacheAge, isStale } = useDataSource();
|
|
7
|
+
const description = useDataSourceDescription();
|
|
8
|
+
return (_jsxs("div", { className: "data-source-indicator", children: [_jsx("div", { className: `source-badge ${dataSource.toLowerCase()}`, children: dataSource }), _jsxs("div", { className: "source-details", children: [_jsx("p", { children: description }), isFromCache && (_jsxs("div", { className: "cache-info", children: [_jsxs("span", { children: ["Cache age: ", Math.round(cacheAge / 1000), "s"] }), _jsx("span", { className: isStale ? 'stale' : 'fresh', children: isStale ? 'Stale' : 'Fresh' })] }))] })] }));
|
|
9
|
+
}
|
|
10
|
+
// Example 2: Conditional rendering based on data source
|
|
11
|
+
export function ConditionalContent() {
|
|
12
|
+
const { isFromCache, isFromDatabase, isStale } = useDataSource();
|
|
13
|
+
return (_jsxs("div", { children: [isFromCache && (_jsx("div", { className: "cache-notice", children: isStale ? (_jsx("p", { children: "\u26A0\uFE0F Showing cached data (may be outdated)" })) : (_jsx("p", { children: "\u2705 Showing fresh cached data" })) })), isFromDatabase && (_jsx("div", { className: "database-notice", children: _jsx("p", { children: "\uD83D\uDD04 Showing live data from database" }) }))] }));
|
|
14
|
+
}
|
|
15
|
+
// Example 3: Performance metrics with data source
|
|
16
|
+
export function PerformanceMetrics() {
|
|
17
|
+
const { dataSource, cacheAge } = useDataSource();
|
|
18
|
+
const { getTimingStats, getCacheStats } = useScoutRefresh();
|
|
19
|
+
const handleShowMetrics = () => {
|
|
20
|
+
const timing = getTimingStats();
|
|
21
|
+
console.log('Performance Metrics:', {
|
|
22
|
+
dataSource,
|
|
23
|
+
cacheAge: cacheAge ? Math.round(cacheAge / 1000) : 'N/A',
|
|
24
|
+
totalDuration: timing.totalDuration,
|
|
25
|
+
cacheLoad: timing.cacheLoad,
|
|
26
|
+
apiCalls: timing.herdModulesApi + timing.userApi,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
return (_jsxs("div", { className: "performance-metrics", children: [_jsx("h3", { children: "Performance Metrics" }), _jsxs("p", { children: ["Data Source: ", dataSource] }), cacheAge && _jsxs("p", { children: ["Cache Age: ", Math.round(cacheAge / 1000), "s"] }), _jsx("button", { onClick: handleShowMetrics, children: "Show Detailed Metrics" })] }));
|
|
30
|
+
}
|
|
31
|
+
// Example 4: Data source aware refresh button
|
|
32
|
+
export function SmartRefreshButton() {
|
|
33
|
+
const { isFromCache, isStale, cacheAge } = useDataSource();
|
|
34
|
+
const { handleRefresh } = useScoutRefresh();
|
|
35
|
+
const getButtonText = () => {
|
|
36
|
+
if (isFromCache) {
|
|
37
|
+
if (isStale) {
|
|
38
|
+
return 'Refresh Stale Data';
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const ageSeconds = Math.round(cacheAge / 1000);
|
|
42
|
+
return `Refresh (${ageSeconds}s old)`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return 'Refresh Data';
|
|
46
|
+
};
|
|
47
|
+
const getButtonStyle = () => {
|
|
48
|
+
if (isFromCache && isStale) {
|
|
49
|
+
return 'refresh-button stale';
|
|
50
|
+
}
|
|
51
|
+
else if (isFromCache) {
|
|
52
|
+
return 'refresh-button cached';
|
|
53
|
+
}
|
|
54
|
+
return 'refresh-button fresh';
|
|
55
|
+
};
|
|
56
|
+
return (_jsx("button", { className: getButtonStyle(), onClick: handleRefresh, children: getButtonText() }));
|
|
57
|
+
}
|
|
58
|
+
// Example 5: Data source status in header
|
|
59
|
+
export function HeaderWithDataSource() {
|
|
60
|
+
const description = useDataSourceDescription();
|
|
61
|
+
const { handleRefresh } = useScoutRefresh();
|
|
62
|
+
return (_jsxs("header", { className: "app-header", children: [_jsx("h1", { children: "Scout Dashboard" }), _jsxs("div", { className: "header-controls", children: [_jsx("span", { className: "data-source-status", children: description }), _jsx("button", { onClick: handleRefresh, children: "Refresh" })] })] }));
|
|
63
|
+
}
|
|
64
|
+
// Example 6: Data source debugging component
|
|
65
|
+
export function DataSourceDebugger() {
|
|
66
|
+
const { dataSource, dataSourceInfo, isFromCache, isFromDatabase, isUnknown } = useDataSource();
|
|
67
|
+
return (_jsxs("div", { className: "data-source-debugger", children: [_jsx("h3", { children: "Data Source Debug Info" }), _jsxs("div", { className: "debug-info", children: [_jsxs("p", { children: [_jsx("strong", { children: "Source:" }), " ", dataSource] }), _jsxs("p", { children: [_jsx("strong", { children: "From Cache:" }), " ", isFromCache ? 'Yes' : 'No'] }), _jsxs("p", { children: [_jsx("strong", { children: "From Database:" }), " ", isFromDatabase ? 'Yes' : 'No'] }), _jsxs("p", { children: [_jsx("strong", { children: "Unknown:" }), " ", isUnknown ? 'Yes' : 'No'] }), dataSourceInfo && (_jsxs("div", { className: "detailed-info", children: [_jsxs("p", { children: [_jsx("strong", { children: "Timestamp:" }), " ", new Date(dataSourceInfo.timestamp).toISOString()] }), dataSourceInfo.cacheAge && (_jsxs("p", { children: [_jsx("strong", { children: "Cache Age:" }), " ", Math.round(dataSourceInfo.cacheAge / 1000), "s"] })), typeof dataSourceInfo.isStale === 'boolean' && (_jsxs("p", { children: [_jsx("strong", { children: "Is Stale:" }), " ", dataSourceInfo.isStale ? 'Yes' : 'No'] }))] }))] })] }));
|
|
68
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EnumDataSource, IDataSourceInfo } from "../types/data_source";
|
|
2
|
+
/**
|
|
3
|
+
* Hook to access data source information from the Redux store
|
|
4
|
+
*
|
|
5
|
+
* @returns Object containing:
|
|
6
|
+
* - dataSource: The current data source (CACHE, DATABASE, or UNKNOWN)
|
|
7
|
+
* - dataSourceInfo: Detailed information about the data source
|
|
8
|
+
* - isFromCache: Boolean indicating if data is from cache
|
|
9
|
+
* - isFromDatabase: Boolean indicating if data is from database
|
|
10
|
+
* - cacheAge: Age of cached data in milliseconds (if from cache)
|
|
11
|
+
* - isStale: Whether cached data is stale (if from cache)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* function MyComponent() {
|
|
16
|
+
* const { dataSource, isFromCache, cacheAge, isStale } = useDataSource();
|
|
17
|
+
*
|
|
18
|
+
* return (
|
|
19
|
+
* <div>
|
|
20
|
+
* <p>Data source: {dataSource}</p>
|
|
21
|
+
* {isFromCache && (
|
|
22
|
+
* <p>Cache age: {Math.round(cacheAge! / 1000)}s, Stale: {isStale ? 'Yes' : 'No'}</p>
|
|
23
|
+
* )}
|
|
24
|
+
* </div>
|
|
25
|
+
* );
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function useDataSource(): {
|
|
30
|
+
dataSource: EnumDataSource;
|
|
31
|
+
dataSourceInfo: IDataSourceInfo | null;
|
|
32
|
+
isFromCache: boolean;
|
|
33
|
+
isFromDatabase: boolean;
|
|
34
|
+
isUnknown: boolean;
|
|
35
|
+
cacheAge: number | undefined;
|
|
36
|
+
isStale: boolean | undefined;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Hook to get a human-readable description of the data source
|
|
40
|
+
*
|
|
41
|
+
* @returns String description of the current data source
|
|
42
|
+
*/
|
|
43
|
+
export declare function useDataSourceDescription(): string;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useSelector } from "react-redux";
|
|
2
|
+
import { EnumDataSource } from "../types/data_source";
|
|
3
|
+
/**
|
|
4
|
+
* Hook to access data source information from the Redux store
|
|
5
|
+
*
|
|
6
|
+
* @returns Object containing:
|
|
7
|
+
* - dataSource: The current data source (CACHE, DATABASE, or UNKNOWN)
|
|
8
|
+
* - dataSourceInfo: Detailed information about the data source
|
|
9
|
+
* - isFromCache: Boolean indicating if data is from cache
|
|
10
|
+
* - isFromDatabase: Boolean indicating if data is from database
|
|
11
|
+
* - cacheAge: Age of cached data in milliseconds (if from cache)
|
|
12
|
+
* - isStale: Whether cached data is stale (if from cache)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* function MyComponent() {
|
|
17
|
+
* const { dataSource, isFromCache, cacheAge, isStale } = useDataSource();
|
|
18
|
+
*
|
|
19
|
+
* return (
|
|
20
|
+
* <div>
|
|
21
|
+
* <p>Data source: {dataSource}</p>
|
|
22
|
+
* {isFromCache && (
|
|
23
|
+
* <p>Cache age: {Math.round(cacheAge! / 1000)}s, Stale: {isStale ? 'Yes' : 'No'}</p>
|
|
24
|
+
* )}
|
|
25
|
+
* </div>
|
|
26
|
+
* );
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function useDataSource() {
|
|
31
|
+
const dataSource = useSelector((state) => state.scout.data_source);
|
|
32
|
+
const dataSourceInfo = useSelector((state) => state.scout.data_source_info);
|
|
33
|
+
const isFromCache = dataSource === EnumDataSource.CACHE;
|
|
34
|
+
const isFromDatabase = dataSource === EnumDataSource.DATABASE;
|
|
35
|
+
const isUnknown = dataSource === EnumDataSource.UNKNOWN;
|
|
36
|
+
return {
|
|
37
|
+
dataSource,
|
|
38
|
+
dataSourceInfo,
|
|
39
|
+
isFromCache,
|
|
40
|
+
isFromDatabase,
|
|
41
|
+
isUnknown,
|
|
42
|
+
cacheAge: dataSourceInfo?.cacheAge,
|
|
43
|
+
isStale: dataSourceInfo?.isStale,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Hook to get a human-readable description of the data source
|
|
48
|
+
*
|
|
49
|
+
* @returns String description of the current data source
|
|
50
|
+
*/
|
|
51
|
+
export function useDataSourceDescription() {
|
|
52
|
+
const { dataSource, cacheAge, isStale } = useDataSource();
|
|
53
|
+
switch (dataSource) {
|
|
54
|
+
case EnumDataSource.CACHE:
|
|
55
|
+
const ageSeconds = cacheAge ? Math.round(cacheAge / 1000) : 0;
|
|
56
|
+
return `Loaded from cache (${ageSeconds}s old${isStale ? ', stale' : ', fresh'})`;
|
|
57
|
+
case EnumDataSource.DATABASE:
|
|
58
|
+
return 'Loaded from database (fresh data)';
|
|
59
|
+
case EnumDataSource.UNKNOWN:
|
|
60
|
+
default:
|
|
61
|
+
return 'Data source unknown';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,21 +1,31 @@
|
|
|
1
|
+
import { CacheStats, TimingStats } from "../helpers/cache";
|
|
1
2
|
export interface UseScoutRefreshOptions {
|
|
2
3
|
autoRefresh?: boolean;
|
|
3
4
|
onRefreshComplete?: () => void;
|
|
5
|
+
cacheFirst?: boolean;
|
|
6
|
+
cacheTtlMs?: number;
|
|
4
7
|
}
|
|
5
8
|
/**
|
|
6
|
-
* Hook for refreshing scout data with detailed timing measurements
|
|
9
|
+
* Hook for refreshing scout data with detailed timing measurements and cache-first loading
|
|
7
10
|
*
|
|
8
11
|
* @param options - Configuration options for the refresh behavior
|
|
9
12
|
* @param options.autoRefresh - Whether to automatically refresh on mount (default: true)
|
|
10
13
|
* @param options.onRefreshComplete - Callback function called when refresh completes
|
|
14
|
+
* @param options.cacheFirst - Whether to load from cache first, then refresh (default: true)
|
|
15
|
+
* @param options.cacheTtlMs - Cache time-to-live in milliseconds (default: 24 hours)
|
|
11
16
|
*
|
|
12
17
|
* @returns Object containing:
|
|
13
18
|
* - handleRefresh: Function to manually trigger a refresh
|
|
14
19
|
* - getTimingStats: Function to get detailed timing statistics for the last refresh
|
|
20
|
+
* - clearCache: Function to clear the cache
|
|
21
|
+
* - getCacheStats: Function to get cache statistics
|
|
15
22
|
*
|
|
16
23
|
* @example
|
|
17
24
|
* ```tsx
|
|
18
|
-
* const { handleRefresh, getTimingStats } = useScoutRefresh(
|
|
25
|
+
* const { handleRefresh, getTimingStats, clearCache, getCacheStats } = useScoutRefresh({
|
|
26
|
+
* cacheFirst: true,
|
|
27
|
+
* cacheTtlMs: 10 * 60 * 1000 // 10 minutes
|
|
28
|
+
* });
|
|
19
29
|
*
|
|
20
30
|
* // Get timing stats after a refresh
|
|
21
31
|
* const stats = getTimingStats();
|
|
@@ -28,11 +38,7 @@ export interface UseScoutRefreshOptions {
|
|
|
28
38
|
*/
|
|
29
39
|
export declare function useScoutRefresh(options?: UseScoutRefreshOptions): {
|
|
30
40
|
handleRefresh: () => Promise<void>;
|
|
31
|
-
getTimingStats: () =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
userApi: number;
|
|
35
|
-
dataProcessing: number;
|
|
36
|
-
localStorage: number;
|
|
37
|
-
};
|
|
41
|
+
getTimingStats: () => TimingStats;
|
|
42
|
+
clearCache: () => Promise<void>;
|
|
43
|
+
getCacheStats: () => Promise<CacheStats>;
|
|
38
44
|
};
|
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
import { useEffect, useCallback, useRef } from "react";
|
|
2
2
|
import { useAppDispatch } from "../store/hooks";
|
|
3
|
-
import { EnumScoutStateStatus, setActiveHerdId, setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiDuration, setUserApiDuration, setDataProcessingDuration, setLocalStorageDuration, setUser, } from "../store/scout";
|
|
3
|
+
import { EnumScoutStateStatus, setActiveHerdId, setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiDuration, setUserApiDuration, setDataProcessingDuration, setLocalStorageDuration, setUser, setDataSource, setDataSourceInfo, } from "../store/scout";
|
|
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 {
|
|
7
|
+
import { scoutCache } from "../helpers/cache";
|
|
8
|
+
import { EnumDataSource } from "../types/data_source";
|
|
8
9
|
/**
|
|
9
|
-
* Hook for refreshing scout data with detailed timing measurements
|
|
10
|
+
* Hook for refreshing scout data with detailed timing measurements and cache-first loading
|
|
10
11
|
*
|
|
11
12
|
* @param options - Configuration options for the refresh behavior
|
|
12
13
|
* @param options.autoRefresh - Whether to automatically refresh on mount (default: true)
|
|
13
14
|
* @param options.onRefreshComplete - Callback function called when refresh completes
|
|
15
|
+
* @param options.cacheFirst - Whether to load from cache first, then refresh (default: true)
|
|
16
|
+
* @param options.cacheTtlMs - Cache time-to-live in milliseconds (default: 24 hours)
|
|
14
17
|
*
|
|
15
18
|
* @returns Object containing:
|
|
16
19
|
* - handleRefresh: Function to manually trigger a refresh
|
|
17
20
|
* - getTimingStats: Function to get detailed timing statistics for the last refresh
|
|
21
|
+
* - clearCache: Function to clear the cache
|
|
22
|
+
* - getCacheStats: Function to get cache statistics
|
|
18
23
|
*
|
|
19
24
|
* @example
|
|
20
25
|
* ```tsx
|
|
21
|
-
* const { handleRefresh, getTimingStats } = useScoutRefresh(
|
|
26
|
+
* const { handleRefresh, getTimingStats, clearCache, getCacheStats } = useScoutRefresh({
|
|
27
|
+
* cacheFirst: true,
|
|
28
|
+
* cacheTtlMs: 10 * 60 * 1000 // 10 minutes
|
|
29
|
+
* });
|
|
22
30
|
*
|
|
23
31
|
* // Get timing stats after a refresh
|
|
24
32
|
* const stats = getTimingStats();
|
|
@@ -30,7 +38,8 @@ import { EnumWebResponse } from "../types/requests";
|
|
|
30
38
|
* ```
|
|
31
39
|
*/
|
|
32
40
|
export function useScoutRefresh(options = {}) {
|
|
33
|
-
const { autoRefresh = true, onRefreshComplete
|
|
41
|
+
const { autoRefresh = true, onRefreshComplete, cacheFirst = true, cacheTtlMs = 24 * 60 * 60 * 1000, // 24 hours default (1 day)
|
|
42
|
+
} = options;
|
|
34
43
|
const dispatch = useAppDispatch();
|
|
35
44
|
const refreshInProgressRef = useRef(false);
|
|
36
45
|
// Refs to store timing measurements
|
|
@@ -40,6 +49,8 @@ export function useScoutRefresh(options = {}) {
|
|
|
40
49
|
userApiDuration: 0,
|
|
41
50
|
dataProcessingDuration: 0,
|
|
42
51
|
localStorageDuration: 0,
|
|
52
|
+
cacheLoadDuration: 0,
|
|
53
|
+
cacheSaveDuration: 0,
|
|
43
54
|
});
|
|
44
55
|
const handleRefresh = useCallback(async () => {
|
|
45
56
|
// Prevent concurrent refresh calls
|
|
@@ -53,14 +64,66 @@ export function useScoutRefresh(options = {}) {
|
|
|
53
64
|
try {
|
|
54
65
|
dispatch(setStatus(EnumScoutStateStatus.LOADING));
|
|
55
66
|
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.LOADING));
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
let cachedHerdModules = null;
|
|
68
|
+
let cacheLoadDuration = 0;
|
|
69
|
+
// Step 1: Load from cache first if enabled
|
|
70
|
+
if (cacheFirst) {
|
|
71
|
+
const cacheStartTime = Date.now();
|
|
72
|
+
try {
|
|
73
|
+
console.log("[useScoutRefresh] Loading from cache...");
|
|
74
|
+
const cacheResult = await scoutCache.getHerdModules();
|
|
75
|
+
cacheLoadDuration = Date.now() - cacheStartTime;
|
|
76
|
+
timingRefs.current.cacheLoadDuration = cacheLoadDuration;
|
|
77
|
+
if (cacheResult.data && cacheResult.data.length > 0) {
|
|
78
|
+
cachedHerdModules = cacheResult.data;
|
|
79
|
+
console.log(`[useScoutRefresh] Loaded ${cachedHerdModules.length} herd modules from cache in ${cacheLoadDuration}ms (age: ${Math.round(cacheResult.age / 1000)}s, stale: ${cacheResult.isStale})`);
|
|
80
|
+
// Set data source to CACHE
|
|
81
|
+
dispatch(setDataSource(EnumDataSource.CACHE));
|
|
82
|
+
dispatch(setDataSourceInfo({
|
|
83
|
+
source: EnumDataSource.CACHE,
|
|
84
|
+
timestamp: Date.now(),
|
|
85
|
+
cacheAge: cacheResult.age,
|
|
86
|
+
isStale: cacheResult.isStale,
|
|
87
|
+
}));
|
|
88
|
+
// Immediately update the store with cached data
|
|
89
|
+
dispatch(setHerdModules(cachedHerdModules));
|
|
90
|
+
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.SUCCESSFULLY_LOADED));
|
|
91
|
+
// If cache is fresh, we can return early
|
|
92
|
+
if (!cacheResult.isStale) {
|
|
93
|
+
console.log("[useScoutRefresh] Cache is fresh, skipping API call");
|
|
94
|
+
// Still need to load user data
|
|
95
|
+
const userStartTime = Date.now();
|
|
96
|
+
const res_new_user = await server_get_user();
|
|
97
|
+
const userApiDuration = Date.now() - userStartTime;
|
|
98
|
+
timingRefs.current.userApiDuration = userApiDuration;
|
|
99
|
+
dispatch(setUserApiDuration(userApiDuration));
|
|
100
|
+
if (res_new_user && res_new_user.data) {
|
|
101
|
+
dispatch(setUser(res_new_user.data));
|
|
102
|
+
}
|
|
103
|
+
const totalDuration = Date.now() - startTime;
|
|
104
|
+
dispatch(setHerdModulesLoadedInMs(totalDuration));
|
|
105
|
+
dispatch(setStatus(EnumScoutStateStatus.DONE_LOADING));
|
|
106
|
+
console.log(`[useScoutRefresh] Cache-first refresh completed in ${totalDuration}ms`);
|
|
107
|
+
onRefreshComplete?.();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log("[useScoutRefresh] No cached data found");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (cacheError) {
|
|
116
|
+
console.warn("[useScoutRefresh] Cache load failed:", cacheError);
|
|
117
|
+
// Continue with API call
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Step 2: Load fresh data from API
|
|
121
|
+
console.log("[useScoutRefresh] Loading fresh data from API...");
|
|
58
122
|
const parallelStartTime = Date.now();
|
|
59
123
|
const [herdModulesResult, userResult] = await Promise.all([
|
|
60
124
|
(async () => {
|
|
61
125
|
const start = Date.now();
|
|
62
126
|
console.log(`[useScoutRefresh] Starting herd modules request at ${new Date(start).toISOString()}`);
|
|
63
|
-
// High priority request with optimization
|
|
64
127
|
const result = await server_load_herd_modules();
|
|
65
128
|
const duration = Date.now() - start;
|
|
66
129
|
console.log(`[useScoutRefresh] Herd modules request completed in ${duration}ms`);
|
|
@@ -69,7 +132,6 @@ export function useScoutRefresh(options = {}) {
|
|
|
69
132
|
(async () => {
|
|
70
133
|
const start = Date.now();
|
|
71
134
|
console.log(`[useScoutRefresh] Starting user request at ${new Date(start).toISOString()}`);
|
|
72
|
-
// High priority request with optimization
|
|
73
135
|
const result = await server_get_user();
|
|
74
136
|
const duration = Date.now() - start;
|
|
75
137
|
console.log(`[useScoutRefresh] User request completed in ${duration}ms`);
|
|
@@ -83,43 +145,12 @@ export function useScoutRefresh(options = {}) {
|
|
|
83
145
|
const res_new_user = userResult.result;
|
|
84
146
|
const herdModulesDuration = herdModulesResult.duration;
|
|
85
147
|
const userApiDuration = userResult.duration;
|
|
86
|
-
// Calculate request timing breakdown
|
|
87
|
-
const requestStartTime = parallelStartTime;
|
|
88
|
-
const requestEndTime = Date.now();
|
|
89
|
-
const totalRequestTime = requestEndTime - requestStartTime;
|
|
90
|
-
console.log(`[useScoutRefresh] Request timing breakdown:`);
|
|
91
|
-
console.log(` - Request started at: ${new Date(requestStartTime).toISOString()}`);
|
|
92
|
-
console.log(` - Request completed at: ${new Date(requestEndTime).toISOString()}`);
|
|
93
|
-
console.log(` - Total request time: ${totalRequestTime}ms`);
|
|
94
|
-
console.log(` - Parallel execution time: ${parallelDuration}ms`);
|
|
95
|
-
console.log(` - Request overhead: ${totalRequestTime - parallelDuration}ms`);
|
|
96
|
-
// Calculate network latency for herd modules
|
|
97
|
-
let networkLatencyMs = 0;
|
|
98
|
-
if (herdModulesResponse.status === EnumWebResponse.SUCCESS &&
|
|
99
|
-
herdModulesResponse.data) {
|
|
100
|
-
const serverFinishTime = herdModulesResponse.time_finished;
|
|
101
|
-
const clientReceiveTime = Date.now();
|
|
102
|
-
const estimatedNetworkLatency = clientReceiveTime - serverFinishTime;
|
|
103
|
-
networkLatencyMs = Math.max(0, estimatedNetworkLatency);
|
|
104
|
-
console.log(`[useScoutRefresh] Herd modules performance:`);
|
|
105
|
-
console.log(` - Server processing: ${herdModulesResponse.server_processing_time_ms}ms`);
|
|
106
|
-
console.log(` - Network latency: ${networkLatencyMs}ms`);
|
|
107
|
-
console.log(` - Total client time: ${herdModulesDuration}ms`);
|
|
108
|
-
}
|
|
109
148
|
// Store timing values
|
|
110
149
|
timingRefs.current.herdModulesDuration = herdModulesDuration;
|
|
111
150
|
timingRefs.current.userApiDuration = userApiDuration;
|
|
112
151
|
// Dispatch timing actions
|
|
113
152
|
dispatch(setHerdModulesApiDuration(herdModulesDuration));
|
|
114
153
|
dispatch(setUserApiDuration(userApiDuration));
|
|
115
|
-
// Calculate network overhead
|
|
116
|
-
const totalApiTime = herdModulesDuration + userApiDuration;
|
|
117
|
-
const networkOverhead = parallelDuration - Math.max(herdModulesDuration, userApiDuration);
|
|
118
|
-
console.log(`[useScoutRefresh] API performance:`);
|
|
119
|
-
console.log(` - Herd modules: ${herdModulesDuration}ms`);
|
|
120
|
-
console.log(` - User API: ${userApiDuration}ms`);
|
|
121
|
-
console.log(` - Parallel execution: ${parallelDuration}ms`);
|
|
122
|
-
console.log(` - Time saved with parallel: ${totalApiTime - parallelDuration}ms`);
|
|
123
154
|
// Validate API responses
|
|
124
155
|
const validationStartTime = Date.now();
|
|
125
156
|
if (!herdModulesResponse.data ||
|
|
@@ -133,7 +164,24 @@ export function useScoutRefresh(options = {}) {
|
|
|
133
164
|
console.log(`[useScoutRefresh] Data validation took: ${validationDuration}ms`);
|
|
134
165
|
// Use the validated data
|
|
135
166
|
const compatible_new_herd_modules = herdModulesResponse.data;
|
|
136
|
-
//
|
|
167
|
+
// Set data source to DATABASE
|
|
168
|
+
dispatch(setDataSource(EnumDataSource.DATABASE));
|
|
169
|
+
dispatch(setDataSourceInfo({
|
|
170
|
+
source: EnumDataSource.DATABASE,
|
|
171
|
+
timestamp: Date.now(),
|
|
172
|
+
}));
|
|
173
|
+
// Step 3: Update cache with fresh data
|
|
174
|
+
const cacheSaveStartTime = Date.now();
|
|
175
|
+
try {
|
|
176
|
+
await scoutCache.setHerdModules(compatible_new_herd_modules, cacheTtlMs);
|
|
177
|
+
const cacheSaveDuration = Date.now() - cacheSaveStartTime;
|
|
178
|
+
timingRefs.current.cacheSaveDuration = cacheSaveDuration;
|
|
179
|
+
console.log(`[useScoutRefresh] Cache updated in ${cacheSaveDuration}ms with TTL: ${Math.round(cacheTtlMs / 1000)}s`);
|
|
180
|
+
}
|
|
181
|
+
catch (cacheError) {
|
|
182
|
+
console.warn("[useScoutRefresh] Cache save failed:", cacheError);
|
|
183
|
+
}
|
|
184
|
+
// Step 4: Update store with fresh data
|
|
137
185
|
const dataProcessingStartTime = Date.now();
|
|
138
186
|
dispatch(setHerdModules(compatible_new_herd_modules));
|
|
139
187
|
dispatch(setUser(res_new_user.data));
|
|
@@ -141,9 +189,8 @@ export function useScoutRefresh(options = {}) {
|
|
|
141
189
|
const dataProcessingDuration = Date.now() - dataProcessingStartTime;
|
|
142
190
|
timingRefs.current.dataProcessingDuration = dataProcessingDuration;
|
|
143
191
|
dispatch(setDataProcessingDuration(dataProcessingDuration));
|
|
144
|
-
//
|
|
192
|
+
// Step 5: Handle localStorage operations
|
|
145
193
|
const localStorageStartTime = Date.now();
|
|
146
|
-
// Safely handle localStorage operations
|
|
147
194
|
try {
|
|
148
195
|
// Check local storage for a last selected herd
|
|
149
196
|
const lastSelectedHerd = localStorage.getItem("last_selected_herd");
|
|
@@ -177,15 +224,24 @@ export function useScoutRefresh(options = {}) {
|
|
|
177
224
|
// Log essential performance metrics
|
|
178
225
|
console.log(`[useScoutRefresh] Refresh completed successfully:`);
|
|
179
226
|
console.log(` - Total duration: ${loadingDuration}ms`);
|
|
180
|
-
console.log(` -
|
|
227
|
+
console.log(` - Cache load: ${cacheLoadDuration}ms`);
|
|
228
|
+
console.log(` - Herd modules API: ${herdModulesDuration}ms`);
|
|
181
229
|
console.log(` - User API: ${userApiDuration}ms`);
|
|
182
|
-
console.log(` -
|
|
183
|
-
console.log(` -
|
|
230
|
+
console.log(` - Cache save: ${timingRefs.current.cacheSaveDuration}ms`);
|
|
231
|
+
console.log(` - Data processing: ${dataProcessingDuration}ms`);
|
|
232
|
+
console.log(` - LocalStorage: ${localStorageDuration}ms`);
|
|
233
|
+
console.log(` - Cache TTL: ${Math.round(cacheTtlMs / 1000)}s`);
|
|
184
234
|
onRefreshComplete?.();
|
|
185
235
|
}
|
|
186
236
|
catch (error) {
|
|
187
237
|
const loadingDuration = Date.now() - startTime;
|
|
188
238
|
console.error("Error refreshing scout data:", error);
|
|
239
|
+
// Set data source to UNKNOWN on error
|
|
240
|
+
dispatch(setDataSource(EnumDataSource.UNKNOWN));
|
|
241
|
+
dispatch(setDataSourceInfo({
|
|
242
|
+
source: EnumDataSource.UNKNOWN,
|
|
243
|
+
timestamp: Date.now(),
|
|
244
|
+
}));
|
|
189
245
|
// Ensure consistent state updates on error
|
|
190
246
|
dispatch(setHerdModulesLoadingState(EnumHerdModulesLoadingState.UNSUCCESSFULLY_LOADED));
|
|
191
247
|
dispatch(setHerdModulesLoadedInMs(loadingDuration));
|
|
@@ -199,7 +255,7 @@ export function useScoutRefresh(options = {}) {
|
|
|
199
255
|
finally {
|
|
200
256
|
refreshInProgressRef.current = false;
|
|
201
257
|
}
|
|
202
|
-
}, [dispatch, onRefreshComplete]);
|
|
258
|
+
}, [dispatch, onRefreshComplete, cacheFirst, cacheTtlMs]);
|
|
203
259
|
useEffect(() => {
|
|
204
260
|
if (autoRefresh) {
|
|
205
261
|
handleRefresh();
|
|
@@ -211,14 +267,45 @@ export function useScoutRefresh(options = {}) {
|
|
|
211
267
|
const startTime = timingRefs.current.startTime;
|
|
212
268
|
return {
|
|
213
269
|
totalDuration: startTime > 0 ? now - startTime : 0,
|
|
270
|
+
cacheLoad: timingRefs.current.cacheLoadDuration,
|
|
214
271
|
herdModulesApi: timingRefs.current.herdModulesDuration,
|
|
215
272
|
userApi: timingRefs.current.userApiDuration,
|
|
273
|
+
cacheSave: timingRefs.current.cacheSaveDuration,
|
|
216
274
|
dataProcessing: timingRefs.current.dataProcessingDuration,
|
|
217
275
|
localStorage: timingRefs.current.localStorageDuration,
|
|
218
276
|
};
|
|
219
277
|
}, []);
|
|
278
|
+
// Utility function to clear cache
|
|
279
|
+
const clearCache = useCallback(async () => {
|
|
280
|
+
try {
|
|
281
|
+
await scoutCache.clearHerdModules();
|
|
282
|
+
console.log("[useScoutRefresh] Cache cleared successfully");
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
console.error("[useScoutRefresh] Failed to clear cache:", error);
|
|
286
|
+
}
|
|
287
|
+
}, []);
|
|
288
|
+
// Utility function to get cache statistics
|
|
289
|
+
const getCacheStats = useCallback(async () => {
|
|
290
|
+
try {
|
|
291
|
+
return await scoutCache.getCacheStats();
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
console.error("[useScoutRefresh] Failed to get cache stats:", error);
|
|
295
|
+
return {
|
|
296
|
+
size: 0,
|
|
297
|
+
lastUpdated: 0,
|
|
298
|
+
isStale: true,
|
|
299
|
+
hitRate: 0,
|
|
300
|
+
totalHits: 0,
|
|
301
|
+
totalMisses: 0,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}, []);
|
|
220
305
|
return {
|
|
221
306
|
handleRefresh,
|
|
222
307
|
getTimingStats,
|
|
308
|
+
clearCache,
|
|
309
|
+
getCacheStats,
|
|
223
310
|
};
|
|
224
311
|
}
|
package/dist/store/scout.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IUser } from "../types/db";
|
|
2
2
|
import { IHerdModule, EnumHerdModulesLoadingState } from "../types/herd_module";
|
|
3
|
+
import { EnumDataSource, IDataSourceInfo } from "../types/data_source";
|
|
3
4
|
export declare enum EnumScoutStateStatus {
|
|
4
5
|
LOADING = "LOADING",
|
|
5
6
|
DONE_LOADING = "DONE_LOADING"
|
|
@@ -17,6 +18,11 @@ export interface ScoutState {
|
|
|
17
18
|
active_device_id: string | null;
|
|
18
19
|
lastRefreshed: number;
|
|
19
20
|
user: IUser | null;
|
|
21
|
+
data_source: EnumDataSource;
|
|
22
|
+
data_source_info: IDataSourceInfo | null;
|
|
23
|
+
}
|
|
24
|
+
export interface RootState {
|
|
25
|
+
scout: ScoutState;
|
|
20
26
|
}
|
|
21
27
|
export declare const scoutSlice: import("@reduxjs/toolkit").Slice<ScoutState, {
|
|
22
28
|
setHerdModules: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
@@ -59,6 +65,14 @@ export declare const scoutSlice: import("@reduxjs/toolkit").Slice<ScoutState, {
|
|
|
59
65
|
payload: any;
|
|
60
66
|
type: string;
|
|
61
67
|
}) => void;
|
|
68
|
+
setDataSource: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
69
|
+
payload: any;
|
|
70
|
+
type: string;
|
|
71
|
+
}) => void;
|
|
72
|
+
setDataSourceInfo: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
73
|
+
payload: any;
|
|
74
|
+
type: string;
|
|
75
|
+
}) => void;
|
|
62
76
|
replaceEventsForHerdModule: (state: import("immer").WritableDraft<ScoutState>, action: {
|
|
63
77
|
payload: any;
|
|
64
78
|
type: string;
|
|
@@ -140,6 +154,6 @@ export declare const scoutSlice: import("@reduxjs/toolkit").Slice<ScoutState, {
|
|
|
140
154
|
type: string;
|
|
141
155
|
}) => void;
|
|
142
156
|
}, "scout", "scout", import("@reduxjs/toolkit").SliceSelectors<ScoutState>>;
|
|
143
|
-
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">, setHerdModulesApiDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesApiDuration">, setUserApiDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUserApiDuration">, setDataProcessingDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataProcessingDuration">, setLocalStorageDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setLocalStorageDuration">, setActiveHerdId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveHerdId">, setActiveDeviceId: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setActiveDeviceId">, appendEventsToHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/appendEventsToHerdModule">, replaceEventsForHerdModule: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/replaceEventsForHerdModule">, 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">;
|
|
157
|
+
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">, setHerdModulesApiDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setHerdModulesApiDuration">, setUserApiDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setUserApiDuration">, setDataProcessingDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setDataProcessingDuration">, setLocalStorageDuration: import("@reduxjs/toolkit").ActionCreatorWithPayload<any, "scout/setLocalStorageDuration">, 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">, 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">;
|
|
144
158
|
declare const _default: import("redux").Reducer<ScoutState>;
|
|
145
159
|
export default _default;
|
package/dist/store/scout.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createSlice } from "@reduxjs/toolkit";
|
|
2
2
|
import { EnumHerdModulesLoadingState } from "../types/herd_module";
|
|
3
|
+
import { EnumDataSource } from "../types/data_source";
|
|
3
4
|
export var EnumScoutStateStatus;
|
|
4
5
|
(function (EnumScoutStateStatus) {
|
|
5
6
|
EnumScoutStateStatus["LOADING"] = "LOADING";
|
|
@@ -19,6 +20,9 @@ const initialState = {
|
|
|
19
20
|
active_herd_id: null,
|
|
20
21
|
active_device_id: null,
|
|
21
22
|
user: null,
|
|
23
|
+
// Initialize data source tracking
|
|
24
|
+
data_source: EnumDataSource.UNKNOWN,
|
|
25
|
+
data_source_info: null,
|
|
22
26
|
};
|
|
23
27
|
export const scoutSlice = createSlice({
|
|
24
28
|
name: "scout",
|
|
@@ -55,6 +59,12 @@ export const scoutSlice = createSlice({
|
|
|
55
59
|
setActiveDeviceId: (state, action) => {
|
|
56
60
|
state.active_device_id = action.payload;
|
|
57
61
|
},
|
|
62
|
+
setDataSource: (state, action) => {
|
|
63
|
+
state.data_source = action.payload;
|
|
64
|
+
},
|
|
65
|
+
setDataSourceInfo: (state, action) => {
|
|
66
|
+
state.data_source_info = action.payload;
|
|
67
|
+
},
|
|
58
68
|
replaceEventsForHerdModule: (state, action) => {
|
|
59
69
|
const { herd_id, events } = action.payload;
|
|
60
70
|
const herd_module = state.herd_modules.find((hm) => hm.herd.id.toString() === herd_id);
|
|
@@ -254,5 +264,5 @@ export const scoutSlice = createSlice({
|
|
|
254
264
|
},
|
|
255
265
|
});
|
|
256
266
|
// Action creators are generated for each case reducer function
|
|
257
|
-
export const { setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiDuration, setUserApiDuration, setDataProcessingDuration, setLocalStorageDuration, setActiveHerdId, setActiveDeviceId, appendEventsToHerdModule, replaceEventsForHerdModule, updateEventValuesForHerdModule, updatePageIndexForHerdModule, appendPlansToHerdModule, setUser, addTag, deleteTag, updateTag, addNewDeviceToHerdModule, updateDeviceForHerdModule, addDevice, deleteDevice, updateDevice, addPlan, deletePlan, updatePlan, addSessionToStore, deleteSessionFromStore, updateSessionInStore, } = scoutSlice.actions;
|
|
267
|
+
export const { setHerdModules, setStatus, setHerdModulesLoadingState, setHerdModulesLoadedInMs, setHerdModulesApiDuration, setUserApiDuration, setDataProcessingDuration, setLocalStorageDuration, setActiveHerdId, setActiveDeviceId, setDataSource, setDataSourceInfo, appendEventsToHerdModule, replaceEventsForHerdModule, updateEventValuesForHerdModule, updatePageIndexForHerdModule, appendPlansToHerdModule, setUser, addTag, deleteTag, updateTag, addNewDeviceToHerdModule, updateDeviceForHerdModule, addDevice, deleteDevice, updateDevice, addPlan, deletePlan, updatePlan, addSessionToStore, deleteSessionFromStore, updateSessionInStore, } = scoutSlice.actions;
|
|
258
268
|
export default scoutSlice.reducer;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enum representing the source of data in the Scout application
|
|
3
|
+
*/
|
|
4
|
+
export declare enum EnumDataSource {
|
|
5
|
+
CACHE = "CACHE",
|
|
6
|
+
DATABASE = "DATABASE",
|
|
7
|
+
UNKNOWN = "UNKNOWN"
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Interface for data source information
|
|
11
|
+
*/
|
|
12
|
+
export interface IDataSourceInfo {
|
|
13
|
+
source: EnumDataSource;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
cacheAge?: number;
|
|
16
|
+
isStale?: boolean;
|
|
17
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enum representing the source of data in the Scout application
|
|
3
|
+
*/
|
|
4
|
+
export var EnumDataSource;
|
|
5
|
+
(function (EnumDataSource) {
|
|
6
|
+
EnumDataSource["CACHE"] = "CACHE";
|
|
7
|
+
EnumDataSource["DATABASE"] = "DATABASE";
|
|
8
|
+
EnumDataSource["UNKNOWN"] = "UNKNOWN";
|
|
9
|
+
})(EnumDataSource || (EnumDataSource = {}));
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED