@adventurelabs/scout-core 1.4.60 → 1.4.61

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,7 +1,7 @@
1
1
  "use server";
2
2
  import { newServerClient } from "../supabase/server";
3
3
  import { IWebResponse } from "../types/requests";
4
- import { generateSignedUrlsBatch, generateSignedUrl } from "./storage";
4
+ import { generateSignedUrlsBatch, generateSignedUrl, isNonEmptyStorageFilePath, } from "./storage";
5
5
  export async function server_get_artifact_by_id(id, client) {
6
6
  const supabase = client || (await newServerClient());
7
7
  const { data, error } = await supabase
@@ -17,7 +17,7 @@ export async function server_get_artifact_by_id(id, client) {
17
17
  }
18
18
  const artifact = data;
19
19
  let media_url = null;
20
- if (artifact.file_path) {
20
+ if (isNonEmptyStorageFilePath(artifact.file_path)) {
21
21
  media_url = await generateSignedUrl(artifact.file_path, undefined, client);
22
22
  }
23
23
  return IWebResponse.success({
@@ -43,7 +43,7 @@ export async function server_get_artifacts_by_herd(herd_id, limit = 50, offset =
43
43
  // Generate signed URLs for artifacts
44
44
  const uniqueFilePaths = Array.from(new Set(data
45
45
  .map((artifact) => artifact.file_path)
46
- .filter((path) => !!path)));
46
+ .filter(isNonEmptyStorageFilePath)));
47
47
  if (uniqueFilePaths.length === 0) {
48
48
  return IWebResponse.success(data).to_compatible();
49
49
  }
@@ -54,8 +54,8 @@ export async function server_get_artifacts_by_herd(herd_id, limit = 50, offset =
54
54
  });
55
55
  const artifactsWithUrls = data.map((artifact) => ({
56
56
  ...artifact,
57
- media_url: artifact.file_path
58
- ? urlMap.get(artifact.file_path) || null
57
+ media_url: isNonEmptyStorageFilePath(artifact.file_path)
58
+ ? urlMap.get(artifact.file_path) ?? null
59
59
  : null,
60
60
  }));
61
61
  return IWebResponse.success(artifactsWithUrls).to_compatible();
@@ -76,7 +76,7 @@ export async function server_get_artifacts_by_device_id(device_id, limit = 50, o
76
76
  // Generate signed URLs for artifacts
77
77
  const uniqueFilePaths = Array.from(new Set(data
78
78
  .map((artifact) => artifact.file_path)
79
- .filter((path) => !!path)));
79
+ .filter(isNonEmptyStorageFilePath)));
80
80
  if (uniqueFilePaths.length === 0) {
81
81
  return IWebResponse.success(data).to_compatible();
82
82
  }
@@ -87,8 +87,8 @@ export async function server_get_artifacts_by_device_id(device_id, limit = 50, o
87
87
  });
88
88
  const artifactsWithUrls = data.map((artifact) => ({
89
89
  ...artifact,
90
- media_url: artifact.file_path
91
- ? urlMap.get(artifact.file_path) || null
90
+ media_url: isNonEmptyStorageFilePath(artifact.file_path)
91
+ ? urlMap.get(artifact.file_path) ?? null
92
92
  : null,
93
93
  }));
94
94
  return IWebResponse.success(artifactsWithUrls).to_compatible();
@@ -121,7 +121,7 @@ export async function server_get_artifacts_by_device_ids_batch(device_ids, limit
121
121
  // Generate signed URLs for artifacts
122
122
  const uniqueFilePaths = Array.from(new Set(data
123
123
  .map((artifact) => artifact.file_path)
124
- .filter((path) => !!path)));
124
+ .filter(isNonEmptyStorageFilePath)));
125
125
  let artifactsWithUrls = data;
126
126
  if (uniqueFilePaths.length > 0) {
127
127
  const signedUrls = await generateSignedUrlsBatch(uniqueFilePaths, undefined, options?.client);
@@ -131,8 +131,8 @@ export async function server_get_artifacts_by_device_ids_batch(device_ids, limit
131
131
  });
