@adventurelabs/scout-core 1.5.4 → 2.0.0

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.
Files changed (219) hide show
  1. package/README.md +47 -0
  2. package/dist/api_keys/actions.d.ts +0 -1
  3. package/dist/api_keys/actions.js +0 -14
  4. package/dist/client/index.d.ts +75 -0
  5. package/dist/client/index.js +82 -0
  6. package/dist/constants/index.d.ts +1 -0
  7. package/dist/constants/index.js +1 -0
  8. package/dist/helpers/analysis_usage/jobs_server.d.ts +25 -0
  9. package/dist/helpers/analysis_usage/jobs_server.js +10 -0
  10. package/dist/helpers/analysis_usage/tasks_server.d.ts +63 -0
  11. package/dist/helpers/analysis_usage/tasks_server.js +14 -0
  12. package/dist/helpers/artifacts.d.ts +0 -1
  13. package/dist/helpers/artifacts.js +10 -147
  14. package/dist/helpers/artifacts.queries.d.ts +26 -0
  15. package/dist/helpers/{artifacts.internal.js → artifacts.queries.js} +24 -41
  16. package/dist/helpers/auth.d.ts +1 -1
  17. package/dist/helpers/auth.js +2 -3
  18. package/dist/helpers/auth_server.d.ts +2 -0
  19. package/dist/helpers/auth_server.js +10 -0
  20. package/dist/helpers/certificates_server.d.ts +101 -0
  21. package/dist/helpers/certificates_server.js +42 -0
  22. package/dist/helpers/chat.d.ts +1 -2
  23. package/dist/helpers/chat.js +3 -38
  24. package/dist/helpers/chat.queries.d.ts +13 -0
  25. package/dist/helpers/chat.queries.js +39 -0
  26. package/dist/helpers/client_abilities_jwt_keys.js +0 -1
  27. package/dist/helpers/client_abilities_token_server.d.ts +1 -0
  28. package/dist/helpers/client_abilities_token_server.js +5 -2
  29. package/dist/helpers/connectivity.d.ts +1 -5
  30. package/dist/helpers/connectivity.js +7 -140
  31. package/dist/helpers/connectivity.queries.d.ts +18 -0
  32. package/dist/helpers/connectivity.queries.js +121 -0
  33. package/dist/helpers/contacts_server.d.ts +63 -0
  34. package/dist/helpers/contacts_server.js +18 -0
  35. package/dist/helpers/deviceTransfer.js +2 -11
  36. package/dist/helpers/deviceTransfer.queries.d.ts +4 -0
  37. package/dist/helpers/deviceTransfer.queries.js +11 -0
  38. package/dist/helpers/devices.js +4 -61
  39. package/dist/helpers/devices.queries.d.ts +4 -1
  40. package/dist/helpers/devices.queries.js +52 -0
  41. package/dist/helpers/document_templates.js +6 -52
  42. package/dist/helpers/document_templates.queries.d.ts +9 -0
  43. package/dist/helpers/document_templates.queries.js +56 -0
  44. package/dist/helpers/documents_server.d.ts +226 -0
  45. package/dist/helpers/documents_server.js +38 -0
  46. package/dist/helpers/email.d.ts +0 -6
  47. package/dist/helpers/email.js +1 -19
  48. package/dist/helpers/embeddings.d.ts +0 -5
  49. package/dist/helpers/embeddings.js +2 -19
  50. package/dist/helpers/embeddings.queries.d.ts +7 -0
  51. package/dist/helpers/{embeddings.internal.js → embeddings.queries.js} +2 -2
  52. package/dist/helpers/eventUtils.d.ts +3 -12
  53. package/dist/helpers/eventUtils.js +0 -10
  54. package/dist/helpers/events.d.ts +0 -1
  55. package/dist/helpers/events.js +7 -107
  56. package/dist/helpers/events.queries.d.ts +17 -0
  57. package/dist/helpers/{events.internal.js → events.queries.js} +24 -15
  58. package/dist/helpers/health_metrics.js +3 -38
  59. package/dist/helpers/health_metrics.queries.d.ts +12 -0
  60. package/dist/helpers/health_metrics.queries.js +39 -0
  61. package/dist/helpers/heartbeats.js +4 -83
  62. package/dist/helpers/heartbeats.queries.d.ts +11 -0
  63. package/dist/helpers/heartbeats.queries.js +76 -0
  64. package/dist/helpers/herd_allowed_domains.js +2 -12
  65. package/dist/helpers/{herd_allowed_domains_internal.d.ts → herd_allowed_domains.queries.d.ts} +1 -1
  66. package/dist/helpers/{herd_allowed_domains_internal.js → herd_allowed_domains.queries.js} +2 -2
  67. package/dist/helpers/herd_credentials_server.d.ts +76 -0
  68. package/dist/helpers/herd_credentials_server.js +22 -0
  69. package/dist/helpers/herd_modules.queries.js +10 -7
  70. package/dist/helpers/herd_roles.js +13 -148
  71. package/dist/helpers/herd_roles.queries.d.ts +18 -0
  72. package/dist/helpers/{herd_roles_internal.js → herd_roles.queries.js} +28 -28
  73. package/dist/helpers/herds.js +5 -47
  74. package/dist/helpers/herds.queries.d.ts +8 -1
  75. package/dist/helpers/herds.queries.js +48 -0
  76. package/dist/helpers/invitations.js +7 -58
  77. package/dist/helpers/invitations.queries.d.ts +10 -0
  78. package/dist/helpers/{invitations_internal.js → invitations.queries.js} +12 -12
  79. package/dist/helpers/issuers.js +6 -52
  80. package/dist/helpers/issuers.queries.d.ts +9 -0
  81. package/dist/helpers/issuers.queries.js +53 -0
  82. package/dist/helpers/layers.js +2 -20
  83. package/dist/helpers/layers.queries.d.ts +2 -1
  84. package/dist/helpers/layers.queries.js +9 -13
  85. package/dist/helpers/lifecycle_server.d.ts +175 -0
  86. package/dist/helpers/lifecycle_server.js +38 -0
  87. package/dist/helpers/localizations.js +5 -42
  88. package/dist/helpers/localizations.queries.d.ts +8 -0
  89. package/dist/helpers/localizations.queries.js +45 -0
  90. package/dist/helpers/maintenance_requests_server.d.ts +124 -0
  91. package/dist/helpers/maintenance_requests_server.js +34 -0
  92. package/dist/helpers/manufacturers.js +5 -43
  93. package/dist/helpers/manufacturers.queries.d.ts +10 -0
  94. package/dist/helpers/manufacturers.queries.js +46 -0
  95. package/dist/helpers/media_urls.d.ts +13 -0
  96. package/dist/helpers/media_urls.js +21 -0
  97. package/dist/helpers/models.js +8 -80
  98. package/dist/helpers/models.queries.d.ts +13 -0
  99. package/dist/helpers/models.queries.js +79 -0
  100. package/dist/helpers/observations_server.d.ts +51 -0
  101. package/dist/helpers/observations_server.js +22 -0
  102. package/dist/helpers/operating_contexts.js +39 -154
  103. package/dist/helpers/operating_contexts.queries.d.ts +48 -0
  104. package/dist/helpers/operating_contexts.queries.js +223 -0
  105. package/dist/helpers/operators.js +4 -57
  106. package/dist/helpers/operators.queries.d.ts +7 -0
  107. package/dist/helpers/operators.queries.js +55 -0
  108. package/dist/helpers/parts.d.ts +0 -87
  109. package/dist/helpers/parts.js +4 -90
  110. package/dist/helpers/parts_server.d.ts +260 -0
  111. package/dist/helpers/parts_server.js +71 -0
  112. package/dist/helpers/pins_server.d.ts +118 -0
  113. package/dist/helpers/pins_server.js +38 -0
  114. package/dist/helpers/plans.js +4 -56
  115. package/dist/helpers/plans.queries.d.ts +4 -1
  116. package/dist/helpers/plans.queries.js +39 -12
  117. package/dist/helpers/points_for_user_server.d.ts +9 -0
  118. package/dist/helpers/points_for_user_server.js +14 -0
  119. package/dist/helpers/product_compatibilities.js +6 -78
  120. package/dist/helpers/product_compatibilities.queries.d.ts +9 -0
  121. package/dist/helpers/product_compatibilities.queries.js +83 -0
  122. package/dist/helpers/product_numbers.js +6 -55
  123. package/dist/helpers/product_numbers.queries.d.ts +9 -0
  124. package/dist/helpers/product_numbers.queries.js +58 -0
  125. package/dist/helpers/products.js +6 -54
  126. package/dist/helpers/products.queries.d.ts +11 -0
  127. package/dist/helpers/products.queries.js +58 -0
  128. package/dist/helpers/providers.js +5 -71
  129. package/dist/helpers/providers.queries.d.ts +5 -1
  130. package/dist/helpers/providers.queries.js +52 -11
  131. package/dist/helpers/pubsub_token_server.d.ts +1 -0
  132. package/dist/helpers/pubsub_token_server.js +5 -2
  133. package/dist/helpers/segmentations_sam3.js +5 -78
  134. package/dist/helpers/segmentations_sam3.queries.d.ts +16 -0
  135. package/dist/helpers/segmentations_sam3.queries.js +81 -0
  136. package/dist/helpers/sensor_poses_server.d.ts +98 -0
  137. package/dist/helpers/sensor_poses_server.js +26 -0
  138. package/dist/helpers/session_incidents_server.d.ts +106 -0
  139. package/dist/helpers/session_incidents_server.js +22 -0
  140. package/dist/helpers/session_summaries.d.ts +2 -0
  141. package/dist/helpers/session_summaries.js +7 -23
  142. package/dist/helpers/session_summaries.queries.d.ts +6 -3
  143. package/dist/helpers/session_summaries.queries.js +27 -0
  144. package/dist/helpers/sessions.js +8 -122
  145. package/dist/helpers/sessions.queries.d.ts +14 -2
  146. package/dist/helpers/sessions.queries.js +88 -1
  147. package/dist/helpers/storage.js +0 -1
  148. package/dist/helpers/supabase_auth.d.ts +6 -0
  149. package/dist/helpers/supabase_auth.js +37 -0
  150. package/dist/helpers/tags.d.ts +0 -1
  151. package/dist/helpers/tags.js +13 -559
  152. package/dist/helpers/tags.queries.d.ts +33 -0
  153. package/dist/helpers/tags.queries.js +454 -0
  154. package/dist/helpers/user_credentials_server.d.ts +76 -0
  155. package/dist/helpers/user_credentials_server.js +22 -0
  156. package/dist/helpers/users.d.ts +2 -2
  157. package/dist/helpers/users.js +8 -107
  158. package/dist/helpers/users.queries.d.ts +9 -1
  159. package/dist/helpers/users.queries.js +107 -0
  160. package/dist/helpers/versions_software.d.ts +3 -1
  161. package/dist/helpers/versions_software.js +24 -0
  162. package/dist/helpers/versions_software_server.d.ts +83 -4
  163. package/dist/helpers/versions_software_server.js +23 -50
  164. package/dist/helpers/zones.d.ts +1 -16
  165. package/dist/helpers/zones.js +4 -104
  166. package/dist/helpers/zones.queries.d.ts +6 -0
  167. package/dist/helpers/zones.queries.js +78 -0
  168. package/dist/hooks/useAuthorizedRealtimeTopics.js +8 -2
  169. package/dist/hooks/useInfiniteQuery.js +18 -14
  170. package/dist/hooks/useScoutRealtimeBroadcast.js +8 -6
  171. package/dist/hooks/useScoutRealtimeDocuments.js +3 -2
  172. package/dist/hooks/useScoutRefresh.d.ts +9 -25
  173. package/dist/hooks/useScoutRefresh.js +33 -213
  174. package/dist/providers/ScoutRefreshProvider.d.ts +1 -1
  175. package/dist/providers/ScoutRefreshProvider.js +56 -13
  176. package/dist/server/index.d.ts +77 -0
  177. package/dist/server/index.js +79 -0
  178. package/dist/server/middleware.d.ts +2 -0
  179. package/dist/server/middleware.js +2 -0
  180. package/dist/server/supabase.d.ts +2 -0
  181. package/dist/server/supabase.js +2 -0
  182. package/dist/store/api.d.ts +40 -40
  183. package/dist/store/api.js +38 -109
  184. package/dist/store/configureStore.d.ts +0 -55
  185. package/dist/store/configureStore.js +1 -8
  186. package/dist/store/hooks.js +1 -0
  187. package/dist/supabase/client.d.ts +3 -0
  188. package/dist/supabase/client.js +10 -0
  189. package/dist/supabase/middleware.d.ts +1 -0
  190. package/dist/supabase/middleware.js +6 -20
  191. package/dist/supabase/server.d.ts +7 -2
  192. package/dist/supabase/server.js +13 -1
  193. package/dist/types/db.d.ts +2 -2
  194. package/dist/types/index.d.ts +5 -0
  195. package/dist/types/index.js +5 -0
  196. package/package.json +53 -4
  197. package/dist/api_keys/index.d.ts +0 -1
  198. package/dist/api_keys/index.js +0 -1
  199. package/dist/helpers/artifacts.internal.d.ts +0 -22
  200. package/dist/helpers/client_abilities_jwt_keys_internal.d.ts +0 -9
  201. package/dist/helpers/client_abilities_jwt_keys_internal.js +0 -20
  202. package/dist/helpers/connectivity.internal.d.ts +0 -10
  203. package/dist/helpers/connectivity.internal.js +0 -47
  204. package/dist/helpers/credentials.d.ts +0 -9
  205. package/dist/helpers/credentials.js +0 -92
  206. package/dist/helpers/embeddings.internal.d.ts +0 -5
  207. package/dist/helpers/events.internal.d.ts +0 -13
  208. package/dist/helpers/herd_roles_internal.d.ts +0 -18
  209. package/dist/helpers/index.d.ts +0 -56
  210. package/dist/helpers/index.js +0 -56
  211. package/dist/helpers/invitations_internal.d.ts +0 -10
  212. package/dist/helpers/versions_software_internal.d.ts +0 -6
  213. package/dist/helpers/versions_software_internal.js +0 -53
  214. package/dist/hooks/useDeviceDisplayName.d.ts +0 -8
  215. package/dist/hooks/useDeviceDisplayName.js +0 -49
  216. package/dist/index.d.ts +0 -108
  217. package/dist/index.js +0 -114
  218. package/dist/supabase/index.d.ts +0 -2
  219. package/dist/supabase/index.js +0 -2
