@adventurelabs/scout-core 1.4.19 → 1.4.21

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.
@@ -9,3 +9,5 @@ export declare function server_update_connectivity(connectivity: (ConnectivityUp
9
9
  }) | (ConnectivityUpdate & {
10
10
  id: number;
11
11
  })[], client?: SupabaseClient): Promise<IWebResponseCompatible<IConnectivity[]>>;
12
+ /** Connectivity records for an artifact (uses session when set, else device + time window). */
13
+ export declare function server_get_connectivity_for_artifact(artifactId: number, maxElements?: number): Promise<IWebResponseCompatible<IConnectivityWithCoordinates[]>>;
@@ -96,3 +96,26 @@ export async function server_update_connectivity(connectivity, client) {
96
96
  }
97
97
  return IWebResponse.success(updatedConnectivity).to_compatible();
98
98
  }
99
+ /** Connectivity records for an artifact (uses session when set, else device + time window). */
100
+ export async function server_get_connectivity_for_artifact(artifactId, maxElements = 1000) {
101
+ const supabase = await newServerClient();
102
+ const { data, error } = await supabase.rpc("get_connectivity_for_artifact", {
103
+ artifact_id_caller: artifactId,
104
+ max_elements_caller: maxElements,
105
+ });
106
+ if (error) {
107
+ console.warn("Error fetching connectivity for artifact:", error.message);
108
+ return {
109
+ status: EnumWebResponse.ERROR,
110
+ msg: error.message,
111
+ data: [],
112
+ };
113
+ }
114
+ const sortedConnectivity = (data || []).sort((a, b) => {
115
+ if (!a.timestamp_start || !b.timestamp_start)
116
+ return 0;
117
+ return (new Date(a.timestamp_start).getTime() -
118
+ new Date(b.timestamp_start).getTime());
119
+ });
120
+ return IWebResponse.success(sortedConnectivity).to_compatible();
121
+ }
@@ -11,3 +11,5 @@ export declare function server_update_session(sessions: (SessionUpdate & {
11
11
  }) | (SessionUpdate & {
12
12
  id: number;
13
13
  })[], client?: SupabaseClient): Promise<IWebResponseCompatible<ISession[]>>;
14
+ /** Artifact IDs for a session (same device, overlapping session interval). Empty if session or timestamps missing. */
15
+ export declare function server_get_artifact_ids_for_session(sessionId: number, client?: SupabaseClient): Promise<IWebResponseCompatible<number[]>>;
@@ -119,3 +119,19 @@ export async function server_update_session(sessions, client) {
119
119
  }
120
120
  return IWebResponse.success(updatedSessions).to_compatible();
121
121
  }
122
+ /** Artifact IDs for a session (same device, overlapping session interval). Empty if session or timestamps missing. */
123
+ export async function server_get_artifact_ids_for_session(sessionId, client) {
124
+ const supabase = client || (await newServerClient());
125
+ const { data, error } = await supabase.rpc("get_artifact_ids_for_session", {
126
+ session_id_caller: sessionId,
127
+ });
128
+ if (error) {
129
+ console.warn("Error fetching artifact IDs for session:", error.message);
130
+ return {
131
+ status: EnumWebResponse.ERROR,
132
+ msg: error.message,
133
+ data: [],
134
+ };
135
+ }
136
+ return IWebResponse.success(Array.isArray(data) ? data : []).to_compatible();
137
+ }
@@ -1,4 +1,4 @@
1
- import { IEventAndTagsPrettyLocation, ITag } from "../types/db";
1
+ import { IEventAndTagsPrettyLocation, ITag, ITagPrettyLocation } from "../types/db";
2
2
  import { IWebResponseCompatible } from "../types/requests";
3
3
  export declare function test_event_loading(device_id: number): Promise<boolean>;
4
4
  export declare function server_create_tags(tags: ITag[]): Promise<IWebResponseCompatible<ITag[]>>;
@@ -10,3 +10,9 @@ export declare function server_get_events_and_tags_for_devices_batch(device_ids:
10
10
  [device_id: number]: IEventAndTagsPrettyLocation[];
11
11
  }>>;
12
12
  export declare function get_event_and_tags_by_event_id(event_id: number): Promise<IWebResponseCompatible<IEventAndTagsPrettyLocation>>;
13
+ /** Tags for an artifact with optional timestamp range and max items. */
14
+ export declare function server_get_tags_for_artifact(artifactId: number, options?: {
15
+ start_timestamp?: string | null;
16
+ end_timestamp?: string | null;
17
+ max_elements?: number;
18
+ }): Promise<IWebResponseCompatible<ITagPrettyLocation[]>>;
@@ -450,3 +450,26 @@ export async function get_event_and_tags_by_event_id(event_id) {
450
450
  }
