@adventurelabs/scout-core 1.4.57 → 1.4.58
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.
- package/dist/constants/db.d.ts +2 -0
- package/dist/constants/db.js +2 -1
- package/dist/helpers/deviceTransfer.d.ts +2 -0
- package/dist/helpers/deviceTransfer.js +14 -0
- package/dist/helpers/storage.d.ts +5 -7
- package/dist/helpers/storage.js +18 -54
- package/dist/providers/ScoutRefreshProvider.d.ts +7 -0
- package/dist/store/api.js +36 -38
- package/dist/types/supabase.d.ts +7 -0
- package/package.json +3 -2
package/dist/constants/db.d.ts
CHANGED
package/dist/constants/db.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export const BUCKET_NAME_SCOUT = "scout";
|
|
2
|
-
|
|
2
|
+
/** Event media when `file_path` uses the `events/...` prefix */
|
|
3
|
+
export const BUCKET_NAME_EVENTS = "events";
|
|
3
4
|
export const SIGNED_URL_EXPIRATION_SECONDS = 12 * 60 * 60;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { newServerClient } from "../supabase/server";
|
|
3
|
+
import { IWebResponse } from "../types/requests";
|
|
4
|
+
export async function transferDeviceToHerd(deviceId, targetHerdId) {
|
|
5
|
+
const client = await newServerClient();
|
|
6
|
+
const { error } = await client.rpc("transfer_device_to_herd", {
|
|
7
|
+
p_device_id: deviceId,
|
|
8
|
+
p_target_herd_id: targetHerdId,
|
|
9
|
+
});
|
|
10
|
+
if (error) {
|
|
11
|
+
return IWebResponse.error(error.message).to_compatible();
|
|
12
|
+
}
|
|
13
|
+
return IWebResponse.success(true).to_compatible();
|
|
14
|
+
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
2
|
import { Database } from "../types/supabase";
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* @returns Promise<string | null> - The signed URL or null if error
|
|
9
|
-
*/
|
|
3
|
+
/** DB `file_path` is always `bucket/object/...` (e.g. `events/254/...`, `artifacts/254/...`). */
|
|
4
|
+
export declare function parseStorageFilePath(filePath: string): {
|
|
5
|
+
bucket: string;
|
|
6
|
+
objectPath: string;
|
|
7
|
+
} | null;
|
|
10
8
|
export declare function generateSignedUrl(filePath: string, expiresIn?: number, supabaseClient?: SupabaseClient<Database>): Promise<string | null>;
|
|
11
9
|
export declare function generateSignedUrlsBatch(filePaths: string[], expiresIn?: number, supabaseClient?: SupabaseClient<Database>): Promise<(string | null)[]>;
|
package/dist/helpers/storage.js
CHANGED
|
@@ -1,64 +1,33 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
import { newServerClient } from "../supabase/server";
|
|
3
|
-
import { SIGNED_URL_EXPIRATION_SECONDS
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
*/
|
|
9
|
-
function getBucketFromFilePath(filePath) {
|
|
10
|
-
// delete any start or end slashes/whitespace
|
|
11
|
-
filePath = filePath.replace(/^\/+|\/+$/g, "");
|
|
12
|
-
const parts = filePath.split("/");
|
|
13
|
-
if (parts.length < 2) {
|
|
3
|
+
import { SIGNED_URL_EXPIRATION_SECONDS } from "../constants/db";
|
|
4
|
+
/** DB `file_path` is always `bucket/object/...` (e.g. `events/254/...`, `artifacts/254/...`). */
|
|
5
|
+
export function parseStorageFilePath(filePath) {
|
|
6
|
+
const cleaned = filePath.trim().replace(/^\/+|\/+$/g, "");
|
|
7
|
+
if (!cleaned) {
|
|
14
8
|
return null;
|
|
15
9
|
}
|
|
16
|
-
const bucket_name = parts[0];
|
|
17
|
-
return bucket_name;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Extracts the short path from a file path
|
|
21
|
-
* @param filePath
|
|
22
|
-
* @returns string | null - null if invalid file path
|
|
23
|
-
*/
|
|
24
|
-
// for example if the input is /artifacts/10/52/test.mp4 - the output shld be 10/52/test.mp4
|
|
25
|
-
function getFormattedPath(filePath) {
|
|
26
|
-
const cleaned = cleanPath(filePath);
|
|
27
10
|
const parts = cleaned.split("/");
|
|
28
11
|
if (parts.length < 2) {
|
|
29
12
|
return null;
|
|
30
13
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// delete leading/trailing slash and whitespace
|
|
37
|
-
return filePath.trim().replace(/^\/+|\/+$/g, "");
|
|
14
|
+
const bucket = parts[0];
|
|
15
|
+
if (/^[0-9]+$/.test(bucket)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return { bucket, objectPath: parts.slice(1).join("/") };
|
|
38
19
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Generates a signed URL for a file in Supabase storage
|
|
41
|
-
* @param filePath - The path to the file in storage (e.g., "events/123/image.jpg")
|
|
42
|
-
* @param expiresIn - Number of seconds until the URL expires (default: 12 hours)
|
|
43
|
-
* @param supabaseClient - Optional Supabase client (will create new one if not provided)
|
|
44
|
-
* @returns Promise<string | null> - The signed URL or null if error
|
|
45
|
-
*/
|
|
46
20
|
export async function generateSignedUrl(filePath, expiresIn = SIGNED_URL_EXPIRATION_SECONDS, supabaseClient) {
|
|
47
21
|
try {
|
|
48
22
|
const supabase = supabaseClient || (await newServerClient());
|
|
49
|
-
const
|
|
50
|
-
if (!
|
|
23
|
+
const parsed = parseStorageFilePath(filePath);
|
|
24
|
+
if (!parsed) {
|
|
51
25
|
console.error("Invalid file path:", filePath);
|
|
52
26
|
return null;
|
|
53
27
|
}
|
|
54
|
-
const formattedPath = getFormattedPath(filePath);
|
|
55
|
-
if (!formattedPath) {
|
|
56
|
-
console.error("Invalid formatted path:", formattedPath);
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
28
|
const { data, error } = await supabase.storage
|
|
60
|
-
.from(
|
|
61
|
-
.createSignedUrl(
|
|
29
|
+
.from(parsed.bucket)
|
|
30
|
+
.createSignedUrl(parsed.objectPath, expiresIn);
|
|
62
31
|
if (error) {
|
|
63
32
|
console.error("Error generating signed URL:", error.message);
|
|
64
33
|
return null;
|
|
@@ -75,19 +44,14 @@ export async function generateSignedUrlsBatch(filePaths, expiresIn = SIGNED_URL_
|
|
|
75
44
|
const supabase = supabaseClient || (await newServerClient());
|
|
76
45
|
const signedUrlPromises = filePaths.map(async (filePath) => {
|
|
77
46
|
try {
|
|
78
|
-
const
|
|
79
|
-
if (!
|
|
47
|
+
const parsed = parseStorageFilePath(filePath);
|
|
48
|
+
if (!parsed) {
|
|
80
49
|
console.error("Invalid file path:", filePath);
|
|
81
50
|
return null;
|
|
82
51
|
}
|
|
83
|
-
const formattedPath = getFormattedPath(filePath);
|
|
84
|
-
if (!formattedPath) {
|
|
85
|
-
console.error("Invalid formatted path:", formattedPath);
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
52
|
const { data, error } = await supabase.storage
|
|
89
|
-
.from(
|
|
90
|
-
.createSignedUrl(
|
|
53
|
+
.from(parsed.bucket)
|
|
54
|
+
.createSignedUrl(parsed.objectPath, expiresIn);
|
|
91
55
|
if (error) {
|
|
92
56
|
console.warn(`Error generating signed URL for ${filePath}:`, error.message);
|
|
93
57
|
return null;
|
|
@@ -2772,6 +2772,13 @@ export declare function useSupabase(): SupabaseClient<Database, "public", "publi
|
|
|
2772
2772
|
status: string;
|
|
2773
2773
|
}[];
|
|
2774
2774
|
};
|
|
2775
|
+
transfer_device_to_herd: {
|
|
2776
|
+
Args: {
|
|
2777
|
+
p_device_id: number;
|
|
2778
|
+
p_target_herd_id: number;
|
|
2779
|
+
};
|
|
2780
|
+
Returns: undefined;
|
|
2781
|
+
};
|
|
2775
2782
|
};
|
|
2776
2783
|
Enums: {
|
|
2777
2784
|
analysis_work_status: "waiting" | "cancelled" | "processing" | "failed" | "success";
|
package/dist/store/api.js
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
import { createApi, fakeBaseQuery } from "@reduxjs/toolkit/query/react";
|
|
2
2
|
import { generateSignedUrlsBatch } from "../helpers/storage";
|
|
3
|
+
async function signedUrlMapForFeedRows(rows) {
|
|
4
|
+
const seen = new Set();
|
|
5
|
+
const paths = [];
|
|
6
|
+
for (const row of rows) {
|
|
7
|
+
const ep = row.event_data?.file_path;
|
|
8
|
+
if (ep && !seen.has(ep)) {
|
|
9
|
+
seen.add(ep);
|
|
10
|
+
paths.push(ep);
|
|
11
|
+
}
|
|
12
|
+
const ap = row.artifact_data?.file_path;
|
|
13
|
+
if (ap && !seen.has(ap)) {
|
|
14
|
+
seen.add(ap);
|
|
15
|
+
paths.push(ap);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const map = new Map();
|
|
19
|
+
if (paths.length === 0) {
|
|
20
|
+
return map;
|
|
21
|
+
}
|
|
22
|
+
const r = await generateSignedUrlsBatch(paths);
|
|
23
|
+
paths.forEach((p, i) => {
|
|
24
|
+
if (r[i])
|
|
25
|
+
map.set(p, r[i]);
|
|
26
|
+
});
|
|
27
|
+
return map;
|
|
28
|
+
}
|
|
3
29
|
// Custom serialize function to exclude supabase client
|
|
4
30
|
const serializeQueryArgs = ({ queryArgs, endpointDefinition, endpointName, }) => {
|
|
5
31
|
const { supabase, ...serializableArgs } = queryArgs;
|
|
@@ -478,26 +504,12 @@ export const scoutApi = createApi({
|
|
|
478
504
|
// Full page (rows.length >= limit) means there might be more; only signal hasMore when we have a nextCursor
|
|
479
505
|
const hasMore = rows.length >= limit;
|
|
480
506
|
const resultRows = hasMore ? rows.slice(0, limit) : rows;
|
|
481
|
-
const uniqueFilePaths = Array.from(new Set(resultRows.flatMap((row) => {
|
|
482
|
-
const paths = [];
|
|
483
|
-
if (row.event_data?.file_path)
|
|
484
|
-
paths.push(row.event_data.file_path);
|
|
485
|
-
if (row.artifact_data?.file_path)
|
|
486
|
-
paths.push(row.artifact_data.file_path);
|
|
487
|
-
return paths;
|
|
488
|
-
})));
|
|
489
507
|
let urlMap = new Map();
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
urlMap.set(uniqueFilePaths[index], url);
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
catch (urlError) {
|
|
499
|
-
console.warn("Failed to generate signed URLs for feed:", urlError);
|
|
500
|
-
}
|
|
508
|
+
try {
|
|
509
|
+
urlMap = await signedUrlMapForFeedRows(resultRows);
|
|
510
|
+
}
|
|
511
|
+
catch (urlError) {
|
|
512
|
+
console.warn("Failed to generate signed URLs for feed:", urlError);
|
|
501
513
|
}
|
|
502
514
|
const items = resultRows.map((row) => ({
|
|
503
515
|
...row,
|
|
@@ -567,26 +579,12 @@ export const scoutApi = createApi({
|
|
|
567
579
|
// Full page (rows.length >= limit) means there might be more; only signal hasMore when we have a nextCursor
|
|
568
580
|
const hasMore = rows.length >= limit;
|
|
569
581
|
const resultRows = hasMore ? rows.slice(0, limit) : rows;
|
|
570
|
-
const uniqueFilePaths = Array.from(new Set(resultRows.flatMap((row) => {
|
|
571
|
-
const paths = [];
|
|
572
|
-
if (row.event_data?.file_path)
|
|
573
|
-
paths.push(row.event_data.file_path);
|
|
574
|
-
if (row.artifact_data?.file_path)
|
|
575
|
-
paths.push(row.artifact_data.file_path);
|
|
576
|
-
return paths;
|
|
577
|
-
})));
|
|
578
582
|
let urlMap = new Map();
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
urlMap.set(uniqueFilePaths[index], url);
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
catch (urlError) {
|
|
588
|
-
console.warn("Failed to generate signed URLs for feed:", urlError);
|
|
589
|
-
}
|
|
583
|
+
try {
|
|
584
|
+
urlMap = await signedUrlMapForFeedRows(resultRows);
|
|
585
|
+
}
|
|
586
|
+
catch (urlError) {
|
|
587
|
+
console.warn("Failed to generate signed URLs for feed:", urlError);
|
|
590
588
|
}
|
|
591
589
|
const items = resultRows.map((row) => ({
|
|
592
590
|
...row,
|
package/dist/types/supabase.d.ts
CHANGED
|
@@ -2857,6 +2857,13 @@ export type Database = {
|
|
|
2857
2857
|
status: string;
|
|
2858
2858
|
}[];
|
|
2859
2859
|
};
|
|
2860
|
+
transfer_device_to_herd: {
|
|
2861
|
+
Args: {
|
|
2862
|
+
p_device_id: number;
|
|
2863
|
+
p_target_herd_id: number;
|
|
2864
|
+
};
|
|
2865
|
+
Returns: undefined;
|
|
2866
|
+
};
|
|
2860
2867
|
};
|
|
2861
2868
|
Enums: {
|
|
2862
2869
|
analysis_work_status: "waiting" | "cancelled" | "processing" | "failed" | "success";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adventurelabs/scout-core",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.58",
|
|
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",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"prepare": "yarn build",
|
|
14
14
|
"dev": "tsc --watch",
|
|
15
|
-
"clean": "rm -rf dist"
|
|
15
|
+
"clean": "rm -rf dist",
|
|
16
|
+
"migrate-storage-paths-device-prefix": "node scripts/migrate-storage-paths-device-prefix.mjs"
|
|
16
17
|
},
|
|
17
18
|
"keywords": [
|
|
18
19
|
"scout",
|