@adventurelabs/scout-core 1.0.80 → 1.0.83
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 +2 -0
- package/dist/helpers/cache.js +112 -3
- package/dist/helpers/devices.d.ts +5 -4
- package/dist/helpers/devices.js +3 -0
- package/dist/helpers/providers.d.ts +6 -0
- package/dist/helpers/providers.js +78 -0
- package/dist/helpers/sessions.js +35 -15
- package/dist/providers/ScoutRefreshProvider.d.ts +43 -8
- package/dist/store/hooks.d.ts +1 -1
- package/dist/supabase/server.d.ts +2 -963
- package/dist/types/db.d.ts +3 -0
- package/dist/types/herd_module.d.ts +4 -2
- package/dist/types/herd_module.js +23 -6
- package/dist/types/supabase.d.ts +44 -9
- package/dist/types/supabase.js +1 -1
- package/package.json +10 -3
- package/dist/helpers/cache-usage-example.d.ts +0 -17
- package/dist/helpers/cache-usage-example.js +0 -82
- package/dist/hooks/data-source-usage-example.d.ts +0 -9
- package/dist/hooks/data-source-usage-example.js +0 -68
- package/dist/hooks/useDataSource.d.ts +0 -43
- package/dist/hooks/useDataSource.js +0 -63
package/dist/types/db.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export type IUserRolePerHerd = Database["public"]["Tables"]["users_roles_per_her
|
|
|
21
21
|
export type IHerd = Database["public"]["Tables"]["herds"]["Row"];
|
|
22
22
|
export type ISession = Database["public"]["Tables"]["sessions"]["Row"];
|
|
23
23
|
export type IConnectivity = Database["public"]["Tables"]["connectivity"]["Row"];
|
|
24
|
+
export type IProvider = Database["public"]["Tables"]["providers"]["Row"];
|
|
24
25
|
export type IEventWithTags = Database["public"]["CompositeTypes"]["event_with_tags"] & {
|
|
25
26
|
earthranger_url: string | null;
|
|
26
27
|
file_path: string | null;
|
|
@@ -62,3 +63,5 @@ export interface IApiKeyScout {
|
|
|
62
63
|
}
|
|
63
64
|
export type Tag = ITag;
|
|
64
65
|
export type TagClassName = string;
|
|
66
|
+
export type DeviceInsert = Database["public"]["Tables"]["devices"]["Insert"];
|
|
67
|
+
export type DeviceUpdate = Database["public"]["Tables"]["devices"]["Update"];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
-
import { IDevice, IEventWithTags, IHerd, IPlan, ILayer, IUserAndRole, IZoneWithActions, ISessionWithCoordinates } from "../types/db";
|
|
2
|
+
import { IDevice, IEventWithTags, IHerd, IPlan, ILayer, IProvider, IUserAndRole, IZoneWithActions, ISessionWithCoordinates } from "../types/db";
|
|
3
3
|
import { EnumWebResponse } from "./requests";
|
|
4
4
|
export declare enum EnumHerdModulesLoadingState {
|
|
5
5
|
NOT_LOADING = "NOT_LOADING",
|
|
@@ -21,7 +21,8 @@ export declare class HerdModule {
|
|
|
21
21
|
labels: string[];
|
|
22
22
|
plans: IPlan[];
|
|
23
23
|
layers: ILayer[];
|
|
24
|
-
|
|
24
|
+
providers: IProvider[];
|
|
25
|
+
constructor(herd: IHerd, devices: IDevice[], events: IEventWithTags[], timestamp_last_refreshed: number, user_roles?: IUserAndRole[] | null, events_page_index?: number, total_events?: number, total_events_with_filters?: number, labels?: string[], plans?: IPlan[], zones?: IZoneWithActions[], sessions?: ISessionWithCoordinates[], layers?: ILayer[], providers?: IProvider[]);
|
|
25
26
|
to_serializable(): IHerdModule;
|
|
26
27
|
static from_herd(herd: IHerd, client: SupabaseClient): Promise<HerdModule>;
|
|
27
28
|
}
|
|
@@ -39,6 +40,7 @@ export interface IHerdModule {
|
|
|
39
40
|
zones: IZoneWithActions[];
|
|
40
41
|
sessions: ISessionWithCoordinates[];
|
|
41
42
|
layers: ILayer[];
|
|
43
|
+
providers: IProvider[];
|
|
42
44
|
}
|
|
43
45
|
export interface IHerdModulesResponse {
|
|
44
46
|
data: IHerdModule[];
|
|
@@ -4,6 +4,7 @@ import { server_get_total_events_by_herd } from "../helpers/events";
|
|
|
4
4
|
import { EnumSessionsVisibility } from "./events";
|
|
5
5
|
import { server_get_plans_by_herd } from "../helpers/plans";
|
|
6
6
|
import { server_get_layers_by_herd } from "../helpers/layers";
|
|
7
|
+
import { server_get_providers_by_herd } from "../helpers/providers";
|
|
7
8
|
import { server_get_events_and_tags_for_devices_batch } from "../helpers/tags";
|
|
8
9
|
import { server_get_users_with_herd_access } from "../helpers/users";
|
|
9
10
|
import { EnumWebResponse } from "./requests";
|
|
@@ -18,7 +19,7 @@ export var EnumHerdModulesLoadingState;
|
|
|
18
19
|
EnumHerdModulesLoadingState["UNSUCCESSFULLY_LOADED"] = "UNSUCCESSFULLY_LOADED";
|
|
19
20
|
})(EnumHerdModulesLoadingState || (EnumHerdModulesLoadingState = {}));
|
|
20
21
|
export class HerdModule {
|
|
21
|
-
constructor(herd, devices, events, timestamp_last_refreshed, user_roles = null, events_page_index = 0, total_events = 0, total_events_with_filters = 0, labels = [], plans = [], zones = [], sessions = [], layers = []) {
|
|
22
|
+
constructor(herd, devices, events, timestamp_last_refreshed, user_roles = null, events_page_index = 0, total_events = 0, total_events_with_filters = 0, labels = [], plans = [], zones = [], sessions = [], layers = [], providers = []) {
|
|
22
23
|
this.user_roles = null;
|
|
23
24
|
this.events_page_index = 0;
|
|
24
25
|
this.total_events = 0;
|
|
@@ -26,6 +27,7 @@ export class HerdModule {
|
|
|
26
27
|
this.labels = [];
|
|
27
28
|
this.plans = [];
|
|
28
29
|
this.layers = [];
|
|
30
|
+
this.providers = [];
|
|
29
31
|
this.herd = herd;
|
|
30
32
|
this.devices = devices;
|
|
31
33
|
this.events = events;
|
|
@@ -39,6 +41,7 @@ export class HerdModule {
|
|
|
39
41
|
this.zones = zones;
|
|
40
42
|
this.sessions = sessions;
|
|
41
43
|
this.layers = layers;
|
|
44
|
+
this.providers = providers;
|
|
42
45
|
}
|
|
43
46
|
to_serializable() {
|
|
44
47
|
return {
|
|
@@ -55,6 +58,7 @@ export class HerdModule {
|
|
|
55
58
|
zones: this.zones,
|
|
56
59
|
sessions: this.sessions,
|
|
57
60
|
layers: this.layers,
|
|
61
|
+
providers: this.providers,
|
|
58
62
|
};
|
|
59
63
|
}
|
|
60
64
|
static async from_herd(herd, client) {
|
|
@@ -95,7 +99,7 @@ export class HerdModule {
|
|
|
95
99
|
}
|
|
96
100
|
}
|
|
97
101
|
// Run all remaining requests in parallel with individual error handling
|
|
98
|
-
const [res_zones, res_user_roles, total_event_count, res_plans, res_sessions, res_layers,] = await Promise.allSettled([
|
|
102
|
+
const [res_zones, res_user_roles, total_event_count, res_plans, res_sessions, res_layers, res_providers,] = await Promise.allSettled([
|
|
99
103
|
server_get_more_zones_and_actions_for_herd(herd.id, 0, 10).catch((error) => {
|
|
100
104
|
console.warn(`[HerdModule] Failed to get zones and actions:`, error);
|
|
101
105
|
return { status: EnumWebResponse.ERROR, data: null };
|
|
@@ -114,12 +118,20 @@ export class HerdModule {
|
|
|
114
118
|
}),
|
|
115
119
|
server_get_sessions_by_herd_id(herd.id).catch((error) => {
|
|
116
120
|
console.warn(`[HerdModule] Failed to get sessions:`, error);
|
|
117
|
-
return {
|
|
121
|
+
return {
|
|
122
|
+
status: EnumWebResponse.ERROR,
|
|
123
|
+
data: [],
|
|
124
|
+
msg: error.message,
|
|
125
|
+
};
|
|
118
126
|
}),
|
|
119
127
|
server_get_layers_by_herd(herd.id).catch((error) => {
|
|
120
128
|
console.warn(`[HerdModule] Failed to get layers:`, error);
|
|
121
129
|
return { status: EnumWebResponse.ERROR, data: null };
|
|
122
130
|
}),
|
|
131
|
+
server_get_providers_by_herd(herd.id).catch((error) => {
|
|
132
|
+
console.warn(`[HerdModule] Failed to get providers:`, error);
|
|
133
|
+
return { status: EnumWebResponse.ERROR, data: null };
|
|
134
|
+
}),
|
|
123
135
|
]);
|
|
124
136
|
// Assign recent events to devices from batch results
|
|
125
137
|
for (let i = 0; i < new_devices.length; i++) {
|
|
@@ -148,23 +160,28 @@ export class HerdModule {
|
|
|
148
160
|
const plans = res_plans.status === "fulfilled" && res_plans.value?.data
|
|
149
161
|
? res_plans.value.data
|
|
150
162
|
: [];
|
|
151
|
-
const sessions = res_sessions.status === "fulfilled" && res_sessions.value?.data
|
|
163
|
+
const sessions = res_sessions.status === "fulfilled" && res_sessions.value?.data
|
|
164
|
+
? res_sessions.value.data
|
|
165
|
+
: [];
|
|
152
166
|
const layers = res_layers.status === "fulfilled" && res_layers.value?.data
|
|
153
167
|
? res_layers.value.data
|
|
154
168
|
: [];
|
|
169
|
+
const providers = res_providers.status === "fulfilled" && res_providers.value?.data
|
|
170
|
+
? res_providers.value.data
|
|
171
|
+
: [];
|
|
155
172
|
// TODO: store in DB and retrieve on load?
|
|
156
173
|
const newLabels = LABELS;
|
|
157
174
|
const endTime = Date.now();
|
|
158
175
|
const loadTime = endTime - startTime;
|
|
159
176
|
console.log(`[HerdModule] Loaded herd ${herd.slug} in ${loadTime}ms (${new_devices.length} devices)`);
|
|
160
|
-
return new HerdModule(herd, new_devices, [], Date.now(), user_roles, 0, total_events, total_events, newLabels, plans, zones, sessions, layers);
|
|
177
|
+
return new HerdModule(herd, new_devices, [], Date.now(), user_roles, 0, total_events, total_events, newLabels, plans, zones, sessions, layers, providers);
|
|
161
178
|
}
|
|
162
179
|
catch (error) {
|
|
163
180
|
const endTime = Date.now();
|
|
164
181
|
const loadTime = endTime - startTime;
|
|
165
182
|
console.error(`[HerdModule] Critical error in HerdModule.from_herd (${loadTime}ms):`, error);
|
|
166
183
|
// Return a minimal but valid HerdModule instance to prevent complete failure
|
|
167
|
-
return new HerdModule(herd, [], [], Date.now(), null, 0, 0, 0, [], [], [], [], []);
|
|
184
|
+
return new HerdModule(herd, [], [], Date.now(), null, 0, 0, 0, [], [], [], [], [], []);
|
|
168
185
|
}
|
|
169
186
|
}
|
|
170
187
|
}
|
package/dist/types/supabase.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type Json = string | number | boolean | null | {
|
|
|
3
3
|
} | Json[];
|
|
4
4
|
export type Database = {
|
|
5
5
|
__InternalSupabase: {
|
|
6
|
-
PostgrestVersion: "
|
|
6
|
+
PostgrestVersion: "13.0.5";
|
|
7
7
|
};
|
|
8
8
|
public: {
|
|
9
9
|
Tables: {
|
|
@@ -416,6 +416,41 @@ export type Database = {
|
|
|
416
416
|
}
|
|
417
417
|
];
|
|
418
418
|
};
|
|
419
|
+
providers: {
|
|
420
|
+
Row: {
|
|
421
|
+
created_at: string;
|
|
422
|
+
herd_id: number;
|
|
423
|
+
id: number;
|
|
424
|
+
key: string | null;
|
|
425
|
+
source: string;
|
|
426
|
+
type: string;
|
|
427
|
+
};
|
|
428
|
+
Insert: {
|
|
429
|
+
created_at?: string;
|
|
430
|
+
herd_id: number;
|
|
431
|
+
id?: number;
|
|
432
|
+
key?: string | null;
|
|
433
|
+
source: string;
|
|
434
|
+
type: string;
|
|
435
|
+
};
|
|
436
|
+
Update: {
|
|
437
|
+
created_at?: string;
|
|
438
|
+
herd_id?: number;
|
|
439
|
+
id?: number;
|
|
440
|
+
key?: string | null;
|
|
441
|
+
source?: string;
|
|
442
|
+
type?: string;
|
|
443
|
+
};
|
|
444
|
+
Relationships: [
|
|
445
|
+
{
|
|
446
|
+
foreignKeyName: "providers_herd_id_fkey";
|
|
447
|
+
columns: ["herd_id"];
|
|
448
|
+
isOneToOne: false;
|
|
449
|
+
referencedRelation: "herds";
|
|
450
|
+
referencedColumns: ["id"];
|
|
451
|
+
}
|
|
452
|
+
];
|
|
453
|
+
};
|
|
419
454
|
sessions: {
|
|
420
455
|
Row: {
|
|
421
456
|
altitude_average: number;
|
|
@@ -427,9 +462,9 @@ export type Database = {
|
|
|
427
462
|
earthranger_url: string | null;
|
|
428
463
|
id: number;
|
|
429
464
|
inserted_at: string;
|
|
430
|
-
locations: unknown;
|
|
465
|
+
locations: unknown | null;
|
|
431
466
|
software_version: string;
|
|
432
|
-
timestamp_end: string;
|
|
467
|
+
timestamp_end: string | null;
|
|
433
468
|
timestamp_start: string;
|
|
434
469
|
velocity_average: number;
|
|
435
470
|
velocity_max: number;
|
|
@@ -445,9 +480,9 @@ export type Database = {
|
|
|
445
480
|
earthranger_url?: string | null;
|
|
446
481
|
id?: number;
|
|
447
482
|
inserted_at?: string;
|
|
448
|
-
locations
|
|
483
|
+
locations?: unknown | null;
|
|
449
484
|
software_version: string;
|
|
450
|
-
timestamp_end
|
|
485
|
+
timestamp_end?: string | null;
|
|
451
486
|
timestamp_start: string;
|
|
452
487
|
velocity_average: number;
|
|
453
488
|
velocity_max: number;
|
|
@@ -463,9 +498,9 @@ export type Database = {
|
|
|
463
498
|
earthranger_url?: string | null;
|
|
464
499
|
id?: number;
|
|
465
500
|
inserted_at?: string;
|
|
466
|
-
locations?: unknown;
|
|
501
|
+
locations?: unknown | null;
|
|
467
502
|
software_version?: string;
|
|
468
|
-
timestamp_end?: string;
|
|
503
|
+
timestamp_end?: string | null;
|
|
469
504
|
timestamp_start?: string;
|
|
470
505
|
velocity_average?: number;
|
|
471
506
|
velocity_max?: number;
|
|
@@ -869,7 +904,7 @@ export type Database = {
|
|
|
869
904
|
device_type: "trail_camera" | "drone_fixed_wing" | "drone_quad" | "gps_tracker" | "sentry_tower" | "smart_buoy" | "radio_mesh_base_station" | "radio_mesh_repeater" | "unknown";
|
|
870
905
|
media_type: "image" | "video" | "audio" | "text";
|
|
871
906
|
plan_type: "mission" | "fence" | "rally" | "markov";
|
|
872
|
-
role: "admin" | "viewer" | "editor";
|
|
907
|
+
role: "admin" | "viewer" | "editor" | "operator";
|
|
873
908
|
tag_observation_type: "manual" | "auto";
|
|
874
909
|
user_status: "ONLINE" | "OFFLINE";
|
|
875
910
|
};
|
|
@@ -1068,7 +1103,7 @@ export declare const Constants: {
|
|
|
1068
1103
|
readonly device_type: readonly ["trail_camera", "drone_fixed_wing", "drone_quad", "gps_tracker", "sentry_tower", "smart_buoy", "radio_mesh_base_station", "radio_mesh_repeater", "unknown"];
|
|
1069
1104
|
readonly media_type: readonly ["image", "video", "audio", "text"];
|
|
1070
1105
|
readonly plan_type: readonly ["mission", "fence", "rally", "markov"];
|
|
1071
|
-
readonly role: readonly ["admin", "viewer", "editor"];
|
|
1106
|
+
readonly role: readonly ["admin", "viewer", "editor", "operator"];
|
|
1072
1107
|
readonly tag_observation_type: readonly ["manual", "auto"];
|
|
1073
1108
|
readonly user_status: readonly ["ONLINE", "OFFLINE"];
|
|
1074
1109
|
};
|
package/dist/types/supabase.js
CHANGED
|
@@ -15,7 +15,7 @@ export const Constants = {
|
|
|
15
15
|
],
|
|
16
16
|
media_type: ["image", "video", "audio", "text"],
|
|
17
17
|
plan_type: ["mission", "fence", "rally", "markov"],
|
|
18
|
-
role: ["admin", "viewer", "editor"],
|
|
18
|
+
role: ["admin", "viewer", "editor", "operator"],
|
|
19
19
|
tag_observation_type: ["manual", "auto"],
|
|
20
20
|
user_status: ["ONLINE", "OFFLINE"],
|
|
21
21
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adventurelabs/scout-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.83",
|
|
4
4
|
"description": "Core utilities and helpers for Adventure Labs Scout applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,12 +38,19 @@
|
|
|
38
38
|
"react-dom": ">=18.0.0",
|
|
39
39
|
"react-redux": ">=9.0.0",
|
|
40
40
|
"@supabase/supabase-js": "^2.57.4",
|
|
41
|
-
"@supabase/ssr": "^0.
|
|
41
|
+
"@supabase/ssr": "^0.7.0",
|
|
42
42
|
"@reduxjs/toolkit": "^2.0.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/react": "^18.0.0",
|
|
46
46
|
"@types/react-dom": "^18.0.0",
|
|
47
|
-
"typescript": "^5.0.0"
|
|
47
|
+
"typescript": "^5.0.0",
|
|
48
|
+
"next": ">=15.0.0",
|
|
49
|
+
"react": ">=18.0.0",
|
|
50
|
+
"react-dom": ">=18.0.0",
|
|
51
|
+
"react-redux": ">=9.0.0",
|
|
52
|
+
"@supabase/supabase-js": "^2.57.4",
|
|
53
|
+
"@supabase/ssr": "^0.7.0",
|
|
54
|
+
"@reduxjs/toolkit": "^2.0.0"
|
|
48
55
|
}
|
|
49
56
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,82 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
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;
|
|
@@ -1,68 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
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;
|
|
@@ -1,63 +0,0 @@
|
|
|
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
|
-
}
|