@adventurelabs/scout-core 1.5.1 → 1.5.3

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();
@@ -40,6 +40,7 @@ export * from "./cache";
40
40
  export * from "./observations";
41
41
  export * from "./operators";
42
42
  export * from "./parts";
43
+ export * from "./sensor_poses";
43
44
  export * from "./versions_software";
44
45
  export * from "./versions_software_server";
45
46
  export * from "./artifacts";
@@ -40,6 +40,7 @@ export * from "./cache";
40
40
  export * from "./observations";
41
41
  export * from "./operators";
42
42
  export * from "./parts";
43
+ export * from "./sensor_poses";
43
44
  export * from "./versions_software";
44
45
  export * from "./versions_software_server";
45
46
  export * from "./artifacts";
@@ -1,4 +1,4 @@
1
- import { DocumentConditionCreateInput, DocumentConditionUpdateInput, IDocumentCondition, IOperatingContext, IOperatingContextEquipmentCategory, IOperatingContextEquipmentItem, IOperatingContextEquipmentRequirement, IOperatingContextPayload, IOperatingContextRegion, IOperatingContextRisk, IOperatingContextRole, IOperatingContextRoleAssignment, IOperatingContextType, IOperatingContextWeatherSnapshot, OperatingContextCreateInput, OperatingContextEquipmentCategoryCreateInput, OperatingContextEquipmentCategoryUpdateInput, OperatingContextEquipmentItemCreateInput, OperatingContextEquipmentItemUpdateInput, OperatingContextEquipmentRequirementCreateInput, OperatingContextEquipmentRequirementUpdateInput, OperatingContextListOptions, OperatingContextPayloadCreateInput, OperatingContextPayloadUpdateInput, OperatingContextRegionCreateInput, OperatingContextRegionUpdateInput, OperatingContextRiskCreateInput, OperatingContextRiskUpdateInput, OperatingContextRoleAssignmentCreateInput, OperatingContextRoleAssignmentUpdateInput, OperatingContextRoleCreateInput, OperatingContextRoleUpdateInput, OperatingContextTypeCreateInput, OperatingContextTypeUpdateInput, OperatingContextUpdateInput } from "../types/db";
1
+ import { DocumentConditionCreateInput, DocumentConditionUpdateInput, IDocumentCondition, IOperatingContext, IOperatingContextDevice, IOperatingContextEquipmentCategory, IOperatingContextEquipmentItem, IOperatingContextEquipmentRequirement, IOperatingContextPayload, IOperatingContextRegion, IOperatingContextRisk, IOperatingContextRole, IOperatingContextRoleAssignment, IOperatingContextType, IOperatingContextWeatherSnapshot, OperatingContextCreateInput, OperatingContextDeviceCreateInput, OperatingContextDeviceUpdateInput, OperatingContextEquipmentCategoryCreateInput, OperatingContextEquipmentCategoryUpdateInput, OperatingContextEquipmentItemCreateInput, OperatingContextEquipmentItemUpdateInput, OperatingContextEquipmentRequirementCreateInput, OperatingContextEquipmentRequirementUpdateInput, OperatingContextListOptions, OperatingContextPayloadCreateInput, OperatingContextPayloadUpdateInput, OperatingContextRegionCreateInput, OperatingContextRegionUpdateInput, OperatingContextRiskCreateInput, OperatingContextRiskUpdateInput, OperatingContextRoleAssignmentCreateInput, OperatingContextRoleAssignmentUpdateInput, OperatingContextRoleCreateInput, OperatingContextRoleUpdateInput, OperatingContextTypeCreateInput, OperatingContextTypeUpdateInput, OperatingContextUpdateInput } from "../types/db";
2
2
  import { IWebResponseCompatible } from "../types/requests";
3
3
  export declare function server_get_document_conditions_by_localization(localization_id: number, options?: OperatingContextListOptions): Promise<IWebResponseCompatible<IDocumentCondition[]>>;
4
4
  export declare function server_create_document_condition(row: DocumentConditionCreateInput): Promise<IWebResponseCompatible<IDocumentCondition | null>>;