@@ -1,589 +1,43 @@
1
- "use server";
2
- // add tag to db
3
1
  import { newServerClient } from "../supabase/server";
4
- import { EnumWebResponse, IWebResponse, } from "../types/requests";
5
- import { generateSignedUrlsBatchWithClient, generateSignedUrlWithClient, } from "./storage_internal";
6
- import { isNonEmptyStorageFilePath } from "./storagePath";
7
- // Helper functions to extract coordinates from location field
8
- function extractLatitude(location) {
9
- try {
10
- if (typeof location === "object" && location !== null) {
11
- if ("coordinates" in location && Array.isArray(location.coordinates)) {
12
- return location.coordinates[1]; // latitude is second coordinate
13
- }
14
- if ("y" in location) {
15
- return location.y;
16
- }
17
- }
18
- if (typeof location === "string") {
19
- const match = location.match(/Point\(([^)]+)\)/);
20
- if (match) {
21
- const coords = match[1].split(" ").map(Number);
22
- if (coords.length === 2) {
23
- return coords[1]; // latitude is second coordinate
24
- }
25
- }
26
- }
27
- }
28
- catch (error) {
29
- console.warn("Error extracting latitude:", error);
30
- }
31
- return null;
32
- }
33
- function extractLongitude(location) {
34
- try {
35
- if (typeof location === "object" && location !== null) {
36
- if ("coordinates" in location && Array.isArray(location.coordinates)) {
37
- return location.coordinates[0]; // longitude is first coordinate
38
- }
39
- if ("x" in location) {
40
- return location.x;
41
- }
42
- }
43
- if (typeof location === "string") {
44
- const match = location.match(/Point\(([^)]+)\)/);
45
- if (match) {
46
- const coords = match[1].split(" ").map(Number);
47
- if (coords.length === 2) {
48
- return coords[0]; // longitude is first coordinate
49
- }
50
- }
51
- }
52
- }
53
- catch (error) {
54
- console.warn("Error extracting longitude:", error);
55
- }
56
- return null;
57
- }
58
- function rpcModelIdsParam(model_ids) {
59
- if (model_ids != null && model_ids.length > 0) {
60
- return { model_ids_caller: model_ids };
61
- }
62
- return {};
63
- }
64
- // Test function to verify individual event loading works
65
- export async function test_event_loading(device_id) {
66
- try {
67
- console.log(`[Event Test] Testing individual event loading for device ${device_id}`);
68
- const events_response = await server_get_events_and_tags_for_device(device_id, 1);
69
- if (events_response.status === EnumWebResponse.SUCCESS) {
70
- console.log(`[Event Test] Successfully loaded ${events_response.data?.length || 0} events for device ${device_id}`);
71
- return true;
72
- }
73
- else {
74
- console.error(`[Event Test] Failed to load events for device ${device_id}:`, events_response.msg);
75
- return false;
76
- }
77
- }
78
- catch (error) {
79
- console.error(`[Event Test] Failed to load events for device ${device_id}:`, error);
80
- return false;
81
- }
82
- }
2
+ import { create_tags_query, delete_tags_by_ids_query, get_event_and_tags_by_event_id_query, get_events_and_tags_for_device_query, get_events_and_tags_for_devices_batch_query, get_minimal_subjects_for_artifact_query, get_more_events_with_tags_by_herd_query, get_tags_for_artifact_by_frame_index_query, get_tags_for_artifact_by_subject_id_query, get_tags_for_artifact_query, get_tags_for_event_query, update_tags_query, } from "./tags.queries";
83
3
  export async function server_create_tags(tags) {
84
- const supabase = await newServerClient();
85
- // remove id key from tags
86
- const formatted_tags = tags.map((tag) => {
87
- const { id, ...rest } = tag;
88
- return {
89
- ...rest,
90
- observation_type: rest.observation_type,
91
- };
92
- });
93
- const { data, error } = await supabase
94
- .from("tags")
95
- .insert(formatted_tags)
96
- .select("*");
97
- if (error) {
98
- return {
99
- status: EnumWebResponse.ERROR,
100
- msg: error.message,
101
- data: null,
102
- };
103
- }
104
- else {
105
- return IWebResponse.success(data).to_compatible();
106
- }
4
+ return create_tags_query(await newServerClient(), tags);
107
5
  }
