@adventurelabs/scout-core 1.5.1 → 1.5.2

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.
@@ -0,0 +1,5 @@
1
+ import { IArtifact, IArtifactWithMediaUrl } from "../types/db";
2
+ /** Collect unique non-empty storage paths used for artifact media signing. */
3
+ export declare function collectArtifactStoragePaths(artifacts: Iterable<Pick<IArtifact, "file_path" | "thumbnail_file_path" | "proxy_file_path">>): string[];
4
+ /** Attach signed URLs for original, thumbnail, and proxy storage paths. */
5
+ export declare function withArtifactMediaUrls(artifact: IArtifact, urlMap: Map<string, string | null | undefined>): IArtifactWithMediaUrl;
@@ -0,0 +1,31 @@
1
+ import { isNonEmptyStorageFilePath } from "./storagePath";
2
+ /** Collect unique non-empty storage paths used for artifact media signing. */
3
+ export function collectArtifactStoragePaths(artifacts) {
4
+ const seen = new Set();
5
+ const paths = [];
6
+ for (const artifact of artifacts) {
7
+ for (const path of [
8
+ artifact.file_path,
9
+ artifact.thumbnail_file_path,
10
+ artifact.proxy_file_path,
11
+ ]) {
12
+ if (isNonEmptyStorageFilePath(path) && !seen.has(path)) {
13
+ seen.add(path);
14
+ paths.push(path);
15
+ }
16
+ }
17
+ }
18
+ return paths;
19
+ }
20
+ function signedUrlForPath(path, urlMap) {
21
+ return isNonEmptyStorageFilePath(path) ? urlMap.get(path) ?? null : null;
22
+ }
23
+ /** Attach signed URLs for original, thumbnail, and proxy storage paths. */
24
+ export function withArtifactMediaUrls(artifact, urlMap) {
25
+ return {
26
+ ...artifact,
27
+ media_url: signedUrlForPath(artifact.file_path, urlMap),
28
+ thumbnail_url: signedUrlForPath(artifact.thumbnail_file_path, urlMap),
29
+ proxy_url: signedUrlForPath(artifact.proxy_file_path, urlMap),
30
+ };
31
+ }
@@ -1,26 +1,19 @@
1
1
  "use server";
2
2
  import { newServerClient } from "../supabase/server";
3
3
  import { IWebResponse } from "../types/requests";
4
- import { generateSignedUrlsBatchWithClient, generateSignedUrlWithClient, } from "./storage_internal";
5
- import { isNonEmptyStorageFilePath } from "./storagePath";
4
+ import { generateSignedUrlsBatchWithClient, } from "./storage_internal";
5
+ import { collectArtifactStoragePaths, withArtifactMediaUrls, } from "./artifactMedia";
6
6
  async function addArtifactMediaUrls(data, client) {
7
- const uniqueFilePaths = Array.from(new Set(data
8
- .map((artifact) => artifact.file_path)
9
- .filter(isNonEmptyStorageFilePath)));
7
+ const uniqueFilePaths = collectArtifactStoragePaths(data);
10
8
  if (uniqueFilePaths.length === 0) {
11
- return data;
9
+ return data.map((artifact) => withArtifactMediaUrls(artifact, new Map()));
12
10
  }
13
11
  const signedUrls = await generateSignedUrlsBatchWithClient(client, uniqueFilePaths);
14
12
  const urlMap = new Map();
15
13
  uniqueFilePaths.forEach((path, index) => {
16
14
  urlMap.set(path, signedUrls[index]);
17
15
  });
18
- return data.map((artifact) => ({
19
- ...artifact,
20
- media_url: isNonEmptyStorageFilePath(artifact.file_path)
21
- ? urlMap.get(artifact.file_path) ?? null
22
- : null,
23
- }));
16
+ return data.map((artifact) => withArtifactMediaUrls(artifact, urlMap));
24
17
  }
