@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.
@@ -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[]>>;
@@ -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
- import { EnumWebResponse, IWebResponse, } from "../types/requests";
56
- import { addSignedUrlsToEvents, addSignedUrlToEvent } from "./storage";
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 get_events_with_tags_for_herd(herd_id, offset, limit)
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 get_events_with_tags_for_device(device_id, limit)
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.72",
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