108
6
  export async function server_delete_tags_by_ids(tag_ids) {
109
- const supabase = await newServerClient();
110
- const { error } = await supabase.from("tags").delete().in("id", tag_ids);
111
- if (error) {
112
- return {
113
- status: EnumWebResponse.ERROR,
114
- msg: error.message,
115
- data: false,
116
- };
117
- }
118
- else {
119
- return IWebResponse.success(true).to_compatible();
120
- }
7
+ return delete_tags_by_ids_query(await newServerClient(), tag_ids);
121
8
  }
122
9
  export async function server_update_tags(tags) {
123
- const supabase = await newServerClient();
124
- // Update each tag individually since we need to preserve the id
125
- const updatedTags = [];
126
- for (const tag of tags) {
127
- const { id, inserted_at: _ignoredInsertedAt, ...updateData } = tag;
128
- const { data, error } = await supabase
129
- .from("tags")
130
- .update({
131
- ...updateData,
132
- observation_type: updateData.observation_type,
133
- })
134
- .eq("id", id)
135
- .select("*")
136
- .single();
137
- if (error) {
138
- return {
139
- status: EnumWebResponse.ERROR,
140
- msg: error.message,
141
- data: null,
142
- };
143
- }
144
- if (data) {
145
- updatedTags.push(data);
146
- }
147
- }
148
- return IWebResponse.success(updatedTags).to_compatible();
10
+ return update_tags_query(await newServerClient(), tags);
149
11
  }