451
451
  return IWebResponse.success(eventWithSignedUrl).to_compatible();
452
452
  }
453
+ /** Tags for an artifact with optional timestamp range and max items. */
454
+ export async function server_get_tags_for_artifact(artifactId, options) {
455
+ const supabase = await newServerClient();
456
+ const { data, error } = await supabase.rpc("get_tags_for_artifact", {
457
+ artifact_id_caller: artifactId,
458
+ max_elements_caller: options?.max_elements ?? 1000,
459
+ ...(options?.start_timestamp != null && {
460
+ start_timestamp_caller: options.start_timestamp,
461
+ }),
462
+ ...(options?.end_timestamp != null && {
463
+ end_timestamp_caller: options.end_timestamp,
464
+ }),
465
+ });
466
+ if (error) {
467
+ console.warn("Error fetching tags for artifact:", error.message);
468
+ return {
469
+ status: EnumWebResponse.ERROR,
470
+ msg: error.message,
471
+ data: [],
472
+ };
473
+ }
474
+ return IWebResponse.success(data ?? []).to_compatible();
475
+ }
@@ -418,6 +418,7 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
418
418
  const [pages, setPages] = useState([]);
419
419
  const [currentCursor, setCurrentCursor] = useState(null);
420
420
  const prevHerdIdRef = useRef();
421
+ const lastAddedCursorRef = useRef(undefined);
421
422
  const currentQuery = useGetFeedInfiniteByHerdQuery({
422
423
  herdId,
423
424
  limit: options.limit || 20,
@@ -431,22 +432,29 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
431
432
  herdId) {
432
433
  setPages([]);
433
434
  setCurrentCursor(null);
435
+ lastAddedCursorRef.current = undefined;
434
436
  }
435
437
  prevHerdIdRef.current = herdId;
436
438
  }, [herdId, options.enabled]);
