@adventurelabs/scout-core 1.0.72 → 1.0.73
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/tags.d.ts +1 -0
- package/dist/helpers/tags.js +24 -4
- package/package.json +5 -8
- package/helpers/README.md +0 -135
- package/hooks/README.md +0 -93
package/dist/helpers/tags.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IEventAndTagsPrettyLocation, ITag } from "../types/db";
|
|
2
2
|
import { IWebResponseCompatible } from "../types/requests";
|
|
3
|
+
export declare function test_event_loading(device_id: number): Promise<boolean>;
|
|
3
4
|
export declare function server_create_tags(tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
|
|
4
5
|
export declare function server_delete_tags_by_ids(tag_ids: number[]): Promise<IWebResponseCompatible<boolean>>;
|
|
5
6
|
export declare function server_update_tags(tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
|
package/dist/helpers/tags.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
// add tag to db
|
|
3
3
|
import { newServerClient } from "../supabase/server";
|
|
4
|
+
import { EnumWebResponse, IWebResponse, } from "../types/requests";
|
|
5
|
+
import { addSignedUrlsToEvents, addSignedUrlToEvent } from "./storage";
|
|
4
6
|
// Helper functions to extract coordinates from location field
|
|
5
7
|
function extractLatitude(location) {
|
|
6
8
|
try {
|
|
@@ -52,8 +54,25 @@ function extractLongitude(location) {
|
|
|
52
54
|
}
|
|
53
55
|
return null;
|
|
54
56
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
// Test function to verify individual event loading works
|
|
58
|
+
export async function test_event_loading(device_id) {
|
|
59
|
+
try {
|
|
60
|
+
console.log(`[Event Test] Testing individual event loading for device ${device_id}`);
|
|
61
|
+
const events_response = await server_get_events_and_tags_for_device(device_id, 1);
|
|
62
|
+
if (events_response.status === EnumWebResponse.SUCCESS) {
|
|
63
|
+
console.log(`[Event Test] Successfully loaded ${events_response.data?.length || 0} events for device ${device_id}`);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
console.error(`[Event Test] Failed to load events for device ${device_id}:`, events_response.msg);
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error(`[Event Test] Failed to load events for device ${device_id}:`, error);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
57
76
|
export async function server_create_tags(tags) {
|
|
58
77
|
const supabase = await newServerClient();
|
|
59
78
|
// remove id key from tags
|
|
@@ -130,7 +149,7 @@ export async function server_get_more_events_with_tags_by_herd(herd_id, offset,
|
|
|
130
149
|
const from = offset * page_count;
|
|
131
150
|
const to = from + page_count - 1;
|
|
132
151
|
const supabase = await newServerClient();
|
|
133
|
-
// make rpc call to
|
|
152
|
+
// make rpc call to get_events_and_tags_for_herd(herd_id, offset, limit)
|
|
134
153
|
const { data, error } = await supabase.rpc("get_events_and_tags_for_herd", {
|
|
135
154
|
herd_id_caller: herd_id,
|
|
136
155
|
offset_caller: from,
|
|
@@ -157,7 +176,7 @@ export async function server_get_more_events_with_tags_by_herd(herd_id, offset,
|
|
|
157
176
|
}
|
|
158
177
|
export async function server_get_events_and_tags_for_device(device_id, limit = 3) {
|
|
159
178
|
const supabase = await newServerClient();
|
|
160
|
-
// make rpc call to
|
|
179
|
+
// make rpc call to get_events_and_tags_for_device(device_id, limit)
|
|
161
180
|
const { data, error } = await supabase.rpc("get_events_and_tags_for_device", {
|
|
162
181
|
device_id_caller: device_id,
|
|
163
182
|
limit_caller: limit,
|
|
@@ -231,6 +250,7 @@ export async function server_get_events_and_tags_for_devices_batch(device_ids, l
|
|
|
231
250
|
timestamp_observation: row.timestamp_observation,
|
|
232
251
|
is_public: row.is_public,
|
|
233
252
|
earthranger_url: row.earthranger_url,
|
|
253
|
+
herd_id: row.herd_id,
|
|
234
254
|
tags: Array.isArray(row.tags) ? row.tags : [],
|
|
235
255
|
};
|
|
236
256
|
eventsByDevice[device_id].push(event);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adventurelabs/scout-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.73",
|
|
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",
|
|
@@ -36,17 +36,14 @@
|
|
|
36
36
|
"next": ">=15.0.0",
|
|
37
37
|
"react": ">=18.0.0",
|
|
38
38
|
"react-dom": ">=18.0.0",
|
|
39
|
-
"react-redux": ">=9.0.0"
|
|
39
|
+
"react-redux": ">=9.0.0",
|
|
40
|
+
"@supabase/supabase-js": "^2.57.4",
|
|
41
|
+
"@supabase/ssr": "^0.6.1",
|
|
42
|
+
"@reduxjs/toolkit": "^2.0.0"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
45
|
"@types/react": "^18.0.0",
|
|
43
46
|
"@types/react-dom": "^18.0.0",
|
|
44
47
|
"typescript": "^5.0.0"
|
|
45
|
-
},
|
|
46
|
-
"dependencies": {
|
|
47
|
-
"@reduxjs/toolkit": "^2.0.0",
|
|
48
|
-
"@supabase/ssr": "^0.6.1",
|
|
49
|
-
"@supabase/supabase-js": "^2.53.0",
|
|
50
|
-
"zustand": "^4.0.0"
|
|
51
48
|
}
|
|
52
49
|
}
|
package/helpers/README.md
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# Scout Core Helpers
|
|
2
|
-
|
|
3
|
-
## Storage and Signed URLs
|
|
4
|
-
|
|
5
|
-
This module provides functionality for handling file storage and signed URL generation for media files.
|
|
6
|
-
|
|
7
|
-
### Key Features
|
|
8
|
-
|
|
9
|
-
- **Signed URL Generation**: Generate secure, time-limited URLs for accessing media files
|
|
10
|
-
- **Backward Compatibility**: Support for both new signed URLs and legacy public URLs
|
|
11
|
-
- **Seamless Integration**: Signed URLs are automatically set as `media_url` for easy use
|
|
12
|
-
- **Efficient Client Usage**: Accepts existing Supabase client to avoid creating multiple instances
|
|
13
|
-
|
|
14
|
-
### Files
|
|
15
|
-
|
|
16
|
-
- **`storage.ts`**: Server-side functions for generating signed URLs (requires "use server")
|
|
17
|
-
- **`eventUtils.ts`**: Client-side utility functions for working with event media URLs
|
|
18
|
-
|
|
19
|
-
### Storage Functions (Server-side)
|
|
20
|
-
|
|
21
|
-
#### `generateSignedUrl(filePath, expiresIn, supabaseClient?)`
|
|
22
|
-
|
|
23
|
-
Generates a signed URL for a file in Supabase storage.
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
// With existing client
|
|
27
|
-
const signedUrl = await generateSignedUrl(
|
|
28
|
-
"events/123/image.jpg",
|
|
29
|
-
3600,
|
|
30
|
-
supabaseClient
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
// Without existing client (creates new one)
|
|
34
|
-
const signedUrl = await generateSignedUrl("events/123/image.jpg", 3600);
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
#### `addSignedUrlsToEvents(events, supabaseClient?)`
|
|
38
|
-
|
|
39
|
-
Adds signed URLs to an array of events, setting them as `media_url`.
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
// With existing client
|
|
43
|
-
const eventsWithUrls = await addSignedUrlsToEvents(events, supabaseClient);
|
|
44
|
-
|
|
45
|
-
// Without existing client (creates new one)
|
|
46
|
-
const eventsWithUrls = await addSignedUrlsToEvents(events);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
#### `addSignedUrlToEvent(event, supabaseClient?)`
|
|
50
|
-
|
|
51
|
-
Adds a signed URL to a single event, setting it as `media_url`.
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
// With existing client
|
|
55
|
-
const eventWithUrl = await addSignedUrlToEvent(event, supabaseClient);
|
|
56
|
-
|
|
57
|
-
// Without existing client (creates new one)
|
|
58
|
-
const eventWithUrl = await addSignedUrlToEvent(event);
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Event Utility Functions (Client-side)
|
|
62
|
-
|
|
63
|
-
#### `getEventMediaUrl(event)`
|
|
64
|
-
|
|
65
|
-
Gets the media URL for an event (now simply returns `event.media_url`).
|
|
66
|
-
|
|
67
|
-
```typescript
|
|
68
|
-
const mediaUrl = getEventMediaUrl(event);
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
#### `hasEventMedia(event)`
|
|
72
|
-
|
|
73
|
-
Checks if an event has any media URL available.
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
const hasMedia = hasEventMedia(event);
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Usage in Components
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
import { getEventMediaUrl, hasEventMedia } from "@adventurelabs/scout-core";
|
|
83
|
-
|
|
84
|
-
function EventMedia({ event }) {
|
|
85
|
-
const mediaUrl = getEventMediaUrl(event);
|
|
86
|
-
|
|
87
|
-
if (!hasEventMedia(event)) {
|
|
88
|
-
return <div>No media available</div>;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return <img src={mediaUrl} alt="Event media" />;
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Database Schema
|
|
96
|
-
|
|
97
|
-
Events now have two URL-related fields:
|
|
98
|
-
|
|
99
|
-
- `file_path`: The storage path for generating signed URLs
|
|
100
|
-
- `media_url`: The URL for accessing media (signed URL when file_path exists, legacy public URL otherwise)
|
|
101
|
-
|
|
102
|
-
### How It Works
|
|
103
|
-
|
|
104
|
-
1. **Event Creation**: Files are uploaded and `file_path` is stored (no `media_url` initially)
|
|
105
|
-
2. **Event Fetching**: When events are retrieved, signed URLs are generated and set as `media_url`
|
|
106
|
-
3. **URL Access**: Components simply use `event.media_url` as before
|
|
107
|
-
4. **Fallback**: If signed URL generation fails, existing `media_url` is preserved
|
|
108
|
-
|
|
109
|
-
### Performance Optimization
|
|
110
|
-
|
|
111
|
-
The storage functions accept an optional `supabaseClient` parameter to reuse existing client instances:
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
// Efficient: Reuse existing client
|
|
115
|
-
const supabase = await newServerClient();
|
|
116
|
-
const events = await fetchEvents(supabase);
|
|
117
|
-
const eventsWithUrls = await addSignedUrlsToEvents(events, supabase);
|
|
118
|
-
|
|
119
|
-
// Less efficient: Creates new client for each operation
|
|
120
|
-
const events = await fetchEvents();
|
|
121
|
-
const eventsWithUrls = await addSignedUrlsToEvents(events); // Creates new client
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Migration Strategy
|
|
125
|
-
|
|
126
|
-
1. **Phase 1**: Store `file_path` for new events (no `media_url`)
|
|
127
|
-
2. **Phase 2**: Generate signed URLs on fetch and set as `media_url`
|
|
128
|
-
3. **Phase 3**: All events use signed URLs seamlessly
|
|
129
|
-
|
|
130
|
-
### Security Benefits
|
|
131
|
-
|
|
132
|
-
- **Time-limited access**: URLs expire after a configurable time
|
|
133
|
-
- **Secure access**: URLs are cryptographically signed
|
|
134
|
-
- **No public exposure**: Files are not publicly accessible without signed URLs
|
|
135
|
-
- **Seamless integration**: No changes needed in existing components
|
package/hooks/README.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# Scout Hooks
|
|
2
|
-
|
|
3
|
-
This directory contains React hooks for the Scout application.
|
|
4
|
-
|
|
5
|
-
## useScoutRefresh
|
|
6
|
-
|
|
7
|
-
A hook for refreshing scout data with detailed timing measurements for performance monitoring and debugging.
|
|
8
|
-
|
|
9
|
-
### Features
|
|
10
|
-
|
|
11
|
-
- **Automatic refresh**: Automatically refreshes data on component mount
|
|
12
|
-
- **Manual refresh**: Provides a function to manually trigger refreshes
|
|
13
|
-
- **Detailed timing**: Measures the duration of each portion of the loading process
|
|
14
|
-
- **Concurrent protection**: Prevents multiple simultaneous refresh operations
|
|
15
|
-
- **Error handling**: Graceful error handling with state consistency
|
|
16
|
-
|
|
17
|
-
### Timing Measurements
|
|
18
|
-
|
|
19
|
-
The hook tracks the duration of several key operations:
|
|
20
|
-
|
|
21
|
-
1. **Herd Modules API Call** (`herd_modules_api_duration_ms`): Time taken to fetch herd modules from the server
|
|
22
|
-
2. **User API Call** (`user_api_duration_ms`): Time taken to fetch user data from the server
|
|
23
|
-
3. **Data Processing** (`data_processing_duration_ms`): Time taken to process and dispatch data to the store
|
|
24
|
-
4. **LocalStorage Operations** (`localStorage_duration_ms`): Time taken for localStorage read/write operations
|
|
25
|
-
5. **Total Duration** (`herd_modules_loaded_in_ms`): Overall time from start to completion
|
|
26
|
-
|
|
27
|
-
### Usage
|
|
28
|
-
|
|
29
|
-
```tsx
|
|
30
|
-
import { useScoutRefresh } from "../hooks";
|
|
31
|
-
|
|
32
|
-
function MyComponent() {
|
|
33
|
-
const { handleRefresh, getTimingStats } = useScoutRefresh({
|
|
34
|
-
autoRefresh: true,
|
|
35
|
-
onRefreshComplete: () => {
|
|
36
|
-
console.log("Refresh completed!");
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const handleManualRefresh = async () => {
|
|
41
|
-
await handleRefresh();
|
|
42
|
-
|
|
43
|
-
// Get timing statistics
|
|
44
|
-
const stats = getTimingStats();
|
|
45
|
-
console.log("Performance breakdown:");
|
|
46
|
-
console.log(`- Herd modules API: ${stats.herdModulesApi}ms`);
|
|
47
|
-
console.log(`- User API: ${stats.userApi}ms`);
|
|
48
|
-
console.log(`- Data processing: ${stats.dataProcessing}ms`);
|
|
49
|
-
console.log(`- LocalStorage: ${stats.localStorage}ms`);
|
|
50
|
-
console.log(`- Total: ${stats.totalDuration}ms`);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<div>
|
|
55
|
-
<button onClick={handleManualRefresh}>Refresh Data</button>
|
|
56
|
-
</div>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Redux State
|
|
62
|
-
|
|
63
|
-
The hook automatically updates the Redux store with timing information:
|
|
64
|
-
|
|
65
|
-
```tsx
|
|
66
|
-
// Access timing data from the store
|
|
67
|
-
const timingData = useSelector((state) => ({
|
|
68
|
-
herdModulesApi: state.scout.herd_modules_api_duration_ms,
|
|
69
|
-
userApi: state.scout.user_api_duration_ms,
|
|
70
|
-
dataProcessing: state.scout.data_processing_duration_ms,
|
|
71
|
-
localStorage: state.scout.localStorage_duration_ms,
|
|
72
|
-
total: state.scout.herd_modules_loaded_in_ms,
|
|
73
|
-
}));
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Performance Monitoring
|
|
77
|
-
|
|
78
|
-
Use these timing measurements to:
|
|
79
|
-
|
|
80
|
-
- Identify performance bottlenecks in the loading process
|
|
81
|
-
- Monitor API response times
|
|
82
|
-
- Track data processing efficiency
|
|
83
|
-
- Debug localStorage performance issues
|
|
84
|
-
- Set performance budgets and alerts
|
|
85
|
-
|
|
86
|
-
### Error Handling
|
|
87
|
-
|
|
88
|
-
The hook includes comprehensive error handling:
|
|
89
|
-
|
|
90
|
-
- API response validation
|
|
91
|
-
- Graceful fallbacks for localStorage failures
|
|
92
|
-
- Consistent state updates even on errors
|
|
93
|
-
- Detailed error logging for debugging
|