150
12
  export async function server_get_more_events_with_tags_by_herd(herd_id, offset, page_count = 10) {
151
- const from = offset * page_count;
152
- const supabase = await newServerClient();
153
- // make rpc call to get_events_and_tags_for_herd(herd_id, offset, limit)
154
- const { data, error } = await supabase.rpc("get_events_and_tags_for_herd", {
155
- herd_id_caller: herd_id,
156
- offset_caller: from,
157
- limit_caller: page_count,
158
- });
159
- if (error) {
160
- console.warn("Error fetching events with tags by herd:", error.message);
161
- return {
162
- status: EnumWebResponse.ERROR,
163
- msg: error.message,
164
- data: [],
165
- };
166
- }
167
- // iterate through data if tags contains null, remove it
168
- const filtered_data = (data || []).map((event) => {
169
- if (!event.tags)
170
- return event;
171
- event.tags = event.tags.filter((tag) => tag !== null);
172
- return event;
173
- });
174
- // Generate signed URLs for events
175
- const filePaths = filtered_data
176
- .map((event) => event.file_path)
177
- .filter(isNonEmptyStorageFilePath);
178
- let eventsWithSignedUrls = filtered_data;
179
- if (filePaths.length > 0) {
180
- const signedUrls = await generateSignedUrlsBatchWithClient(supabase, filePaths, undefined);
181
- const urlMap = new Map();
182
- filePaths.forEach((path, index) => {
183
- urlMap.set(path, signedUrls[index]);
184
- });
185
- eventsWithSignedUrls = filtered_data.map((event) => ({
186
- ...event,
187
- media_url: isNonEmptyStorageFilePath(event.file_path)
188
- ? urlMap.get(event.file_path) ?? null
189
- : null,
190
- }));
191
- }
192
- return IWebResponse.success(eventsWithSignedUrls).to_compatible();
13
+ return get_more_events_with_tags_by_herd_query(await newServerClient(), herd_id, offset, page_count);
193
14
  }