@@ -25,6 +25,9 @@ export declare function server_update_operating_context_payload(id: number, upda
25
25
  export declare function server_get_operating_context_role_assignments_by_operating_context(operating_context_id: number, options?: OperatingContextListOptions): Promise<IWebResponseCompatible<IOperatingContextRoleAssignment[]>>;
26
26
  export declare function server_create_operating_context_role_assignment(row: OperatingContextRoleAssignmentCreateInput): Promise<IWebResponseCompatible<IOperatingContextRoleAssignment | null>>;
27
27
  export declare function server_update_operating_context_role_assignment(id: number, updates: OperatingContextRoleAssignmentUpdateInput): Promise<IWebResponseCompatible<IOperatingContextRoleAssignment | null>>;
28
+ export declare function server_get_operating_context_devices_by_operating_context(operating_context_id: number, options?: OperatingContextListOptions): Promise<IWebResponseCompatible<IOperatingContextDevice[]>>;
29
+ export declare function server_create_operating_context_device(row: OperatingContextDeviceCreateInput): Promise<IWebResponseCompatible<IOperatingContextDevice | null>>;
30
+ export declare function server_update_operating_context_device(id: number, updates: OperatingContextDeviceUpdateInput): Promise<IWebResponseCompatible<IOperatingContextDevice | null>>;
28
31
  export declare function server_get_operating_context_equipment_requirements_by_operating_context(operating_context_id: number, options?: OperatingContextListOptions): Promise<IWebResponseCompatible<IOperatingContextEquipmentRequirement[]>>;
29
32
  export declare function server_create_operating_context_equipment_requirement(row: OperatingContextEquipmentRequirementCreateInput): Promise<IWebResponseCompatible<IOperatingContextEquipmentRequirement | null>>;
30
33
  export declare function server_update_operating_context_equipment_requirement(id: number, updates: OperatingContextEquipmentRequirementUpdateInput): Promise<IWebResponseCompatible<IOperatingContextEquipmentRequirement | null>>;
@@ -177,6 +177,15 @@ export async function server_create_operating_context_role_assignment(row) {
177
177
  export async function server_update_operating_context_role_assignment(id, updates) {
178
178
  return patch_row("operating_context_role_assignments", id, updates, "lifecycle");
179
179
  }
180
+ export async function server_get_operating_context_devices_by_operating_context(operating_context_id, options) {
181
+ return list_by("operating_contexts_devices", "operating_context_id", operating_context_id, options);
182
+ }
183
+ export async function server_create_operating_context_device(row) {
184
+ return insert_row("operating_contexts_devices", row);
185
+ }
186
+ export async function server_update_operating_context_device(id, updates) {
187
+ return patch_row("operating_contexts_devices", id, updates, "lifecycle");
188
+ }
180
189
  export async function server_get_operating_context_equipment_requirements_by_operating_context(operating_context_id, options) {
181
190
  return list_by("operating_context_equipment_requirements", "operating_context_id", operating_context_id, options);
182
191
  }
@@ -0,0 +1,16 @@
1
+ import { Database } from "../types/supabase";
2
+ import { ISensorPose, SensorPoseInsert, SensorPoseUpdate } from "../types/db";
3
+ import { IWebResponseCompatible } from "../types/requests";
4
+ import { SupabaseClient } from "@supabase/supabase-js";
5
+ interface SensorPoseTimeRangeOptions {
6
+ start_timestamp?: string;
7
+ end_timestamp?: string;
8
+ limit?: number;
9
+ }
10
+ export declare function get_sensor_poses_by_part_id(client: SupabaseClient<Database>, part_id: number, options?: SensorPoseTimeRangeOptions): Promise<IWebResponseCompatible<ISensorPose[]>>;
11
+ export declare function get_sensor_pose_by_id(client: SupabaseClient<Database>, sensor_pose_id: number): Promise<IWebResponseCompatible<ISensorPose | null>>;
12
+ export declare function create_sensor_pose(client: SupabaseClient<Database>, row: SensorPoseInsert): Promise<IWebResponseCompatible<ISensorPose | null>>;
13
+ export declare function create_sensor_poses(client: SupabaseClient<Database>, rows: SensorPoseInsert[]): Promise<IWebResponseCompatible<ISensorPose[]>>;
14
+ export declare function update_sensor_pose(client: SupabaseClient<Database>, sensor_pose_id: number, patch: SensorPoseUpdate): Promise<IWebResponseCompatible<ISensorPose | null>>;
15
+ export declare function delete_sensor_pose(client: SupabaseClient<Database>, sensor_pose_id: number): Promise<IWebResponseCompatible<ISensorPose | null>>;
16
+ export {};
@@ -0,0 +1,97 @@
1
+ import { IWebResponse } from "../types/requests";
2
+ export async function get_sensor_poses_by_part_id(client, part_id, options) {
3
+ let query = client
4
+ .from("sensor_poses")
5
+ .select("*")
6
+ .eq("part_id", part_id)
7
+ .order("timestamp_observation", { ascending: true });
8
+ if (options?.start_timestamp) {
9
+ query = query.gte("timestamp_observation", options.start_timestamp);
10
+ }
11
+ if (options?.end_timestamp) {
12
+ query = query.lte("timestamp_observation", options.end_timestamp);
13
+ }
14
+ if (options?.limit != null) {
15
+ query = query.limit(options.limit);
16
+ }
17
+ const { data, error } = await query;
18
+ if (error) {
19
+ return IWebResponse.error(error.message).to_compatible();
20
+ }
21
+ return IWebResponse.success(data ?? []).to_compatible();
22
+ }
23
+ export async function get_sensor_pose_by_id(client, sensor_pose_id) {
24
+ const { data, error } = await client
25
+ .from("sensor_poses")
26
+ .select("*")
27
+ .eq("id", sensor_pose_id)
28
+ .maybeSingle();
29
+ if (error) {
30
+ return IWebResponse.error(error.message).to_compatible();
31
+ }
32
+ if (!data) {
33
+ return IWebResponse.error("Sensor pose not found").to_compatible();
34
+ }
35
+ return IWebResponse.success(data).to_compatible();
36
+ }
37
+ export async function create_sensor_pose(client, row) {
38
+ const { data, error } = await client
39
+ .from("sensor_poses")
40
+ .insert([row])
41
+ .select("*")
42
+ .single();
43
+ if (error) {
44
+ return IWebResponse.error(error.message).to_compatible();
45
+ }
46
+ if (!data) {
47
+ return IWebResponse.error("Failed to create sensor pose").to_compatible();
48
+ }
49
+ return IWebResponse.success(data).to_compatible();
50
+ }
51
+ export async function create_sensor_poses(client, rows) {
52
+ if (rows.length === 0) {
53
+ return IWebResponse.success([]).to_compatible();
54
+ }
55
+ const { data, error } = await client
56
+ .from("sensor_poses")
57
+ .insert(rows)
58
+ .select("*");
59
+ if (error) {
60
+ return IWebResponse.error(error.message).to_compatible();
61
+ }
62
+ return IWebResponse.success(data ?? []).to_compatible();
63
+ }
64
+ export async function update_sensor_pose(client, sensor_pose_id, patch) {
65
+ const { id: _id, inserted_at: _ia, ...updateData } = patch;
66
+ if (Object.keys(updateData).length === 0) {
67
+ return IWebResponse.error("No valid fields to update").to_compatible();
68
+ }
69
+ const { data, error } = await client
70
+ .from("sensor_poses")
71
+ .update(updateData)
72
+ .eq("id", sensor_pose_id)
73
+ .select("*")
74
+ .single();
75
+ if (error) {
76
+ return IWebResponse.error(error.message).to_compatible();
77
+ }
78
+ if (!data) {
79
+ return IWebResponse.error("Sensor pose not found or update failed").to_compatible();
80
+ }
81
+ return IWebResponse.success(data).to_compatible();
82
+ }
83
+ export async function delete_sensor_pose(client, sensor_pose_id) {
84
+ const { data, error } = await client
85
+ .from("sensor_poses")
86
+ .delete()
87
+ .eq("id", sensor_pose_id)
88
+ .select("*")
89
+ .single();
90
+ if (error) {
91
+ return IWebResponse.error(error.message).to_compatible();
92
+ }
93
+ if (!data) {
94
+ return IWebResponse.error("Sensor pose not found or deletion failed").to_compatible();
95
+ }
96
+ return IWebResponse.success(data).to_compatible();
97
+ }
@@ -1,12 +1,12 @@
1
1
  import { SupabaseClient } from "@supabase/supabase-js";
2
2
  import { Database } from "../types/supabase";
3
- import { IOperatingContext, IOperatingContextEquipmentRequirement, IOperatingContextPayload, IOperatingContextRegion, IOperatingContextRisk, IOperatingContextRoleAssignment } from "../types/db";
3
+ import { IOperatingContext, IOperatingContextDevice, IOperatingContextEquipmentRequirement, IOperatingContextPayload, IOperatingContextRegion, IOperatingContextRisk, IOperatingContextRoleAssignment } from "../types/db";
4
4
  import { RealtimeData } from "../types/realtime";
5
5
  import { ScoutRealtimeBroadcastOptions } from "./useScoutRealtimeBroadcast";
6
- export type OperatingContextRealtimeRow = IOperatingContext | IOperatingContextPayload | IOperatingContextRoleAssignment | IOperatingContextEquipmentRequirement | IOperatingContextRegion | IOperatingContextRisk;
6
+ export type OperatingContextRealtimeRow = IOperatingContext | IOperatingContextDevice | IOperatingContextPayload | IOperatingContextRoleAssignment | IOperatingContextEquipmentRequirement | IOperatingContextRegion | IOperatingContextRisk;
7
7
  type OperatingContextRealtimeChange<T, Table extends string> = Omit<RealtimeData<T>, "table"> & {
8
8
  table: Table;
9
9
  };
10
- export type OperatingContextRealtimeData = OperatingContextRealtimeChange<IOperatingContext, "operating_contexts"> | OperatingContextRealtimeChange<IOperatingContextPayload, "operating_context_payloads"> | OperatingContextRealtimeChange<IOperatingContextRoleAssignment, "operating_context_role_assignments"> | OperatingContextRealtimeChange<IOperatingContextEquipmentRequirement, "operating_context_equipment_requirements"> | OperatingContextRealtimeChange<IOperatingContextRegion, "operating_context_regions"> | OperatingContextRealtimeChange<IOperatingContextRisk, "operating_context_risks">;
10
+ export type OperatingContextRealtimeData = OperatingContextRealtimeChange<IOperatingContext, "operating_contexts"> | OperatingContextRealtimeChange<IOperatingContextDevice, "operating_contexts_devices"> | OperatingContextRealtimeChange<IOperatingContextPayload, "operating_context_payloads"> | OperatingContextRealtimeChange<IOperatingContextRoleAssignment, "operating_context_role_assignments"> | OperatingContextRealtimeChange<IOperatingContextEquipmentRequirement, "operating_context_equipment_requirements"> | OperatingContextRealtimeChange<IOperatingContextRegion, "operating_context_regions"> | OperatingContextRealtimeChange<IOperatingContextRisk, "operating_context_risks">;
11
11
  export declare function useScoutRealtimeOperatingContexts(scoutSupabase: SupabaseClient<Database>, options?: ScoutRealtimeBroadcastOptions): [OperatingContextRealtimeData | null, () => void];
12
12
  export {};
@@ -4,6 +4,7 @@ import { useSelector } from "react-redux";
4
4
  import { useScoutRealtimeBroadcast, } from "./useScoutRealtimeBroadcast";
5
5
  const TABLE_MARKERS = [
6
6
  ["operating_contexts", "operating_context_type_id"],
7
+ ["operating_contexts_devices", "device_id"],
7
8
  ["operating_context_payloads", "payload_type"],
8
9
  ["operating_context_role_assignments", "membership_id"],
9
10
  ["operating_context_equipment_requirements", "equipment_item_id"],
package/dist/index.d.ts CHANGED
@@ -62,6 +62,7 @@ export * from "./helpers/operators";
62
62
  export * from "./helpers/versions_software";
63
63
  export * from "./helpers/versions_software_server";
64
64
  export * from "./helpers/parts";
65
+ export * from "./helpers/sensor_poses";
65
66
  export * from "./helpers/manufacturers";
66
67
  export * from "./helpers/products";
67
68
  export * from "./helpers/product_numbers";
package/dist/index.js CHANGED
@@ -65,6 +65,7 @@ export * from "./helpers/operators";
65
65
  export * from "./helpers/versions_software";
66
66
  export * from "./helpers/versions_software_server";
67
67
  export * from "./helpers/parts";
68
+ export * from "./helpers/sensor_poses";
68
69
  export * from "./helpers/manufacturers";
69
70
  export * from "./helpers/products";
70
71
  export * from "./helpers/product_numbers";
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). */
@@ -79,6 +80,7 @@ export type IOperatingContextEquipmentItem = Database["public"]["Tables"]["opera
79
80
  export type IOperatingContext = Database["public"]["Tables"]["operating_contexts"]["Row"];
80
81
  export type IOperatingContextPayload = Database["public"]["Tables"]["operating_context_payloads"]["Row"];
81
82
  export type IOperatingContextRoleAssignment = Database["public"]["Tables"]["operating_context_role_assignments"]["Row"];
83
+ export type IOperatingContextDevice = Database["public"]["Tables"]["operating_contexts_devices"]["Row"];
82
84
  export type IOperatingContextEquipmentRequirement = Database["public"]["Tables"]["operating_context_equipment_requirements"]["Row"];
83
85
  export type IOperatingContextRegion = Database["public"]["Tables"]["operating_context_regions"]["Row"];
84
86
  export type IOperatingContextRisk = Database["public"]["Tables"]["operating_context_risks"]["Row"];
@@ -119,6 +121,8 @@ export type OperatingContextPayloadCreateInput = Pick<OperatingContextPayloadIns
119
121
  export type OperatingContextPayloadUpdateInput = Partial<Omit<OperatingContextPayloadCreateInput, "operating_context_id">> & LifecyclePatch;
120
122
  export type OperatingContextRoleAssignmentCreateInput = Pick<OperatingContextRoleAssignmentInsert, "operating_context_id" | "membership_id" | "role_id" | "herd_id">;
121
123
  export type OperatingContextRoleAssignmentUpdateInput = Partial<Pick<OperatingContextRoleAssignmentCreateInput, "membership_id" | "role_id">> & LifecyclePatch;
124
+ export type OperatingContextDeviceCreateInput = Pick<OperatingContextDeviceInsert, "operating_context_id" | "device_id" | "herd_id">;
125
+ export type OperatingContextDeviceUpdateInput = Partial<Pick<OperatingContextDeviceCreateInput, "device_id">> & LifecyclePatch;
122
126
  export type OperatingContextEquipmentRequirementCreateInput = Pick<OperatingContextEquipmentRequirementInsert, "operating_context_id" | "equipment_item_id">;
123
127
  export type OperatingContextEquipmentRequirementUpdateInput = Partial<Pick<OperatingContextEquipmentRequirementCreateInput, "equipment_item_id">> & LifecyclePatch;
124
128
  export interface OperatingContextRegionCreateInput {
@@ -148,6 +152,8 @@ export type IAnalysisJob = Database["public"]["Tables"]["analysis_jobs"]["Row"];
148
152
  export type IAnalysisTask = Database["public"]["Tables"]["analysis_tasks"]["Row"];
149
153
  export type IArtifactWithMediaUrl = IArtifact & {
150
154
  media_url?: string | null;
155
+ thumbnail_url?: string | null;
156
+ proxy_url?: string | null;
151
157
  };
152
158
  export type IEventWithMediaUrl = IEvent & {
153
159
  media_url?: string | null;
@@ -222,6 +228,8 @@ export type OperatingContextPayloadInsert = Database["public"]["Tables"]["operat
222
228
  export type OperatingContextPayloadUpdate = Database["public"]["Tables"]["operating_context_payloads"]["Update"];
223
229
  export type OperatingContextRoleAssignmentInsert = Database["public"]["Tables"]["operating_context_role_assignments"]["Insert"];
224
230
  export type OperatingContextRoleAssignmentUpdate = Database["public"]["Tables"]["operating_context_role_assignments"]["Update"];
231
+ export type OperatingContextDeviceInsert = Database["public"]["Tables"]["operating_contexts_devices"]["Insert"];
232
+ export type OperatingContextDeviceUpdate = Database["public"]["Tables"]["operating_contexts_devices"]["Update"];
225
233
  export type OperatingContextEquipmentRequirementInsert = Database["public"]["Tables"]["operating_context_equipment_requirements"]["Insert"];
226
234
  export type OperatingContextEquipmentRequirementUpdate = Database["public"]["Tables"]["operating_context_equipment_requirements"]["Update"];
227
235
  export type OperatingContextRegionInsert = Database["public"]["Tables"]["operating_context_regions"]["Insert"];
@@ -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;
@@ -2681,6 +2693,64 @@ export type Database = {
2681
2693
  }
2682
2694
  ];
2683
2695
  };
2696
+ operating_contexts_devices: {
2697
+ Row: {
2698
+ device_id: number;
2699
+ herd_id: number;
2700
+ id: number;
2701
+ inserted_at: string;
2702
+ lifecycle: Database["public"]["Enums"]["entity_lifecycle"];
2703
+ lifecycle_changed_at: string;
2704
+ lifecycle_changed_by: string | null;
2705
+ lifecycle_reason: string | null;
2706
+ operating_context_id: number;
2707
+ };
2708
+ Insert: {
2709
+ device_id: number;
2710
+ herd_id: number;
2711
+ id?: number;
2712
+ inserted_at?: string;
2713
+ lifecycle?: Database["public"]["Enums"]["entity_lifecycle"];
2714
+ lifecycle_changed_at?: string;
2715
+ lifecycle_changed_by?: string | null;
2716
+ lifecycle_reason?: string | null;
2717
+ operating_context_id: number;
2718
+ };
2719
+ Update: {
2720
+ device_id?: number;
2721
+ herd_id?: number;
2722
+ id?: number;
2723
+ inserted_at?: string;
2724
+ lifecycle?: Database["public"]["Enums"]["entity_lifecycle"];
2725
+ lifecycle_changed_at?: string;
2726
+ lifecycle_changed_by?: string | null;
2727
+ lifecycle_reason?: string | null;
2728
+ operating_context_id?: number;
2729
+ };
2730
+ Relationships: [
2731
+ {
2732
+ foreignKeyName: "operating_context_device_context_fkey";
2733
+ columns: ["operating_context_id", "herd_id"];
2734
+ isOneToOne: false;
2735
+ referencedRelation: "operating_contexts";
2736
+ referencedColumns: ["id", "herd_id"];
2737
+ },
2738
+ {
2739
+ foreignKeyName: "operating_context_device_device_fkey";
2740
+ columns: ["device_id"];
2741
+ isOneToOne: false;
2742
+ referencedRelation: "devices";
2743
+ referencedColumns: ["id"];
2744
+ },
2745
+ {
2746
+ foreignKeyName: "operating_contexts_devices_lifecycle_changed_by_fkey";
2747
+ columns: ["lifecycle_changed_by"];
2748
+ isOneToOne: false;
2749
+ referencedRelation: "users";
2750
+ referencedColumns: ["id"];
2751
+ }
2752
+ ];
2753
+ };
2684
2754
  operators: {
2685
2755
  Row: {
2686
2756
  action: string | null;
@@ -4231,9 +4301,13 @@ export type Database = {
4231
4301
  lifecycle_changed_by: string | null;
4232
4302
  lifecycle_reason: string | null;
4233
4303
  modality: string | null;
4304
+ proxy_file_path: string | null;
4305
+ proxy_generated_at: string | null;
4234
4306
  segmented_at: string | null;
4235
4307
  session_id: number | null;
4236
4308
  tagged_at: string | null;
4309
+ thumbnail_file_path: string | null;
4310
+ thumbnail_generated_at: string | null;
4237
4311
  timestamp_observation: string | null;
4238
4312
  timestamp_observation_end: string;
4239
4313
  tracked_at: string | null;
@@ -4263,9 +4337,13 @@ export type Database = {
4263
4337
  lifecycle_changed_by: string | null;
4264
4338
  lifecycle_reason: string | null;
4265
4339
  modality: string | null;
4340
+ proxy_file_path: string | null;
4341
+ proxy_generated_at: string | null;
4266
4342
  segmented_at: string | null;
4267
4343
  session_id: number | null;
4268
4344
  tagged_at: string | null;
4345
+ thumbnail_file_path: string | null;
4346
+ thumbnail_generated_at: string | null;
4269
4347
  timestamp_observation: string | null;
4270
4348
  timestamp_observation_end: string;
4271
4349
  tracked_at: string | null;
@@ -4296,9 +4374,13 @@ export type Database = {
4296
4374
  lifecycle_changed_by: string | null;
4297
4375
  lifecycle_reason: string | null;
4298
4376
  modality: string | null;
4377
+ proxy_file_path: string | null;
4378
+ proxy_generated_at: string | null;
4299
4379
  segmented_at: string | null;
4300
4380
  session_id: number | null;
4301
4381
  tagged_at: string | null;
4382
+ thumbnail_file_path: string | null;
4383
+ thumbnail_generated_at: string | null;
4302
4384
  timestamp_observation: string | null;
4303
4385
  timestamp_observation_end: string;
4304
4386
  tracked_at: string | null;
@@ -4329,9 +4411,13 @@ export type Database = {
4329
4411
  lifecycle_changed_by: string | null;
4330
4412
  lifecycle_reason: string | null;
4331
4413
  modality: string | null;
4414
+ proxy_file_path: string | null;
4415
+ proxy_generated_at: string | null;
4332
4416
  segmented_at: string | null;
4333
4417
  session_id: number | null;
4334
4418
  tagged_at: string | null;
4419
+ thumbnail_file_path: string | null;
4420
+ thumbnail_generated_at: string | null;
4335
4421
  timestamp_observation: string | null;
4336
4422
  timestamp_observation_end: string;
4337
4423
  tracked_at: string | null;
@@ -4363,9 +4449,13 @@ export type Database = {
4363
4449
  lifecycle_changed_by: string | null;
4364
4450
  lifecycle_reason: string | null;
4365
4451
  modality: string | null;
4452
+ proxy_file_path: string | null;
4453
+ proxy_generated_at: string | null;
4366
4454
  segmented_at: string | null;
4367
4455
  session_id: number | null;
4368
4456
  tagged_at: string | null;
4457
+ thumbnail_file_path: string | null;
4458
+ thumbnail_generated_at: string | null;
4369
4459
  timestamp_observation: string | null;
4370
4460
  timestamp_observation_end: string;
4371
4461
  tracked_at: string | null;
@@ -4397,9 +4487,13 @@ export type Database = {
4397
4487
  lifecycle_changed_by: string | null;
4398
4488
  lifecycle_reason: string | null;
4399
4489
  modality: string | null;
4490
+ proxy_file_path: string | null;
4491
+ proxy_generated_at: string | null;
4400
4492
  segmented_at: string | null;
4401
4493
  session_id: number | null;
4402
4494
  tagged_at: string | null;
4495
+ thumbnail_file_path: string | null;
4496
+ thumbnail_generated_at: string | null;
4403
4497
  timestamp_observation: string | null;
4404
4498
  timestamp_observation_end: string;
4405
4499
  tracked_at: string | null;
@@ -4432,9 +4526,13 @@ export type Database = {
4432
4526
  lifecycle_changed_by: string | null;
4433
4527
  lifecycle_reason: string | null;
4434
4528
  modality: string | null;
4529
+ proxy_file_path: string | null;
4530
+ proxy_generated_at: string | null;
4435
4531
  segmented_at: string | null;
4436
4532
  session_id: number | null;
4437
4533
  tagged_at: string | null;
4534
+ thumbnail_file_path: string | null;
4535
+ thumbnail_generated_at: string | null;
4438
4536
  timestamp_observation: string | null;
4439
4537
  timestamp_observation_end: string;
4440
4538
  tracked_at: string | null;
@@ -4466,9 +4564,13 @@ export type Database = {
4466
4564
  lifecycle_changed_by: string | null;
4467
4565
  lifecycle_reason: string | null;
4468
4566
  modality: string | null;
4567
+ proxy_file_path: string | null;
4568
+ proxy_generated_at: string | null;
4469
4569
  segmented_at: string | null;
4470
4570
  session_id: number | null;
4471
4571
  tagged_at: string | null;
4572
+ thumbnail_file_path: string | null;
4573
+ thumbnail_generated_at: string | null;
4472
4574
  timestamp_observation: string | null;
4473
4575
  timestamp_observation_end: string;
4474
4576
  tracked_at: string | null;
@@ -4501,9 +4603,13 @@ export type Database = {
4501
4603
  lifecycle_changed_by: string | null;
4502
4604
  lifecycle_reason: string | null;
4503
4605
  modality: string | null;
4606
+ proxy_file_path: string | null;
4607
+ proxy_generated_at: string | null;
4504
4608
  segmented_at: string | null;
4505
4609
  session_id: number | null;
4506
4610
  tagged_at: string | null;
4611
+ thumbnail_file_path: string | null;
4612
+ thumbnail_generated_at: string | null;
4507
4613
  timestamp_observation: string | null;
4508
4614
  timestamp_observation_end: string;
4509
4615
  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.3",
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",