437
439
  useEffect(() => {
438
- if (currentQuery.data && !currentQuery.isLoading) {
439
- setPages((prev) => {
440
- const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
441
- if (!existingPage) {
442
- return [
443
- ...prev,
444
- { cursor: currentCursor, data: currentQuery.data.items },
445
- ];
446
- }
447
- return prev;
448
- });
449
- }
440
+ if (!currentQuery.data || currentQuery.isLoading)
441
+ return;
442
+ if (feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
443
+ return;
444
+ setPages((prev) => {
445
+ const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
446
+ if (!existingPage) {
447
+ const items = Array.isArray(currentQuery.data?.items)
448
+ ? currentQuery.data.items
449
+ : [];
450
+ lastAddedCursorRef.current = currentCursor;
451
+ return [
452
+ ...prev,
453
+ { cursor: currentCursor, data: items },
454
+ ];
455
+ }
456
+ return prev;
457
+ });
450
458
  }, [currentQuery.data, currentQuery.isLoading, currentCursor]);
451
459
  const loadMore = useCallback(() => {
452
460
  if (currentQuery.data?.hasMore &&
@@ -458,9 +466,19 @@ export const useInfiniteFeedByHerd = (herdId, options) => {
458
466
  const refetch = useCallback(() => {
459
467
  setPages([]);
460
468
  setCurrentCursor(null);
469
+ lastAddedCursorRef.current = undefined;
461
470
  currentQuery.refetch();
462
471
  }, [currentQuery]);
463
- const allItems = useMemo(() => pages.flatMap((p) => p.data), [pages]);
472
+ const allItems = useMemo(() => {
473
+ const seen = new Set();
474
+ return pages.flatMap((p) => p.data).filter((item) => {
475
+ const key = `${item.sort_ts ?? ''}_${item.sort_id ?? ''}_${item.feed_type ?? ''}`;
476
+ if (seen.has(key))
477
+ return false;
478
+ seen.add(key);
479
+ return true;
480
+ });
481
+ }, [pages]);
464
482
  return {
465
483
  items: allItems,
466
484
  isLoading: currentQuery.isLoading && pages.length === 0,
@@ -475,6 +493,7 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
475
493
  const [pages, setPages] = useState([]);
476
494
  const [currentCursor, setCurrentCursor] = useState(null);
477
495
  const prevDeviceIdRef = useRef();
496
+ const lastAddedCursorRef = useRef(undefined);
478
497
  const currentQuery = useGetFeedInfiniteByDeviceQuery({
479
498
  deviceId,
480
499
  limit: options.limit || 20,
@@ -488,22 +507,29 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
488
507
  deviceId) {
489
508
  setPages([]);
490
509
  setCurrentCursor(null);
510
+ lastAddedCursorRef.current = undefined;
491
511
  }
492
512
  prevDeviceIdRef.current = deviceId;
493
513
  }, [deviceId, options.enabled]);
494
514
  useEffect(() => {
495
- if (currentQuery.data && !currentQuery.isLoading) {
496
- setPages((prev) => {
497
- const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
498
- if (!existingPage) {
499
- return [
500
- ...prev,
501
- { cursor: currentCursor, data: currentQuery.data.items },
502
- ];
503
- }
504
- return prev;
505
- });
506
- }
515
+ if (!currentQuery.data || currentQuery.isLoading)
516
+ return;
517
+ if (feedCursorEq(lastAddedCursorRef.current ?? null, currentCursor))
518
+ return;
519
+ setPages((prev) => {
520
+ const existingPage = prev.find((p) => feedCursorEq(p.cursor, currentCursor));
521
+ if (!existingPage) {
522
+ const items = Array.isArray(currentQuery.data?.items)
523
+ ? currentQuery.data.items
524
+ : [];
525
+ lastAddedCursorRef.current = currentCursor;
526
+ return [
527
+ ...prev,
528
+ { cursor: currentCursor, data: items },
529
+ ];
530
+ }
531
+ return prev;
532
+ });
507
533
  }, [currentQuery.data, currentQuery.isLoading, currentCursor]);
508
534
  const loadMore = useCallback(() => {
509
535
  if (currentQuery.data?.hasMore &&
@@ -515,9 +541,19 @@ export const useInfiniteFeedByDevice = (deviceId, options) => {
515
541
  const refetch = useCallback(() => {
516
542
  setPages([]);
517
543
  setCurrentCursor(null);
544
+ lastAddedCursorRef.current = undefined;
518
545
  currentQuery.refetch();
519
546
  }, [currentQuery]);
520
- const allItems = useMemo(() => pages.flatMap((p) => p.data), [pages]);
547
+ const allItems = useMemo(() => {
548
+ const seen = new Set();
549
+ return pages.flatMap((p) => p.data).filter((item) => {
550
+ const key = `${item.sort_ts ?? ''}_${item.sort_id ?? ''}_${item.feed_type ?? ''}`;
551
+ if (seen.has(key))
552
+ return false;
553
+ seen.add(key);
554
+ return true;
555
+ });
556
+ }, [pages]);
521
557
  return {
522
558
  items: allItems,
523
559
  isLoading: currentQuery.isLoading && pages.length === 0,
@@ -58,6 +58,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
58
58
  id: number;
59
59
  modality: string | null;
60
60
  session_id: number | null;
61
+ tagged_at: string | null;
61
62
  timestamp_observation: string | null;
62
63
  timestamp_observation_end: string;
63
64
  updated_at: string | null;
@@ -72,6 +73,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
72
73
  id?: number;
73
74
  modality?: string | null;
74
75
  session_id?: number | null;
76
+ tagged_at?: string | null;
75
77
  timestamp_observation?: string | null;
76
78
  timestamp_observation_end?: string;
77
79
  updated_at?: string | null;
@@ -86,6 +88,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
86
88
  id?: number;
87
89
  modality?: string | null;
88
90
  session_id?: number | null;
91
+ tagged_at?: string | null;
89
92
  timestamp_observation?: string | null;
90
93
  timestamp_observation_end?: string;
91
94
  updated_at?: string | null;
@@ -1341,6 +1344,12 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1341
1344
  status: string;
1342
1345
  }[];
1343
1346
  };
1347
+ get_artifact_ids_for_session: {
1348
+ Args: {
1349
+ session_id_caller: number;
1350
+ };
1351
+ Returns: number[];
1352
+ };
1344
1353
  get_artifacts_for_device: {
1345
1354
  Args: {
1346
1355
  device_id_caller: number;
@@ -1357,6 +1366,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1357
1366
  id: number;
1358
1367
  modality: string | null;
1359
1368
  session_id: number | null;
1369
+ tagged_at: string | null;
1360
1370
  timestamp_observation: string | null;
1361
1371
  timestamp_observation_end: string;
1362
1372
  updated_at: string | null;
@@ -1383,6 +1393,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1383
1393
  id: number;
1384
1394
  modality: string | null;
1385
1395
  session_id: number | null;
1396
+ tagged_at: string | null;
1386
1397
  timestamp_observation: string | null;
1387
1398
  timestamp_observation_end: string;
1388
1399
  updated_at: string | null;
@@ -1410,6 +1421,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1410
1421
  id: number;
1411
1422
  modality: string | null;
1412
1423
  session_id: number | null;
1424
+ tagged_at: string | null;
1413
1425
  timestamp_observation: string | null;
1414
1426
  timestamp_observation_end: string;
1415
1427
  updated_at: string | null;
@@ -1437,6 +1449,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1437
1449
  id: number;
1438
1450
  modality: string | null;
1439
1451
  session_id: number | null;
1452
+ tagged_at: string | null;
1440
1453
  timestamp_observation: string | null;
1441
1454
  timestamp_observation_end: string;
1442
1455
  updated_at: string | null;
@@ -1465,6 +1478,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1465
1478
  id: number;
1466
1479
  modality: string | null;
1467
1480
  session_id: number | null;
1481
+ tagged_at: string | null;
1468
1482
  timestamp_observation: string | null;
1469
1483
  timestamp_observation_end: string;
1470
1484
  updated_at: string | null;
@@ -1493,6 +1507,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1493
1507
  id: number;
1494
1508
  modality: string | null;
1495
1509
  session_id: number | null;
1510
+ tagged_at: string | null;
1496
1511
  timestamp_observation: string | null;
1497
1512
  timestamp_observation_end: string;
1498
1513
  updated_at: string | null;
@@ -1521,6 +1536,7 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1521
1536
  id: number;
1522
1537
  modality: string | null;
1523
1538
  session_id: number | null;
1539
+ tagged_at: string | null;
1524
1540
  timestamp_observation: string | null;
1525
1541
  timestamp_observation_end: string;
1526
1542
  updated_at: string | null;
@@ -1532,6 +1548,19 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1532
1548
  isSetofReturn: true;
1533
1549
  };
1534
1550
  };
1551
+ get_connectivity_for_artifact: {
1552
+ Args: {
1553
+ artifact_id_caller: number;
1554
+ max_elements_caller?: number;
1555
+ };
1556
+ Returns: Database["public"]["CompositeTypes"]["connectivity_with_coordinates"][];
1557
+ SetofOptions: {
1558
+ from: "*";
1559
+ to: "connectivity_with_coordinates";
1560
+ isOneToOne: false;
1561
+ isSetofReturn: true;
1562
+ };
1563
+ };
1535
1564
  get_connectivity_with_coordinates: {
1536
1565
  Args: {
1537
1566
  session_id_caller: number;
@@ -1864,6 +1893,21 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
1864
1893
  isSetofReturn: true;
1865
1894
  };
1866
1895
  };
1896
+ get_tags_for_artifact: {
1897
+ Args: {
1898
+ artifact_id_caller: number;
1899
+ end_timestamp_caller?: string;
1900
+ max_elements_caller?: number;
1901
+ start_timestamp_caller?: string;
1902
+ };
1903
+ Returns: Database["public"]["CompositeTypes"]["tags_pretty_location"][];
1904
+ SetofOptions: {
1905
+ from: "*";
1906
+ to: "tags_pretty_location";
1907
+ isOneToOne: false;
1908
+ isSetofReturn: true;
1909
+ };
1910
+ };
1867
1911
  get_total_artifacts_for_herd: {
1868
1912
  Args: {
1869
1913
  herd_id_caller: number;
@@ -57,6 +57,7 @@ export type Database = {
57
57
  id: number;
58
58
  modality: string | null;
59
59
  session_id: number | null;
60
+ tagged_at: string | null;
60
61
  timestamp_observation: string | null;
61
62
  timestamp_observation_end: string;
62
63
  updated_at: string | null;
@@ -71,6 +72,7 @@ export type Database = {
71
72
  id?: number;
72
73
  modality?: string | null;
73
74
  session_id?: number | null;
75
+ tagged_at?: string | null;
74
76
  timestamp_observation?: string | null;
75
77
  timestamp_observation_end?: string;
76
78
  updated_at?: string | null;
@@ -85,6 +87,7 @@ export type Database = {
85
87
  id?: number;
86
88
  modality?: string | null;
87
89
  session_id?: number | null;
90
+ tagged_at?: string | null;
88
91
  timestamp_observation?: string | null;
89
92
  timestamp_observation_end?: string;
90
93
  updated_at?: string | null;
@@ -1408,6 +1411,12 @@ export type Database = {
1408
1411
  status: string;
1409
1412
  }[];
1410
1413
  };
1414
+ get_artifact_ids_for_session: {
1415
+ Args: {
1416
+ session_id_caller: number;
1417
+ };
1418
+ Returns: number[];
1419
+ };
1411
1420
  get_artifacts_for_device: {
1412
1421
  Args: {
1413
1422
  device_id_caller: number;
@@ -1424,6 +1433,7 @@ export type Database = {
1424
1433
  id: number;
1425
1434
  modality: string | null;
1426
1435
  session_id: number | null;
1436
+ tagged_at: string | null;
1427
1437
  timestamp_observation: string | null;
1428
1438
  timestamp_observation_end: string;
1429
1439
  updated_at: string | null;
@@ -1450,6 +1460,7 @@ export type Database = {
1450
1460
  id: number;
1451
1461
  modality: string | null;
1452
1462
  session_id: number | null;
1463
+ tagged_at: string | null;
1453
1464
  timestamp_observation: string | null;
1454
1465
  timestamp_observation_end: string;
1455
1466
  updated_at: string | null;
@@ -1477,6 +1488,7 @@ export type Database = {
1477
1488
  id: number;
1478
1489
  modality: string | null;
1479
1490
  session_id: number | null;
1491
+ tagged_at: string | null;
1480
1492
  timestamp_observation: string | null;
1481
1493
  timestamp_observation_end: string;
1482
1494
  updated_at: string | null;
@@ -1504,6 +1516,7 @@ export type Database = {
1504
1516
  id: number;
1505
1517
  modality: string | null;
1506
1518
  session_id: number | null;
1519
+ tagged_at: string | null;
1507
1520
  timestamp_observation: string | null;
1508
1521
  timestamp_observation_end: string;
1509
1522
  updated_at: string | null;
@@ -1532,6 +1545,7 @@ export type Database = {
1532
1545
  id: number;
1533
1546
  modality: string | null;
1534
1547
  session_id: number | null;
1548
+ tagged_at: string | null;
1535
1549
  timestamp_observation: string | null;
1536
1550
  timestamp_observation_end: string;
1537
1551
  updated_at: string | null;
@@ -1560,6 +1574,7 @@ export type Database = {
1560
1574
  id: number;
1561
1575
  modality: string | null;
1562
1576
  session_id: number | null;
1577
+ tagged_at: string | null;
1563
1578
  timestamp_observation: string | null;
1564
1579
  timestamp_observation_end: string;
1565
1580
  updated_at: string | null;
@@ -1588,6 +1603,7 @@ export type Database = {
1588
1603
  id: number;
1589
1604
  modality: string | null;
1590
1605
  session_id: number | null;
1606
+ tagged_at: string | null;
1591
1607
  timestamp_observation: string | null;
1592
1608
  timestamp_observation_end: string;
1593
1609
  updated_at: string | null;
@@ -1599,6 +1615,19 @@ export type Database = {
1599
1615
  isSetofReturn: true;
1600
1616
  };
1601
1617
  };
1618
+ get_connectivity_for_artifact: {
1619
+ Args: {
1620
+ artifact_id_caller: number;
1621
+ max_elements_caller?: number;
1622
+ };
1623
+ Returns: Database["public"]["CompositeTypes"]["connectivity_with_coordinates"][];
1624
+ SetofOptions: {
1625
+ from: "*";
1626
+ to: "connectivity_with_coordinates";
1627
+ isOneToOne: false;
1628
+ isSetofReturn: true;
1629
+ };
1630
+ };
1602
1631
  get_connectivity_with_coordinates: {
1603
1632
  Args: {
1604
1633
  session_id_caller: number;
@@ -1931,6 +1960,21 @@ export type Database = {
1931
1960
  isSetofReturn: true;
1932
1961
  };
1933
1962
  };
1963
+ get_tags_for_artifact: {
1964
+ Args: {
1965
+ artifact_id_caller: number;
1966
+ end_timestamp_caller?: string;
1967
+ max_elements_caller?: number;
1968
+ start_timestamp_caller?: string;
1969
+ };
1970
+ Returns: Database["public"]["CompositeTypes"]["tags_pretty_location"][];
1971
+ SetofOptions: {
1972
+ from: "*";
1973
+ to: "tags_pretty_location";
1974
+ isOneToOne: false;
1975
+ isSetofReturn: true;
1976
+ };
1977
+ };
1934
1978
  get_total_artifacts_for_herd: {
1935
1979
  Args: {
1936
1980
  herd_id_caller: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.4.19",
3
+ "version": "1.4.21",
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",