194
15
  export async function server_get_events_and_tags_for_device(device_id, limit = 3) {
195
- const supabase = await newServerClient();
196
- // make rpc call to get_events_and_tags_for_device(device_id, limit)
197
- const { data, error } = await supabase.rpc("get_events_and_tags_for_device", {
198
- device_id_caller: device_id,
199
- limit_caller: limit,
200
- });
201
- if (error) {
202
- console.warn("Error fetching recent events with tags by device:", error.message);
203
- return {
204
- status: EnumWebResponse.ERROR,
205
- msg: error.message,
206
- data: [],
207
- };
208
- }
209
- // Generate signed URLs for events
210
- const eventData = data || [];
211
- const filePaths = eventData
212
- .map((event) => event.file_path)
213
- .filter(isNonEmptyStorageFilePath);
214
- let eventsWithSignedUrls = eventData;
215
- if (filePaths.length > 0) {
216
- const signedUrls = await generateSignedUrlsBatchWithClient(supabase, filePaths, undefined);
217
- const urlMap = new Map();
218
- filePaths.forEach((path, index) => {
219
- urlMap.set(path, signedUrls[index]);
220
- });
221
- eventsWithSignedUrls = eventData.map((event) => ({
222
- ...event,
223
- media_url: isNonEmptyStorageFilePath(event.file_path)
224
- ? urlMap.get(event.file_path) ?? null
225
- : null,
226
- }));
227
- }
228
- return IWebResponse.success(eventsWithSignedUrls).to_compatible();
16
+ return get_events_and_tags_for_device_query(await newServerClient(), device_id, limit);
229
17
  }
230
18
  export async function server_get_events_and_tags_for_devices_batch(device_ids, limit = 1) {
231
- const supabase = await newServerClient();
232
- // Use single RPC call for all devices
233
- const { data, error } = await supabase.rpc("get_events_and_tags_for_devices_batch", {
234
- device_ids: device_ids,
235
- limit_per_device: limit,
236
- });
237
- if (error) {
238
- console.error(`[Events Batch] Database error:`, error.message);
239
- console.log(`[Events Batch] Falling back to individual calls...`);
240
- // Fallback to individual event loading
241
- const result = {};
242
- const promises = device_ids.map(async (device_id) => {
243
- try {
244
- const events_response = await server_get_events_and_tags_for_device(device_id, limit);
245
- if (events_response.status === EnumWebResponse.SUCCESS &&
246
- events_response.data) {
247
- result[device_id] = events_response.data;
248
- }
249
- else {
250
- result[device_id] = [];
251
- }
252
- }
253
- catch (err) {
254
- console.warn(`[Events Batch] Failed for device ${device_id}:`, err);
255
- result[device_id] = [];
256
- }
257
- });
258
- await Promise.all(promises);
259
- return IWebResponse.success(result).to_compatible();
260
- }
261
- if (!data) {
262
- return IWebResponse.success({}).to_compatible();
263
- }
264
- // Group events by device_id
265
- const eventsByDevice = {};
266
- data.forEach((row) => {
267
- const device_id = row.device_id;
268
- if (!eventsByDevice[device_id]) {
269
- eventsByDevice[device_id] = [];
270
- }
271
- // Create event object from the event_and_tags_pretty_location structure
272
- const event = {
273
- id: row.id, // Changed from row.event_id to row.id
274
- inserted_at: row.inserted_at,
275
- message: row.message,
276
- media_url: row.media_url,
277
- file_path: row.file_path,
278
- latitude: row.latitude,
279
- longitude: row.longitude,
280
- altitude: row.altitude,
281
- heading: row.heading,
282
- media_type: row.media_type,
283
- device_id: device_id,
284
- timestamp_observation: row.timestamp_observation,
285
- is_public: row.is_public,
286
- earthranger_url: row.earthranger_url,
287
- herd_id: row.herd_id,
288
- tags: Array.isArray(row.tags) ? row.tags : [],
289
- embedded_at: row.embedded_at ?? null,
290
- segmented_at: row.segmented_at ?? null,
291
- tagged_at: row.tagged_at ?? null,
292
- };
293
- eventsByDevice[device_id].push(event);
294
- });
295
- // Add signed URLs to all events
296
- const result = {};
297
- // Get all file paths for batch URL generation
298
- const allFilePaths = [];
299
- for (const device_id in eventsByDevice) {
300
- const events = eventsByDevice[device_id];
301
- events.forEach((event) => {
302
- if (isNonEmptyStorageFilePath(event.file_path) &&
303
- !allFilePaths.includes(event.file_path)) {
304
- allFilePaths.push(event.file_path);
305
- }
306
- });
307
- }
308
- // Generate all signed URLs at once
309
- const signedUrls = allFilePaths.length > 0
310
- ? await generateSignedUrlsBatchWithClient(supabase, allFilePaths, undefined)
311
- : [];
312
- const urlMap = new Map();
313
- allFilePaths.forEach((path, index) => {
314
- urlMap.set(path, signedUrls[index]);
315
- });
316
- // Apply signed URLs to events
317
- for (const device_id in eventsByDevice) {
318
- const events = eventsByDevice[device_id];
319
- const eventsWithSignedUrls = events.map((event) => ({
320
- ...event,
321
- media_url: isNonEmptyStorageFilePath(event.file_path)
322
- ? urlMap.get(event.file_path) ?? null
323
- : null,
324
- }));
325
- result[parseInt(device_id)] = eventsWithSignedUrls;
326
- }
327
- return IWebResponse.success(result).to_compatible();
19
+ return get_events_and_tags_for_devices_batch_query(await newServerClient(), device_ids, limit);
328
20
  }