25
18
  export async function server_get_artifact_by_id(id) {
26
19
  const client = await newServerClient();
@@ -36,14 +29,15 @@ export async function server_get_artifact_by_id(id) {
36
29
  return IWebResponse.success(null).to_compatible();
37
30
  }
38
31
  const artifact = data;
39
- let media_url = null;
40
- if (isNonEmptyStorageFilePath(artifact.file_path)) {
41
- media_url = await generateSignedUrlWithClient(client, artifact.file_path);
32
+ const paths = collectArtifactStoragePaths([artifact]);
33
+ const urlMap = new Map();
34
+ if (paths.length > 0) {
35
+ const signedUrls = await generateSignedUrlsBatchWithClient(client, paths);
36
+ paths.forEach((path, index) => {
37
+ urlMap.set(path, signedUrls[index]);
38
+ });
42
39
  }
43
- return IWebResponse.success({
44
- ...artifact,
45
- media_url,
46
- }).to_compatible();
40
+ return IWebResponse.success(withArtifactMediaUrls(artifact, urlMap)).to_compatible();
47
41
  }
48
42
  export async function server_get_artifacts_by_herd(herd_id, limit = 50, offset = 0, options) {
49
43
  const client = await newServerClient();
package/dist/store/api.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createApi, fakeBaseQuery } from "@reduxjs/toolkit/query/react";
2
2
  import { generateSignedUrlsBatch, } from "../helpers/storage";
3
+ import { collectArtifactStoragePaths, withArtifactMediaUrls, } from "../helpers/artifactMedia";
3
4
  import { isNonEmptyStorageFilePath } from "../helpers/storagePath";
4
5
  async function signedUrlMapForFeedRows(rows) {
5
6
  const seen = new Set();
@@ -10,10 +11,13 @@ async function signedUrlMapForFeedRows(rows) {
10
11
  seen.add(ep);
11
12
  paths.push(ep);
12
13
  }
13
- const ap = row.artifact_data?.file_path;
14
- if (isNonEmptyStorageFilePath(ap) && !seen.has(ap)) {
15
- seen.add(ap);
16
- paths.push(ap);
14
+ if (row.artifact_data) {
15
+ for (const path of collectArtifactStoragePaths([row.artifact_data])) {
16
+ if (!seen.has(path)) {
17
+ seen.add(path);
18
+ paths.push(path);
19
+ }
20
+ }
17
21
  }
18
22
  }
19
23
  const map = new Map();
@@ -345,9 +349,7 @@ export const scoutApi = createApi({
345
349
  ? artifacts.slice(0, limit)
346
350
  : artifacts;
347
351
  // Generate signed URLs for artifacts
348
- const uniqueFilePaths = Array.from(new Set(resultArtifacts
349
- .map((artifact) => artifact.file_path)
350
- .filter(isNonEmptyStorageFilePath)));
352
+ const uniqueFilePaths = collectArtifactStoragePaths(resultArtifacts);
351
353
  let urlMap = new Map();
352
354
  if (uniqueFilePaths.length > 0) {
353
355
  try {
@@ -362,12 +364,7 @@ export const scoutApi = createApi({
362
364
  console.warn("Failed to generate signed URLs for artifacts:", urlError);
363
365
  }
364
366
  }
365
- const artifactsWithUrls = resultArtifacts.map((artifact) => ({
366
- ...artifact,
367
- media_url: isNonEmptyStorageFilePath(artifact.file_path)
368
- ? urlMap.get(artifact.file_path) ?? null
369
- : null,
370
- }));
367
+ const artifactsWithUrls = resultArtifacts.map((artifact) => withArtifactMediaUrls(artifact, urlMap));
371
368
  const nextCursor = hasMore && resultArtifacts.length > 0
372
369
  ? {
373
370
  timestamp: resultArtifacts[resultArtifacts.length - 1].created_at,
@@ -424,9 +421,7 @@ export const scoutApi = createApi({
424
421
  ? artifacts.slice(0, limit)
425
422
  : artifacts;
426
423
  // Generate signed URLs for artifacts
427
- const uniqueFilePaths = Array.from(new Set(resultArtifacts
428
- .map((artifact) => artifact.file_path)
429
- .filter(isNonEmptyStorageFilePath)));
424
+ const uniqueFilePaths = collectArtifactStoragePaths(resultArtifacts);
430
425
  let urlMap = new Map();
431
426
  if (uniqueFilePaths.length > 0) {
432
427
  try {
@@ -441,12 +436,7 @@ export const scoutApi = createApi({
441
436
  console.warn("Failed to generate signed URLs for artifacts:", urlError);
442
437
  }
443
438
  }
444
- const artifactsWithUrls = resultArtifacts.map((artifact) => ({
445
- ...artifact,
446
- media_url: isNonEmptyStorageFilePath(artifact.file_path)
447
- ? urlMap.get(artifact.file_path) ?? null
448
- : null,
449
- }));
439
+ const artifactsWithUrls = resultArtifacts.map((artifact) => withArtifactMediaUrls(artifact, urlMap));
450
440
  const nextCursor = hasMore && resultArtifacts.length > 0
451
441
  ? {
452
442
  timestamp: resultArtifacts[resultArtifacts.length - 1].created_at,
@@ -523,12 +513,7 @@ export const scoutApi = createApi({
523
513
  }
524
514
  : null,
525
515
  artifact_data: row.artifact_data
526
- ? {
527
- ...row.artifact_data,
528
- media_url: isNonEmptyStorageFilePath(row.artifact_data.file_path)
529
- ? urlMap.get(row.artifact_data.file_path) ?? null
530
- : null,
531
- }
516
+ ? withArtifactMediaUrls(row.artifact_data, urlMap)
532
517
  : null,
533
518
  }));
534
519
  const last = resultRows[resultRows.length - 1];
@@ -598,12 +583,7 @@ export const scoutApi = createApi({
598
583
  }
599
584
  : null,
600
585
  artifact_data: row.artifact_data
601
- ? {
602
- ...row.artifact_data,
603
- media_url: isNonEmptyStorageFilePath(row.artifact_data.file_path)
604
- ? urlMap.get(row.artifact_data.file_path) ?? null
605
- : null,
606
- }
586
+ ? withArtifactMediaUrls(row.artifact_data, urlMap)
607
587
  : null,
608
588
  }));
609
589
  const last = resultRows[resultRows.length - 1];
@@ -6,6 +6,7 @@ export type MediaType = Database["public"]["Enums"]["media_type"];
6
6
  export type TagObservationType = Database["public"]["Enums"]["tag_observation_type"];
7
7
  export type AnalysisWorkStatus = Database["public"]["Enums"]["analysis_work_status"];
8
8
  export type EntityLifecycle = Database["public"]["Enums"]["entity_lifecycle"];
9
+ export type DocumentEntity = Database["public"]["Enums"]["document_entity"];
9
10
  /** Supabase Auth user (`auth.getUser()`); not `public.users`. */
10
11
  export type IUser = User;
11
12
  /** Full `public.users` profile row (generated from DB). */
@@ -148,6 +149,8 @@ export type IAnalysisJob = Database["public"]["Tables"]["analysis_jobs"]["Row"];
148
149
  export type IAnalysisTask = Database["public"]["Tables"]["analysis_tasks"]["Row"];
149
150
  export type IArtifactWithMediaUrl = IArtifact & {
150
151
  media_url?: string | null;
152
+ thumbnail_url?: string | null;
153
+ proxy_url?: string | null;
151
154
  };
152
155
  export type IEventWithMediaUrl = IEvent & {
153
156
  media_url?: string | null;
@@ -257,9 +257,13 @@ export type Database = {
257
257
  lifecycle_changed_by: string | null;
258
258
  lifecycle_reason: string | null;
259
259
  modality: string | null;
260
+ proxy_file_path: string | null;
261
+ proxy_generated_at: string | null;
260
262
  segmented_at: string | null;
261
263
  session_id: number | null;
262
264
  tagged_at: string | null;
265
+ thumbnail_file_path: string | null;
266
+ thumbnail_generated_at: string | null;
263
267
  timestamp_observation: string | null;
264
268
  timestamp_observation_end: string;
265
269
  tracked_at: string | null;
@@ -277,9 +281,13 @@ export type Database = {
277
281
  lifecycle_changed_by?: string | null;
278
282
  lifecycle_reason?: string | null;
279
283
  modality?: string | null;
284
+ proxy_file_path?: string | null;
285
+ proxy_generated_at?: string | null;
280
286
  segmented_at?: string | null;
281
287
  session_id?: number | null;
282
288
  tagged_at?: string | null;
289
+ thumbnail_file_path?: string | null;
290
+ thumbnail_generated_at?: string | null;
283
291
  timestamp_observation?: string | null;
284
292
  timestamp_observation_end?: string;
285
293
  tracked_at?: string | null;
@@ -297,9 +305,13 @@ export type Database = {
297
305
  lifecycle_changed_by?: string | null;
298
306
  lifecycle_reason?: string | null;
299
307
  modality?: string | null;
308
+ proxy_file_path?: string | null;
309
+ proxy_generated_at?: string | null;
300
310
  segmented_at?: string | null;
301
311
  session_id?: number | null;
302
312
  tagged_at?: string | null;
313
+ thumbnail_file_path?: string | null;
314
+ thumbnail_generated_at?: string | null;
303
315
  timestamp_observation?: string | null;
304
316
  timestamp_observation_end?: string;
305
317
  tracked_at?: string | null;
@@ -4231,9 +4243,13 @@ export type Database = {
4231
4243
  lifecycle_changed_by: string | null;
4232
4244
  lifecycle_reason: string | null;
4233
4245
  modality: string | null;
4246
+ proxy_file_path: string | null;
4247
+ proxy_generated_at: string | null;
4234
4248
  segmented_at: string | null;
4235
4249
  session_id: number | null;
4236
4250
  tagged_at: string | null;
4251
+ thumbnail_file_path: string | null;
4252
+ thumbnail_generated_at: string | null;
4237
4253
  timestamp_observation: string | null;
4238
4254
  timestamp_observation_end: string;
4239
4255
  tracked_at: string | null;
@@ -4263,9 +4279,13 @@ export type Database = {
4263
4279
  lifecycle_changed_by: string | null;
4264
4280
  lifecycle_reason: string | null;
4265
4281
  modality: string | null;
4282
+ proxy_file_path: string | null;
4283
+ proxy_generated_at: string | null;
4266
4284
  segmented_at: string | null;
4267
4285
  session_id: number | null;
4268
4286
  tagged_at: string | null;
4287
+ thumbnail_file_path: string | null;
4288
+ thumbnail_generated_at: string | null;
4269
4289
  timestamp_observation: string | null;
4270
4290
  timestamp_observation_end: string;
4271
4291
  tracked_at: string | null;
@@ -4296,9 +4316,13 @@ export type Database = {
4296
4316
  lifecycle_changed_by: string | null;
4297
4317
  lifecycle_reason: string | null;
4298
4318
  modality: string | null;
4319
+ proxy_file_path: string | null;
4320
+ proxy_generated_at: string | null;
4299
4321
  segmented_at: string | null;
4300
4322
  session_id: number | null;
4301
4323
  tagged_at: string | null;
4324
+ thumbnail_file_path: string | null;
4325
+ thumbnail_generated_at: string | null;
4302
4326
  timestamp_observation: string | null;
4303
4327
  timestamp_observation_end: string;
4304
4328
  tracked_at: string | null;
@@ -4329,9 +4353,13 @@ export type Database = {
4329
4353
  lifecycle_changed_by: string | null;
4330
4354
  lifecycle_reason: string | null;
4331
4355
  modality: string | null;
4356
+ proxy_file_path: string | null;
4357
+ proxy_generated_at: string | null;
4332
4358
  segmented_at: string | null;
4333
4359
  session_id: number | null;
4334
4360
  tagged_at: string | null;
4361
+ thumbnail_file_path: string | null;
4362
+ thumbnail_generated_at: string | null;
4335
4363
  timestamp_observation: string | null;
4336
4364
  timestamp_observation_end: string;
4337
4365
  tracked_at: string | null;
@@ -4363,9 +4391,13 @@ export type Database = {
4363
4391
  lifecycle_changed_by: string | null;
4364
4392
  lifecycle_reason: string | null;
4365
4393
  modality: string | null;
4394
+ proxy_file_path: string | null;
4395
+ proxy_generated_at: string | null;
4366
4396
  segmented_at: string | null;
4367
4397
  session_id: number | null;
4368
4398
  tagged_at: string | null;
4399
+ thumbnail_file_path: string | null;
4400
+ thumbnail_generated_at: string | null;
4369
4401
  timestamp_observation: string | null;
4370
4402
  timestamp_observation_end: string;
4371
4403
  tracked_at: string | null;
@@ -4397,9 +4429,13 @@ export type Database = {
4397
4429
  lifecycle_changed_by: string | null;
4398
4430
  lifecycle_reason: string | null;
4399
4431
  modality: string | null;
4432
+ proxy_file_path: string | null;
4433
+ proxy_generated_at: string | null;
4400
4434
  segmented_at: string | null;
4401
4435
  session_id: number | null;
4402
4436
  tagged_at: string | null;
4437
+ thumbnail_file_path: string | null;
4438
+ thumbnail_generated_at: string | null;
4403
4439
  timestamp_observation: string | null;
4404
4440
  timestamp_observation_end: string;
4405
4441
  tracked_at: string | null;
@@ -4432,9 +4468,13 @@ export type Database = {
4432
4468
  lifecycle_changed_by: string | null;
4433
4469
  lifecycle_reason: string | null;
4434
4470
  modality: string | null;
4471
+ proxy_file_path: string | null;
4472
+ proxy_generated_at: string | null;
4435
4473
  segmented_at: string | null;
4436
4474
  session_id: number | null;
4437
4475
  tagged_at: string | null;
4476
+ thumbnail_file_path: string | null;
4477
+ thumbnail_generated_at: string | null;
4438
4478
  timestamp_observation: string | null;
4439
4479
  timestamp_observation_end: string;
4440
4480
  tracked_at: string | null;
@@ -4466,9 +4506,13 @@ export type Database = {
4466
4506
  lifecycle_changed_by: string | null;
4467
4507
  lifecycle_reason: string | null;
4468
4508
  modality: string | null;
4509
+ proxy_file_path: string | null;
4510
+ proxy_generated_at: string | null;
4469
4511
  segmented_at: string | null;
4470
4512
  session_id: number | null;
4471
4513
  tagged_at: string | null;
4514
+ thumbnail_file_path: string | null;
4515
+ thumbnail_generated_at: string | null;
4472
4516
  timestamp_observation: string | null;
4473
4517
  timestamp_observation_end: string;
4474
4518
  tracked_at: string | null;
@@ -4501,9 +4545,13 @@ export type Database = {
4501
4545
  lifecycle_changed_by: string | null;
4502
4546
  lifecycle_reason: string | null;
4503
4547
  modality: string | null;
4548
+ proxy_file_path: string | null;
4549
+ proxy_generated_at: string | null;
4504
4550
  segmented_at: string | null;
4505
4551
  session_id: number | null;
4506
4552
  tagged_at: string | null;
4553
+ thumbnail_file_path: string | null;
4554
+ thumbnail_generated_at: string | null;
4507
4555
  timestamp_observation: string | null;
4508
4556
  timestamp_observation_end: string;
4509
4557
  tracked_at: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
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",