@adventurelabs/scout-core 1.0.46 → 1.0.48

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,4 +1,5 @@
1
1
  import { IApiKeyScout } from "../types/db";
2
+ export declare function test_api_key_loading(device_id: string): Promise<boolean>;
2
3
  export declare function server_list_api_keys(device_id: string): Promise<IApiKeyScout[]>;
3
4
  export declare function server_list_api_keys_batch(device_ids: string[]): Promise<{
4
5
  [device_id: number]: IApiKeyScout[];
@@ -1,5 +1,18 @@
1
1
  "use server";
2
2
  import { newServerClient } from "../supabase/server";
3
+ // Test function to verify individual API key loading works
4
+ export async function test_api_key_loading(device_id) {
5
+ try {
6
+ console.log(`[API Key Test] Testing individual API key loading for device ${device_id}`);
7
+ const api_keys = await server_list_api_keys(device_id);
8
+ console.log(`[API Key Test] Successfully loaded ${api_keys.length} API keys for device ${device_id}`);
9
+ return true;
10
+ }
11
+ catch (error) {
12
+ console.error(`[API Key Test] Failed to load API keys for device ${device_id}:`, error);
13
+ return false;
14
+ }
15
+ }
3
16
  export async function server_list_api_keys(device_id) {
4
17
  const supabase = await newServerClient();
5
18
  const { data, error } = await supabase.rpc("load_api_keys", {
@@ -19,37 +32,70 @@ export async function server_list_api_keys(device_id) {
19
32
  }
20
33
  export async function server_list_api_keys_batch(device_ids) {
21
34
  const startTime = Date.now();
22
- console.log(`[API Keys Batch] Starting batch load for ${device_ids.length} devices at ${new Date().toISOString()}`);
23
- console.log(`[API Keys Batch] Debug: device_ids array:`, device_ids);
24
- console.log(`[API Keys Batch] Debug: device_ids types:`, device_ids.map((id) => ({ id, type: typeof id })));
25
35
  const supabase = await newServerClient();
26
- console.log(`[API Keys Batch] Making RPC call to load_api_keys_batch...`);
27
- const { data, error } = await supabase.rpc("load_api_keys_batch", {
28
- device_ids: device_ids,
29
- });
30
- const duration = Date.now() - startTime;
31
- if (error) {
32
- console.error(`[API Keys Batch] ERROR after ${duration}ms:`, error.message);
33
- return {};
34
- }
35
- if (!data) {
36
- console.log(`[API Keys Batch] No data returned after ${duration}ms`);
37
- return {};
38
- }
39
- console.log(`[API Keys Batch] RPC completed in ${duration}ms, received ${data.length} API key records`);
40
- const result = {};
41
- // Group API keys by device_id
42
- data.forEach((item) => {
43
- const device_id = item.device_id;
44
- if (!result[device_id]) {
45
- result[device_id] = [];
36
+ // Check if the batch function exists by trying a simple call
37
+ try {
38
+ const { data, error } = await supabase.rpc("load_api_keys_batch", {
39
+ device_ids: device_ids,
40
+ });
41
+ if (error) {
42
+ // Check if it's a "function does not exist" error
43
+ if (error.message.includes("function") &&
44
+ error.message.includes("does not exist")) {
45
+ console.log(`[API Keys Batch] Batch function not deployed, using individual calls...`);
46
+ }
47
+ else {
48
+ console.error(`[API Keys Batch] Database error:`, error.message);
49
+ }
50
+ console.log(`[API Keys Batch] Falling back to individual calls...`);
51
+ // Fallback to individual API key loading
52
+ const result = {};
53
+ const promises = device_ids.map(async (device_id) => {
54
+ try {
55
+ const api_keys = await server_list_api_keys(device_id);
56
+ result[parseInt(device_id)] = api_keys;
57
+ }
58
+ catch (err) {
59
+ console.warn(`[API Keys Batch] Failed for device ${device_id}:`, err);
60
+ result[parseInt(device_id)] = [];
61
+ }
62
+ });
63
+ await Promise.all(promises);
64
+ return result;
65
+ }
66
+ if (!data) {
67
+ return {};
46
68
  }
47
- result[device_id].push({
48
- id: item.api_key_id.toString(),
49
- key: item.api_key_key,
69
+ const result = {};
70
+ // Group API keys by device_id
71
+ data.forEach((item) => {
72
+ const device_id = item.device_id;
73
+ if (!result[device_id]) {
74
+ result[device_id] = [];
75
+ }
76
+ result[device_id].push({
77
+ id: item.api_key_id.toString(),
78
+ key: item.api_key_key,
79
+ });
50
80
  });
51
- });
52
- const totalDuration = Date.now() - startTime;
53
- console.log(`[API Keys Batch] COMPLETED in ${totalDuration}ms - returning API keys for ${Object.keys(result).length} devices`);
54
- return result;
81
+ return result;
82
+ }
83
+ catch (err) {
84
+ console.error(`[API Keys Batch] Unexpected error:`, err);
85
+ console.log(`[API Keys Batch] Falling back to individual calls...`);
86
+ // Fallback to individual API key loading
87
+ const result = {};
88
+ const promises = device_ids.map(async (device_id) => {
89
+ try {
90
+ const api_keys = await server_list_api_keys(device_id);
91
+ result[parseInt(device_id)] = api_keys;
92
+ }
93
+ catch (err) {
94
+ console.warn(`[API Keys Batch] Failed for device ${device_id}:`, err);
95
+ result[parseInt(device_id)] = [];
96
+ }
97
+ });
98
+ await Promise.all(promises);
99
+ return result;
100
+ }
55
101
  }
@@ -1,5 +1,6 @@
1
1
  import { IEventWithTags, 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[]>>;
@@ -3,6 +3,25 @@
3
3
  import { newServerClient } from "../supabase/server";
4
4
  import { EnumWebResponse, IWebResponse, } from "../types/requests";
5
5
  import { addSignedUrlsToEvents, addSignedUrlToEvent } from "./storage";
6
+ // Test function to verify individual event loading works
7
+ export async function test_event_loading(device_id) {
8
+ try {
9
+ console.log(`[Event Test] Testing individual event loading for device ${device_id}`);
10
+ const events_response = await server_get_events_and_tags_for_device(device_id, 1);
11
+ if (events_response.status === EnumWebResponse.SUCCESS) {
12
+ console.log(`[Event Test] Successfully loaded ${events_response.data?.length || 0} events for device ${device_id}`);
13
+ return true;
14
+ }
15
+ else {
16
+ console.error(`[Event Test] Failed to load events for device ${device_id}:`, events_response.msg);
17
+ return false;
18
+ }
19
+ }
20
+ catch (error) {
21
+ console.error(`[Event Test] Failed to load events for device ${device_id}:`, error);
22
+ return false;
23
+ }
24
+ }
6
25
  export async function server_create_tags(tags) {
7
26
  const supabase = await newServerClient();
8
27
  // remove id key from tags
@@ -148,30 +167,39 @@ export async function server_get_events_and_tags_for_device(device_id, limit = 3
148
167
  }
149
168
  export async function server_get_events_and_tags_for_devices_batch(device_ids, limit = 1) {
150
169
  const startTime = Date.now();
151
- console.log(`[Events Batch] Starting batch load for ${device_ids.length} devices (limit: ${limit}) at ${new Date().toISOString()}`);
152
- console.log(`[Events Batch] Debug: device_ids array:`, device_ids);
153
- console.log(`[Events Batch] Debug: device_ids types:`, device_ids.map((id) => ({ id, type: typeof id })));
154
170
  const supabase = await newServerClient();
155
- console.log(`[Events Batch] Making RPC call to get_events_and_tags_for_devices_batch...`);
156
171
  // Use single RPC call for all devices
157
172
  const { data, error } = await supabase.rpc("get_events_and_tags_for_devices_batch", {
158
173
  device_ids: device_ids,
159
174
  limit_per_device: limit,
160
175
  });
161
- const duration = Date.now() - startTime;
162
176
  if (error) {
163
- console.error(`[Events Batch] ERROR after ${duration}ms:`, error.message);
164
- return {
165
- status: EnumWebResponse.ERROR,
166
- msg: error.message,
167
- data: {},
168
- };
177
+ console.error(`[Events Batch] Database error:`, error.message);
178
+ console.log(`[Events Batch] Falling back to individual calls...`);
179
+ // Fallback to individual event loading
180
+ const result = {};
181
+ const promises = device_ids.map(async (device_id) => {
182
+ try {
183
+ const events_response = await server_get_events_and_tags_for_device(parseInt(device_id), limit);
184
+ if (events_response.status === EnumWebResponse.SUCCESS &&
185
+ events_response.data) {
186
+ result[parseInt(device_id)] = events_response.data;
187
+ }
188
+ else {
189
+ result[parseInt(device_id)] = [];
190
+ }
191
+ }
192
+ catch (err) {
193
+ console.warn(`[Events Batch] Failed for device ${device_id}:`, err);
194
+ result[parseInt(device_id)] = [];
195
+ }
196
+ });
197
+ await Promise.all(promises);
198
+ return IWebResponse.success(result).to_compatible();
169
199
  }
170
200
  if (!data) {
171
- console.log(`[Events Batch] No data returned after ${duration}ms`);
172
201
  return IWebResponse.success({}).to_compatible();
173
202
  }
174
- console.log(`[Events Batch] RPC completed in ${duration}ms, received ${data.length} event records`);
175
203
  // Group events by device_id
176
204
  const eventsByDevice = {};
177
205
  data.forEach((row) => {
@@ -179,7 +207,7 @@ export async function server_get_events_and_tags_for_devices_batch(device_ids, l
179
207
  if (!eventsByDevice[device_id]) {
180
208
  eventsByDevice[device_id] = [];
181
209
  }
182
- // Create event object from the new structure
210
+ // Create event object from the database function structure
183
211
  const event = {
184
212
  id: row.event_id,
185
213
  inserted_at: row.inserted_at,
@@ -206,8 +234,6 @@ export async function server_get_events_and_tags_for_devices_batch(device_ids, l
206
234
  const eventsWithSignedUrls = await addSignedUrlsToEvents(events, supabase);
207
235
  result[parseInt(device_id)] = eventsWithSignedUrls;
208
236
  }
209
- const totalDuration = Date.now() - startTime;
210
- console.log(`[Events Batch] COMPLETED in ${totalDuration}ms - returning events for ${Object.keys(result).length} devices`);
211
237
  return IWebResponse.success(result).to_compatible();
212
238
  }
213
239
  export async function get_event_and_tags_by_event_id(event_id) {
@@ -52,7 +52,7 @@ export class HerdModule {
52
52
  let response_new_devices = await get_devices_by_herd(herd.id, client);
53
53
  if (response_new_devices.status == EnumWebResponse.ERROR ||
54
54
  !response_new_devices.data) {
55
- console.warn("No devices found for herd");
55
+ console.warn(`[HerdModule] No devices found for herd ${herd.id}`);
56
56
  return new HerdModule(herd, [], [], Date.now());
57
57
  }
58
58
  const new_devices = response_new_devices.data;
@@ -61,60 +61,48 @@ export class HerdModule {
61
61
  if (new_devices.length > 0) {
62
62
  const batchStartTime = Date.now();
63
63
  try {
64
- console.log(`[HerdModule] Debug: new_devices array:`, new_devices.map((d) => ({
65
- id: d.id,
66
- type: typeof d.id,
67
- created_by: d.created_by,
68
- name: d.name,
69
- })));
70
64
  const device_ids = new_devices.map((device) => (device.id ?? 0).toString());
71
- console.log(`[HerdModule] Starting parallel batch load for ${device_ids.length} devices at ${new Date().toISOString()}:`, device_ids);
72
65
  // Load API keys and events in parallel
73
- console.log(`[HerdModule] Initiating Promise.all for API keys and events...`);
74
66
  const [api_keys_batch, events_response] = await Promise.all([
75
67
  server_list_api_keys_batch(device_ids),
76
68
  server_get_events_and_tags_for_devices_batch(device_ids, 1),
77
69
  ]);
78
- const batchDuration = Date.now() - batchStartTime;
79
- console.log(`[HerdModule] Parallel batch load completed in ${batchDuration}ms`);
80
70
  // Assign API keys to devices
81
71
  for (let i = 0; i < new_devices.length; i++) {
82
72
  const device_id = new_devices[i].id ?? 0;
83
73
  new_devices[i].api_keys_scout = api_keys_batch[device_id] || [];
84
74
  }
85
- console.log(`[HerdModule] API keys loaded for ${Object.keys(api_keys_batch).length} devices`);
86
75
  // Process events response
87
76
  if (events_response.status === EnumWebResponse.SUCCESS &&
88
77
  events_response.data) {
89
78
  recent_events_batch = events_response.data;
90
- console.log(`[HerdModule] Events loaded for ${Object.keys(recent_events_batch).length} devices`);
91
79
  }
92
80
  }
93
81
  catch (error) {
94
- console.error(`[HerdModule] CRITICAL ERROR in batch load after ${Date.now() - batchStartTime}ms:`, error);
82
+ console.error(`[HerdModule] Batch load error:`, error);
95
83
  // Continue without API keys and events
96
84
  }
97
85
  }
98
86
  // Run all remaining requests in parallel with individual error handling
99
87
  const [res_zones, res_user_roles, total_event_count, res_plans, res_sessions,] = await Promise.allSettled([
100
88
  server_get_more_zones_and_actions_for_herd(herd.id, 0, 10).catch((error) => {
101
- console.warn("Failed to get zones and actions:", error);
89
+ console.warn(`[HerdModule] Failed to get zones and actions:`, error);
102
90
  return { status: EnumWebResponse.ERROR, data: null };
103
91
  }),
104
92
  server_get_users_with_herd_access(herd.id).catch((error) => {
105
- console.warn("Failed to get user roles:", error);
93
+ console.warn(`[HerdModule] Failed to get user roles:`, error);
106
94
  return { status: EnumWebResponse.ERROR, data: null };
107
95
  }),
108
96
  server_get_total_events_by_herd(herd.id, EnumSessionsVisibility.Exclude).catch((error) => {
109
- console.warn("Failed to get total events count:", error);
97
+ console.warn(`[HerdModule] Failed to get total events count:`, error);
110
98
  return { status: EnumWebResponse.ERROR, data: null };
111
99
  }),
112
100
  server_get_plans_by_herd(herd.id).catch((error) => {
113
- console.warn("Failed to get plans:", error);
101
+ console.warn(`[HerdModule] Failed to get plans:`, error);
114
102
  return { status: EnumWebResponse.ERROR, data: null };
115
103
  }),
116
104
  getSessionsByHerdId(client, herd.id).catch((error) => {
117
- console.warn("Failed to get sessions:", error);
105
+ console.warn(`[HerdModule] Failed to get sessions:`, error);
118
106
  return [];
119
107
  }),
120
108
  ]);
@@ -151,7 +139,7 @@ export class HerdModule {
151
139
  return new HerdModule(herd, new_devices, [], Date.now(), user_roles, 0, total_events, total_events, newLabels, plans, zones, sessions);
152
140
  }
153
141
  catch (error) {
154
- console.error("Critical error in HerdModule.from_herd:", error);
142
+ console.error(`[HerdModule] Critical error in HerdModule.from_herd:`, error);
155
143
  // Return a minimal but valid HerdModule instance to prevent complete failure
156
144
  return new HerdModule(herd, [], [], Date.now());
157
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.46",
3
+ "version": "1.0.48",
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",