329
21
  export async function get_event_and_tags_by_event_id(event_id) {
330
- const supabase = await newServerClient();
331
- // Get event and tags
332
- const { data, error } = await supabase
333
- .from("events")
334
- .select(`
335
- *,
336
- tags: tags (*)
337
- `)
338
- .eq("id", event_id);
339
- if (error) {
340
- console.warn("Error fetching event with tags by event id:", error.message);
341
- return {
342
- status: EnumWebResponse.ERROR,
343
- msg: error.message,
344
- data: null,
345
- };
346
- }
347
- if (!data || data.length === 0) {
348
- return {
349
- status: EnumWebResponse.ERROR,
350
- msg: "Event not found",
351
- data: null,
352
- };
353
- }
354
- // Get herd_id from device
355
- const { data: deviceData, error: deviceError } = await supabase
356
- .from("devices")
357
- .select("herd_id")
358
- .eq("id", data[0].device_id)
359
- .single();
360
- if (deviceError) {
361
- console.warn("Error fetching device herd_id:", deviceError.message);
362
- return {
363
- status: EnumWebResponse.ERROR,
364
- msg: deviceError.message,
365
- data: null,
366
- };
367
- }
368
- // Transform location to latitude/longitude with better error handling
369
- let latitude = null;
370
- let longitude = null;
371
- try {
372
- if (data[0].location) {
373
- if (typeof data[0].location === "object" && data[0].location !== null) {
374
- // Handle PostGIS Point format: { coordinates: [lon, lat] }
375
- if ("coordinates" in data[0].location &&
376
- Array.isArray(data[0].location.coordinates)) {
377
- longitude = data[0].location.coordinates[0];
378
- latitude = data[0].location.coordinates[1];
379
- }
380
- // Handle alternative format: { x: lon, y: lat }
381
- else if ("x" in data[0].location && "y" in data[0].location) {
382
- longitude = data[0].location.x;
383
- latitude = data[0].location.y;
384
- }
385
- }
386
- // Handle string format: "Point(lon lat)"
387
- else if (typeof data[0].location === "string") {
388
- const match = data[0].location.match(/Point\(([^)]+)\)/);
389
- if (match) {
390
- const coords = match[1].split(" ").map(Number);
391
- if (coords.length === 2) {
392
- longitude = coords[0];
393
- latitude = coords[1];
394
- }
395
- }
396
- }
397
- }
398
- }
399
- catch (locationError) {
400
- console.warn("Error parsing location data:", locationError);
401
- // Continue with null coordinates
402
- }
403
- const transformedData = {
404
- id: data[0].id,
405
- inserted_at: data[0].inserted_at,
406
- message: data[0].message,
407
- media_url: data[0].media_url,
408
- latitude: latitude,
409
- longitude: longitude,
410
- altitude: data[0].altitude,
411
- heading: data[0].heading,
412
- media_type: data[0].media_type,
413
- device_id: data[0].device_id,
414
- timestamp_observation: data[0].timestamp_observation,
415
- is_public: data[0].is_public,
416
- herd_id: deviceData.herd_id,
417
- tags: (data[0].tags || []).map((tag) => {
418
- const originLoc = tag.origin_location;
419
- const subjectLoc = tag.subject_location;
420
- const originLat = originLoc ? extractLatitude(originLoc) : null;
421
- const originLon = originLoc ? extractLongitude(originLoc) : null;
422
- const subjectLat = subjectLoc ? extractLatitude(subjectLoc) : null;
423
- const subjectLon = subjectLoc ? extractLongitude(subjectLoc) : null;
424
- return {
425
- id: tag.id,
426
- inserted_at: tag.inserted_at,
427
- artifact_id: tag.artifact_id,
428
- event_id: tag.event_id,
429
- model_id: tag.model_id,
430
- width: tag.width,
431
- height: tag.height,
432
- conf: tag.conf,
433
- x: tag.x,
434
- y: tag.y,
435
- class: tag.class,
436
- timestamp_observation: tag.timestamp_observation,
437
- frame_index: tag.frame_index ?? null,
438
- origin_location: tag.origin_location ?? null,
439
- origin_pitch: tag.origin_pitch,
440
- origin_heading: tag.origin_heading,
441
- origin_roll: tag.origin_roll,
442
- sensor_pitch: tag.sensor_pitch,
443
- sensor_yaw: tag.sensor_yaw,
444
- sensor_roll: tag.sensor_roll,
445
- origin_height: tag.origin_height,
446
- subject_location: tag.subject_location ?? null,
447
- subject_height: tag.subject_height,
448
- origin_latitude: originLat,
449
- origin_longitude: originLon,
450
- subject_latitude: subjectLat,
451
- subject_longitude: subjectLon,
452
- subject_id: tag.subject_id ?? null,
453
- };
454
- }),
455
- earthranger_url: data[0].earthranger_url,
456
- file_path: data[0].file_path,
457
- embedded_at: data[0].embedded_at ?? null,
458
- segmented_at: data[0].segmented_at ?? null,
459
- tagged_at: data[0].tagged_at ?? null,
460
- };
461
- // Generate signed URL for event
462
- let eventWithSignedUrl = transformedData;
463
- if (isNonEmptyStorageFilePath(transformedData.file_path)) {
464
- const signedUrl = await generateSignedUrlWithClient(supabase, transformedData.file_path, undefined);
465
- eventWithSignedUrl = {
466
- ...transformedData,
467
- media_url: signedUrl,
468
- };
469
- }
470
- return IWebResponse.success(eventWithSignedUrl).to_compatible();
22
+ return get_event_and_tags_by_event_id_query(await newServerClient(), event_id);
471
23
  }