132
132
  artifactsWithUrls = data.map((artifact) => ({
133
133
  ...artifact,
134
- media_url: artifact.file_path
135
- ? urlMap.get(artifact.file_path) || null
134
+ media_url: isNonEmptyStorageFilePath(artifact.file_path)
135
+ ? urlMap.get(artifact.file_path) ?? null
136
136
  : null,
137
137
  }));
138
138
  }
@@ -2,7 +2,7 @@
2
2
  import { newServerClient } from "../supabase/server";
3
3
  import { EnumWebResponse, IWebResponse, } from "../types/requests";
4
4
  import { EnumSessionsVisibility } from "../types/events";
5
- import { generateSignedUrl } from "./storage";
5
+ import { generateSignedUrl, isNonEmptyStorageFilePath } from "./storage";
6
6
  export async function server_get_event_by_id(id, client) {
7
7
  const supabase = client || (await newServerClient());
8
8
  const { data, error } = await supabase
@@ -18,7 +18,7 @@ export async function server_get_event_by_id(id, client) {
18
18
  }
19
19
  const event = data;
20
20
  let media_url = null;
21
- if (event.file_path) {
21
+ if (isNonEmptyStorageFilePath(event.file_path)) {
22
22
  media_url = await generateSignedUrl(event.file_path, undefined, client);
23
23
  }
24
24
  return IWebResponse.success({
@@ -1,7 +1,7 @@
1
1
  "use server";
2
2
  import { newServerClient } from "../supabase/server";
3
3
  import { EnumWebResponse, IWebResponse, } from "../types/requests";
4
- import { generateSignedUrlsBatch } from "./storage";
4
+ import { generateSignedUrlsBatch, isNonEmptyStorageFilePath, } from "./storage";
5
5
  // Get session by ID with coordinates
6
6
  export async function server_get_session_by_id(sessionId, client) {
7
7
  const supabase = client || (await newServerClient());
@@ -79,7 +79,7 @@ export async function server_get_events_and_tags_by_session_id(sessionId, limit
79
79
  const eventData = (data || []);
80
80
  const filePaths = eventData
81
81
  .map((event) => event.file_path)
82
- .filter((path) => !!path);
82
+ .filter(isNonEmptyStorageFilePath);
83
83
  let eventsWithSignedUrls = eventData;
84
84
  if (filePaths.length > 0) {
85
85
  const signedUrls = await generateSignedUrlsBatch(filePaths, undefined, supabase);
@@ -89,7 +89,9 @@ export async function server_get_events_and_tags_by_session_id(sessionId, limit
89
89
  });
90
90
  eventsWithSignedUrls = eventData.map((event) => ({
91
91
  ...event,
92
- media_url: event.file_path ? urlMap.get(event.file_path) || null : null,
92
+ media_url: isNonEmptyStorageFilePath(event.file_path)
93
+ ? urlMap.get(event.file_path) ?? null
94
+ : null,
93
95
  }));
94
96
  }
95
97
  return IWebResponse.success(eventsWithSignedUrls).to_compatible();
@@ -1,5 +1,5 @@
1
1
  import { SupabaseClient } from "@supabase/supabase-js";
2
2
  import { Database } from "../types/supabase";
3
- export { parseStorageFilePath } from "./storagePath";
3
+ export { parseStorageFilePath, isNonEmptyStorageFilePath } from "./storagePath";
4
4
  export declare function generateSignedUrl(filePath: string, expiresIn?: number, supabaseClient?: SupabaseClient<Database>): Promise<string | null>;
5
5
  export declare function generateSignedUrlsBatch(filePaths: string[], expiresIn?: number, supabaseClient?: SupabaseClient<Database>): Promise<(string | null)[]>;
@@ -1,7 +1,7 @@
1
1
  import { newServerClient } from "../supabase/server";
2
2
  import { SIGNED_URL_EXPIRATION_SECONDS } from "../constants/db";
3
3
  import { parseStorageFilePath } from "./storagePath";
4
- export { parseStorageFilePath } from "./storagePath";
4
+ export { parseStorageFilePath, isNonEmptyStorageFilePath } from "./storagePath";
5
5
  export async function generateSignedUrl(filePath, expiresIn = SIGNED_URL_EXPIRATION_SECONDS, supabaseClient) {
6
6
  try {
7
7
  const supabase = supabaseClient || (await newServerClient());
@@ -1,3 +1,5 @@
1
+ /** True when we can sign storage: non-null and not blank after trim. */
2
+ export declare function isNonEmptyStorageFilePath(path: string | null | undefined): path is string;
1
3
  /** Assumes DB `file_path` is always `bucket/object/...` */
2
4
  export declare function parseStorageFilePath(filePath: string): {
3
5
  bucket: string;
@@ -1,3 +1,7 @@
1
+ /** True when we can sign storage: non-null and not blank after trim. */
2
+ export function isNonEmptyStorageFilePath(path) {
3
+ return path != null && path.trim() !== "";
4
+ }
1
5
  /** Assumes DB `file_path` is always `bucket/object/...` */
2
6
  export function parseStorageFilePath(filePath) {
3
7
  const cleaned = filePath.trim().replace(/^\/+|\/+$/g, "");
@@ -2,7 +2,7 @@
2
2
  // add tag to db
3
3
  import { newServerClient } from "../supabase/server";
4
4
  import { EnumWebResponse, IWebResponse, } from "../types/requests";
5
- import { generateSignedUrlsBatch, generateSignedUrl } from "./storage";
5
+ import { generateSignedUrlsBatch, generateSignedUrl, isNonEmptyStorageFilePath, } from "./storage";
6
6
  // Helper functions to extract coordinates from location field
7
7
  function extractLatitude(location) {
8
8
  try {
@@ -178,7 +178,7 @@ export async function server_get_more_events_with_tags_by_herd(herd_id, offset,
178
178
  // Generate signed URLs for events
179
179
  const filePaths = filtered_data
180
180
  .map((event) => event.file_path)
181
- .filter((path) => !!path);
181
+ .filter(isNonEmptyStorageFilePath);
182
182
  let eventsWithSignedUrls = filtered_data;
183
183
  if (filePaths.length > 0) {
184
184
  const signedUrls = await generateSignedUrlsBatch(filePaths, undefined, supabase);
@@ -188,7 +188,9 @@ export async function server_get_more_events_with_tags_by_herd(herd_id, offset,
188
188
  });
189
189
  eventsWithSignedUrls = filtered_data.map((event) => ({
190
190
  ...event,
191
- media_url: event.file_path ? urlMap.get(event.file_path) || null : null,
191
+ media_url: isNonEmptyStorageFilePath(event.file_path)
192
+ ? urlMap.get(event.file_path) ?? null
193
+ : null,
192
194
  }));
193
195
  }
194
196
  return IWebResponse.success(eventsWithSignedUrls).to_compatible();
@@ -212,7 +214,7 @@ export async function server_get_events_and_tags_for_device(device_id, limit = 3
212
214
  const eventData = data || [];
213
215
  const filePaths = eventData
214
216
  .map((event) => event.file_path)
215
- .filter((path) => !!path);
217
+ .filter(isNonEmptyStorageFilePath);
216
218
  let eventsWithSignedUrls = eventData;
217
219
  if (filePaths.length > 0) {
218
220
  const signedUrls = await generateSignedUrlsBatch(filePaths, undefined, supabase);
@@ -222,7 +224,9 @@ export async function server_get_events_and_tags_for_device(device_id, limit = 3
222
224
  });
223
225
  eventsWithSignedUrls = eventData.map((event) => ({
224
226
  ...event,
225
- media_url: event.file_path ? urlMap.get(event.file_path) || null : null,
227
+ media_url: isNonEmptyStorageFilePath(event.file_path)
228
+ ? urlMap.get(event.file_path) ?? null
229
+ : null,
226
230
  }));
227
231
  }
228
232
  return IWebResponse.success(eventsWithSignedUrls).to_compatible();
@@ -296,7 +300,8 @@ export async function server_get_events_and_tags_for_devices_batch(device_ids, l
296
300
  for (const device_id in eventsByDevice) {
297
301
  const events = eventsByDevice[device_id];
298
302
  events.forEach((event) => {
299
- if (event.file_path && !allFilePaths.includes(event.file_path)) {
303
+ if (isNonEmptyStorageFilePath(event.file_path) &&
304
+ !allFilePaths.includes(event.file_path)) {
300
305
  allFilePaths.push(event.file_path);
301
306
  }
302
307
  });
@@ -314,7 +319,9 @@ export async function server_get_events_and_tags_for_devices_batch(device_ids, l
314
319
  const events = eventsByDevice[device_id];
315
320
  const eventsWithSignedUrls = events.map((event) => ({
316
321
  ...event,
317
- media_url: event.file_path ? urlMap.get(event.file_path) || null : null,
322
+ media_url: isNonEmptyStorageFilePath(event.file_path)
323
+ ? urlMap.get(event.file_path) ?? null
324
+ : null,
318
325
  }));
319
326
  result[parseInt(device_id)] = eventsWithSignedUrls;
320
327
  }
@@ -454,7 +461,7 @@ export async function get_event_and_tags_by_event_id(event_id) {
454
461
  };
455
462
  // Generate signed URL for event
456
463
  let eventWithSignedUrl = transformedData;
457
- if (transformedData.file_path) {
464
+ if (isNonEmptyStorageFilePath(transformedData.file_path)) {
458
465
  const signedUrl = await generateSignedUrl(transformedData.file_path, undefined, supabase);
459
466
  eventWithSignedUrl = {
460
467
  ...transformedData,
package/dist/store/api.js CHANGED
@@ -1,16 +1,16 @@
1
1
  import { createApi, fakeBaseQuery } from "@reduxjs/toolkit/query/react";
2
- import { generateSignedUrlsBatch } from "../helpers/storage";
2
+ import { generateSignedUrlsBatch, isNonEmptyStorageFilePath, } from "../helpers/storage";
3
3
  async function signedUrlMapForFeedRows(rows) {
4
4
  const seen = new Set();
5
5
  const paths = [];
6
6
  for (const row of rows) {
7
7
  const ep = row.event_data?.file_path;
8
- if (ep && !seen.has(ep)) {
8
+ if (isNonEmptyStorageFilePath(ep) && !seen.has(ep)) {
9
9
  seen.add(ep);
10
10
  paths.push(ep);
11
11
  }
12
12
  const ap = row.artifact_data?.file_path;
13
- if (ap && !seen.has(ap)) {
13
+ if (isNonEmptyStorageFilePath(ap) && !seen.has(ap)) {
14
14
  seen.add(ap);
15
15
  paths.push(ap);
16
16
  }
@@ -185,7 +185,7 @@ export const scoutApi = createApi({
185
185
  // Generate signed URLs for events
186
186
  const uniqueFilePaths = Array.from(new Set(resultEvents
187
187
  .map((event) => event.file_path)
188
- .filter((path) => path !== null && path !== undefined)));
188
+ .filter(isNonEmptyStorageFilePath)));
189
189
  let urlMap = new Map();
190
190
  if (uniqueFilePaths.length > 0) {
191
191
  try {
@@ -202,8 +202,8 @@ export const scoutApi = createApi({
202
202
  }
203
203
  const eventsWithUrls = resultEvents.map((event) => ({
204
204
  ...event,
205
- media_url: event.file_path
206
- ? urlMap.get(event.file_path) || null
205
+ media_url: isNonEmptyStorageFilePath(event.file_path)
206
+ ? urlMap.get(event.file_path) ?? null
207
207
  : null,
208
208
  }));
209
209
  const nextCursor = hasMore && resultEvents.length > 0
@@ -263,7 +263,7 @@ export const scoutApi = createApi({
263
263
  // Generate signed URLs for events
264
264
  const uniqueFilePaths = Array.from(new Set(resultEvents
265
265
  .map((event) => event.file_path)
266
- .filter((path) => path !== null && path !== undefined)));
266
+ .filter(isNonEmptyStorageFilePath)));
267
267
  let urlMap = new Map();
268
268
  if (uniqueFilePaths.length > 0) {
269
269
  try {
@@ -280,8 +280,8 @@ export const scoutApi = createApi({
280
280
  }
281
281
  const eventsWithUrls = resultEvents.map((event) => ({
282
282
  ...event,
283
- media_url: event.file_path
284
- ? urlMap.get(event.file_path) || null
283
+ media_url: isNonEmptyStorageFilePath(event.file_path)
284
+ ? urlMap.get(event.file_path) ?? null
285
285
  : null,
286
286
  }));
287
287
  const nextCursor = hasMore && resultEvents.length > 0
@@ -346,7 +346,7 @@ export const scoutApi = createApi({
346
346
  // Generate signed URLs for artifacts
347
347
  const uniqueFilePaths = Array.from(new Set(resultArtifacts
348
348
  .map((artifact) => artifact.file_path)
349
- .filter((path) => path !== null && path !== undefined)));
349
+ .filter(isNonEmptyStorageFilePath)));
350
350
  let urlMap = new Map();
351
351
  if (uniqueFilePaths.length > 0) {
352
352
  try {
@@ -363,8 +363,8 @@ export const scoutApi = createApi({
363
363
  }
364
364
  const artifactsWithUrls = resultArtifacts.map((artifact) => ({
365
365
  ...artifact,
366
- media_url: artifact.file_path
367
- ? urlMap.get(artifact.file_path) || null
366
+ media_url: isNonEmptyStorageFilePath(artifact.file_path)
367
+ ? urlMap.get(artifact.file_path) ?? null
368
368
  : null,
369
369
  }));
370
370
  const nextCursor = hasMore && resultArtifacts.length > 0
@@ -425,7 +425,7 @@ export const scoutApi = createApi({
425
425
  // Generate signed URLs for artifacts
426
426
  const uniqueFilePaths = Array.from(new Set(resultArtifacts
427
427
  .map((artifact) => artifact.file_path)
428
- .filter((path) => path !== null && path !== undefined)));
428
+ .filter(isNonEmptyStorageFilePath)));
429
429
  let urlMap = new Map();
430
430
  if (uniqueFilePaths.length > 0) {
431
431
  try {
@@ -442,8 +442,8 @@ export const scoutApi = createApi({
442
442
  }
443
443
  const artifactsWithUrls = resultArtifacts.map((artifact) => ({
444
444
  ...artifact,
445
- media_url: artifact.file_path
446
- ? urlMap.get(artifact.file_path) || null
445
+ media_url: isNonEmptyStorageFilePath(artifact.file_path)
446
+ ? urlMap.get(artifact.file_path) ?? null
447
447
  : null,
448
448
  }));
449
449
  const nextCursor = hasMore && resultArtifacts.length > 0
@@ -516,7 +516,7 @@ export const scoutApi = createApi({
516
516
  event_data: row.event_data
517
517
  ? {
518
518
  ...row.event_data,
519
- media_url: row.event_data.file_path
519
+ media_url: isNonEmptyStorageFilePath(row.event_data.file_path)
520
520
  ? urlMap.get(row.event_data.file_path) ?? null
521
521
  : null,
522
522
  }
@@ -524,7 +524,7 @@ export const scoutApi = createApi({
524
524
  artifact_data: row.artifact_data
525
525
  ? {
526
526
  ...row.artifact_data,
527
- media_url: row.artifact_data.file_path
527
+ media_url: isNonEmptyStorageFilePath(row.artifact_data.file_path)
528
528
  ? urlMap.get(row.artifact_data.file_path) ?? null
529
529
  : null,
530
530
  }
@@ -591,7 +591,7 @@ export const scoutApi = createApi({
591
591
  event_data: row.event_data
592
592
  ? {
593
593
  ...row.event_data,
594
- media_url: row.event_data.file_path
594
+ media_url: isNonEmptyStorageFilePath(row.event_data.file_path)
595
595
  ? urlMap.get(row.event_data.file_path) ?? null
596
596
  : null,
597
597
  }
@@ -599,7 +599,7 @@ export const scoutApi = createApi({
599
599
  artifact_data: row.artifact_data
600
600
  ? {
601
601
  ...row.artifact_data,
602
- media_url: row.artifact_data.file_path
602
+ media_url: isNonEmptyStorageFilePath(row.artifact_data.file_path)
603
603
  ? urlMap.get(row.artifact_data.file_path) ?? null
604
604
  : null,
605
605
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.4.60",
3
+ "version": "1.4.61",
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",