472
24
  /** Tags for an artifact with optional timestamp range, model_ids filter, and max_elements (omit or null for no limit). */
473
25
  export async function server_get_tags_for_artifact(artifactId, options) {
474
- const supabase = await newServerClient();
475
- const { data, error } = await supabase.rpc("get_tags_for_artifact", {
476
- artifact_id_caller: artifactId,
477
- ...(options?.start_timestamp != null && {
478
- start_timestamp_caller: options.start_timestamp,
479
- }),
480
- ...(options?.end_timestamp != null && {
481
- end_timestamp_caller: options.end_timestamp,
482
- }),
483
- ...rpcModelIdsParam(options?.model_ids),
484
- ...(options?.max_elements != null && {
485
- max_elements_caller: options.max_elements,
486
- }),
487
- });
488
- if (error) {
489
- console.warn("Error fetching tags for artifact:", error.message);
490
- return {
491
- status: EnumWebResponse.ERROR,
492
- msg: error.message,
493
- data: [],
494
- };
495
- }
496
- return IWebResponse.success(data ?? []).to_compatible();
26
+ return get_tags_for_artifact_query(await newServerClient(), artifactId, options);
497
27
  }
498
28
  /** Tags for an artifact filtered by frame_index range and minimum confidence. Omit maxElements (or pass null) for no row limit. */
499
29
  export async function server_get_tags_for_artifact_by_frame_index(artifactId, minConf, frameIndexStart, frameIndexEnd, model_ids = null, maxElements = null) {
500
- const supabase = await newServerClient();
501
- const { data, error } = await supabase.rpc("get_tags_for_artifact_by_frame_index", {
502
- artifact_id_caller: artifactId,
503
- min_conf_caller: minConf,
504
- frame_index_start_caller: frameIndexStart,
505
- frame_index_end_caller: frameIndexEnd,
506
- ...rpcModelIdsParam(model_ids),
507
- ...(maxElements != null && { max_elements_caller: maxElements }),
508
- });
509
- if (error) {
510
- console.warn("Error fetching tags for artifact by frame index:", error.message);
511
- return {
512
- status: EnumWebResponse.ERROR,
513
- msg: error.message,
514
- data: [],
515
- };
516
- }
517
- return IWebResponse.success(data ?? []).to_compatible();
30
+ return get_tags_for_artifact_by_frame_index_query(await newServerClient(), artifactId, minConf, frameIndexStart, frameIndexEnd, model_ids, maxElements);
518
31
  }
519
32
  /** Tags for an artifact and subject_id. Omit maxElements (or pass null) for no row limit. */
520
33
  export async function server_get_tags_for_artifact_by_subject_id(artifactId, subjectId, model_ids = null, maxElements = null) {
521
- const supabase = await newServerClient();
522
- const { data, error } = await supabase.rpc("get_tags_for_artifact_by_subject_id", {
523
- artifact_id_caller: artifactId,
524
- subject_id_caller: subjectId,
525
- ...rpcModelIdsParam(model_ids),
526
- ...(maxElements != null && { max_elements_caller: maxElements }),
527
- });
528
- if (error) {
529
- console.warn("Error fetching tags for artifact by subject id:", error.message);
530
- return {
531
- status: EnumWebResponse.ERROR,
532
- msg: error.message,
533
- data: [],
534
- };
535
- }
536
- return IWebResponse.success(data ?? []).to_compatible();
34
+ return get_tags_for_artifact_by_subject_id_query(await newServerClient(), artifactId, subjectId, model_ids, maxElements);
537
35
  }
538
36
  /** Per-subject aggregates for tags on an artifact (rows with subject_id only). Optional min_avg_conf / min_count / model_ids; max_elements caps subject rows (omit or null for no cap). */
539
37
  export async function server_get_minimal_subjects_for_artifact(artifactId, options) {
540
- const supabase = await newServerClient();
541
- const { data, error } = await supabase.rpc("get_minimal_subjects_for_artifact", {
542
- artifact_id_caller: artifactId,
543
- ...(options?.min_avg_conf != null && {
544
- min_avg_conf_caller: options.min_avg_conf,
545
- }),
546
- ...(options?.min_count != null && {
547
- min_count_caller: options.min_count,
548
- }),
549
- ...rpcModelIdsParam(options?.model_ids),
550
- ...(options?.max_elements != null && {
551
- max_elements_caller: options.max_elements,
552
- }),
553
- });
554
- if (error) {
555
- console.warn("Error fetching minimal subjects for artifact:", error.message);
556
- return {
557
- status: EnumWebResponse.ERROR,
558
- msg: error.message,
559
- data: [],
560
- };
561
- }
562
- return IWebResponse.success(data ?? []).to_compatible();
38
+ return get_minimal_subjects_for_artifact_query(await newServerClient(), artifactId, options);
563
39
  }
564
40
  /** Tags for an event with optional timestamp range, model_ids filter, and max_elements (omit or null for no limit). */
565
41
  export async function server_get_tags_for_event(eventId, options) {
566
- const supabase = await newServerClient();
567
- const { data, error } = await supabase.rpc("get_tags_for_event", {
568
- event_id_caller: eventId,
569
- ...(options?.start_timestamp != null && {
570
- start_timestamp_caller: options.start_timestamp,
571
- }),
572
- ...(options?.end_timestamp != null && {
573
- end_timestamp_caller: options.end_timestamp,
574
- }),
575
- ...rpcModelIdsParam(options?.model_ids),
576
- ...(options?.max_elements != null && {
577
- max_elements_caller: options.max_elements,
578
- }),
579
- });
580
- if (error) {
581
- console.warn("Error fetching tags for event:", error.message);
582
- return {
583
- status: EnumWebResponse.ERROR,
584
- msg: error.message,
585
- data: [],
586
- };
587
- }
588
- return IWebResponse.success(data ?? []).to_compatible();
42
+ return get_tags_for_event_query(await newServerClient(), eventId, options);
589
43
  }
@@ -0,0 +1,33 @@
1
+ import { SupabaseClient } from "@supabase/supabase-js";
2
+ import { IEventAndTagsPrettyLocation, IMinimalSubjectForArtifact, ITag, ITagPrettyLocation } from "../types/db";
3
+ import { IWebResponseCompatible } from "../types/requests";
4
+ import { Database } from "../types/supabase";
5
+ export declare function create_tags_query(client: SupabaseClient<Database>, tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
6
+ export declare function delete_tags_by_ids_query(client: SupabaseClient<Database>, tag_ids: number[]): Promise<IWebResponseCompatible<boolean>>;
7
+ export declare function update_tags_query(client: SupabaseClient<Database>, tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
8
+ export declare function get_more_events_with_tags_by_herd_query(client: SupabaseClient<Database>, herd_id: number, offset: number, page_count?: number): Promise<IWebResponseCompatible<IEventAndTagsPrettyLocation[]>>;
9
+ export declare function get_events_and_tags_for_device_query(client: SupabaseClient<Database>, device_id: number, limit?: number): Promise<IWebResponseCompatible<IEventAndTagsPrettyLocation[]>>;
10
+ export declare function get_events_and_tags_for_devices_batch_query(client: SupabaseClient<Database>, device_ids: number[], limit?: number): Promise<IWebResponseCompatible<{
11
+ [device_id: number]: IEventAndTagsPrettyLocation[];
12
+ }>>;
13
+ export declare function get_event_and_tags_by_event_id_query(client: SupabaseClient<Database>, event_id: number): Promise<IWebResponseCompatible<IEventAndTagsPrettyLocation>>;
14
+ export declare function get_tags_for_artifact_query(client: SupabaseClient<Database>, artifactId: number, options?: {
15
+ start_timestamp?: string | null;
16
+ end_timestamp?: string | null;
17
+ max_elements?: number | null;
18
+ model_ids?: number[] | null;
19
+ }): Promise<IWebResponseCompatible<ITagPrettyLocation[]>>;
20
+ export declare function get_tags_for_artifact_by_frame_index_query(client: SupabaseClient<Database>, artifactId: number, minConf: number, frameIndexStart: number, frameIndexEnd: number, model_ids?: number[] | null, maxElements?: number | null): Promise<IWebResponseCompatible<ITagPrettyLocation[]>>;
21
+ export declare function get_tags_for_artifact_by_subject_id_query(client: SupabaseClient<Database>, artifactId: number, subjectId: number, model_ids?: number[] | null, maxElements?: number | null): Promise<IWebResponseCompatible<ITagPrettyLocation[]>>;
22
+ export declare function get_minimal_subjects_for_artifact_query(client: SupabaseClient<Database>, artifactId: number, options?: {
23
+ min_avg_conf?: number | null;
24
+ min_count?: number | null;
25
+ model_ids?: number[] | null;
26
+ max_elements?: number | null;
27
+ }): Promise<IWebResponseCompatible<IMinimalSubjectForArtifact[]>>;
28
+ export declare function get_tags_for_event_query(client: SupabaseClient<Database>, eventId: number, options?: {
29
+ start_timestamp?: string | null;
30
+ end_timestamp?: string | null;
31
+ max_elements?: number | null;
32
+ model_ids?: number[] | null;
33
+ }): Promise<IWebResponseCompatible<ITagPrettyLocation[]>>;