@fnndsc/salsa 3.2.4

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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +164 -0
  3. package/dist/compute/index.d.ts +17 -0
  4. package/dist/compute/index.js +33 -0
  5. package/dist/compute/index.js.map +1 -0
  6. package/dist/connect/index.d.ts +25 -0
  7. package/dist/connect/index.js +27 -0
  8. package/dist/connect/index.js.map +1 -0
  9. package/dist/context/index.d.ts +38 -0
  10. package/dist/context/index.js +58 -0
  11. package/dist/context/index.js.map +1 -0
  12. package/dist/feeds/index.d.ts +60 -0
  13. package/dist/feeds/index.js +101 -0
  14. package/dist/feeds/index.js.map +1 -0
  15. package/dist/files/index.d.ts +179 -0
  16. package/dist/files/index.js +562 -0
  17. package/dist/files/index.js.map +1 -0
  18. package/dist/files/pacs_content.d.ts +29 -0
  19. package/dist/files/pacs_content.js +121 -0
  20. package/dist/files/pacs_content.js.map +1 -0
  21. package/dist/files/pipeline_content.d.ts +22 -0
  22. package/dist/files/pipeline_content.js +31 -0
  23. package/dist/files/pipeline_content.js.map +1 -0
  24. package/dist/files/regular_content.d.ts +33 -0
  25. package/dist/files/regular_content.js +177 -0
  26. package/dist/files/regular_content.js.map +1 -0
  27. package/dist/groups/index.d.ts +21 -0
  28. package/dist/groups/index.js +37 -0
  29. package/dist/groups/index.js.map +1 -0
  30. package/dist/index.d.ts +21 -0
  31. package/dist/index.js +42 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/jobs/index.d.ts +6 -0
  34. package/dist/jobs/index.js +17 -0
  35. package/dist/jobs/index.js.map +1 -0
  36. package/dist/jobs/jobOps.d.ts +108 -0
  37. package/dist/jobs/jobOps.js +307 -0
  38. package/dist/jobs/jobOps.js.map +1 -0
  39. package/dist/pacs/index.d.ts +64 -0
  40. package/dist/pacs/index.js +91 -0
  41. package/dist/pacs/index.js.map +1 -0
  42. package/dist/pacs/resultDecode.d.ts +13 -0
  43. package/dist/pacs/resultDecode.js +19 -0
  44. package/dist/pacs/resultDecode.js.map +1 -0
  45. package/dist/pipelines/index.d.ts +58 -0
  46. package/dist/pipelines/index.js +194 -0
  47. package/dist/pipelines/index.js.map +1 -0
  48. package/dist/plugininstances/index.d.ts +15 -0
  49. package/dist/plugininstances/index.js +27 -0
  50. package/dist/plugininstances/index.js.map +1 -0
  51. package/dist/pluginmetas/index.d.ts +21 -0
  52. package/dist/pluginmetas/index.js +37 -0
  53. package/dist/pluginmetas/index.js.map +1 -0
  54. package/dist/plugins/index.d.ts +118 -0
  55. package/dist/plugins/index.js +247 -0
  56. package/dist/plugins/index.js.map +1 -0
  57. package/dist/plugins/internal/plugin_executeContinueFeed.d.ts +17 -0
  58. package/dist/plugins/internal/plugin_executeContinueFeed.js +65 -0
  59. package/dist/plugins/internal/plugin_executeContinueFeed.js.map +1 -0
  60. package/dist/plugins/internal/plugin_executeNewFeed.d.ts +32 -0
  61. package/dist/plugins/internal/plugin_executeNewFeed.js +97 -0
  62. package/dist/plugins/internal/plugin_executeNewFeed.js.map +1 -0
  63. package/dist/plugins/peer_search.d.ts +65 -0
  64. package/dist/plugins/peer_search.js +94 -0
  65. package/dist/plugins/peer_search.js.map +1 -0
  66. package/dist/plugins/plugin_executeInPlace.d.ts +69 -0
  67. package/dist/plugins/plugin_executeInPlace.js +66 -0
  68. package/dist/plugins/plugin_executeInPlace.js.map +1 -0
  69. package/dist/plugins/plugin_register.d.ts +64 -0
  70. package/dist/plugins/plugin_register.js +121 -0
  71. package/dist/plugins/plugin_register.js.map +1 -0
  72. package/dist/plugins/store_import.d.ts +64 -0
  73. package/dist/plugins/store_import.js +196 -0
  74. package/dist/plugins/store_import.js.map +1 -0
  75. package/dist/store/index.d.ts +15 -0
  76. package/dist/store/index.js +37 -0
  77. package/dist/store/index.js.map +1 -0
  78. package/dist/tags/index.d.ts +21 -0
  79. package/dist/tags/index.js +37 -0
  80. package/dist/tags/index.js.map +1 -0
  81. package/dist/vfs/dispatcher.d.ts +83 -0
  82. package/dist/vfs/dispatcher.js +192 -0
  83. package/dist/vfs/dispatcher.js.map +1 -0
  84. package/dist/vfs/index.d.ts +9 -0
  85. package/dist/vfs/index.js +26 -0
  86. package/dist/vfs/index.js.map +1 -0
  87. package/dist/vfs/provider.d.ts +80 -0
  88. package/dist/vfs/provider.js +11 -0
  89. package/dist/vfs/provider.js.map +1 -0
  90. package/dist/vfs/providers/etc.d.ts +44 -0
  91. package/dist/vfs/providers/etc.js +119 -0
  92. package/dist/vfs/providers/etc.js.map +1 -0
  93. package/dist/vfs/providers/native.d.ts +34 -0
  94. package/dist/vfs/providers/native.js +168 -0
  95. package/dist/vfs/providers/native.js.map +1 -0
  96. package/dist/vfs/providers/pacs.d.ts +56 -0
  97. package/dist/vfs/providers/pacs.js +413 -0
  98. package/dist/vfs/providers/pacs.js.map +1 -0
  99. package/dist/vfs/providers/pacsHelpers.d.ts +27 -0
  100. package/dist/vfs/providers/pacsHelpers.js +108 -0
  101. package/dist/vfs/providers/pacsHelpers.js.map +1 -0
  102. package/dist/vfs/providers/pacs_content.d.ts +27 -0
  103. package/dist/vfs/providers/pacs_content.js +137 -0
  104. package/dist/vfs/providers/pacs_content.js.map +1 -0
  105. package/dist/vfs/providers/proc.d.ts +62 -0
  106. package/dist/vfs/providers/proc.js +478 -0
  107. package/dist/vfs/providers/proc.js.map +1 -0
  108. package/dist/workflows/index.d.ts +21 -0
  109. package/dist/workflows/index.js +37 -0
  110. package/dist/workflows/index.js.map +1 -0
  111. package/package.json +57 -0
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @file Native ChRIS VFS Provider.
3
+ *
4
+ * Implements filesystem operations mapping directly to CUBE storage.
5
+ *
6
+ * @module
7
+ */
8
+ import { Result } from "@fnndsc/cumin";
9
+ import { VFSProvider, VFSItem, CpOptions } from "../provider.js";
10
+ /**
11
+ * Native ChRIS filesystem provider operating on absolute CUBE folders.
12
+ */
13
+ export declare class NativeVfsProvider implements VFSProvider {
14
+ /** Prefix matches everything. */
15
+ prefix: string;
16
+ /**
17
+ * Lists native ChRIS folder contents (dirs, files, links).
18
+ *
19
+ * @param pathStr - Absolute directory path.
20
+ * @param options - Sorting parameters.
21
+ */
22
+ list(pathStr: string, options?: {
23
+ sort?: "name" | "size" | "date" | "owner";
24
+ reverse?: boolean;
25
+ }): Promise<Result<VFSItem[]>>;
26
+ /**
27
+ * Copies native files or folders using Salsa's files_copy algorithms.
28
+ *
29
+ * @param src - Source absolute path.
30
+ * @param dest - Destination absolute path.
31
+ * @param options - Copy options.
32
+ */
33
+ cp(src: string, dest: string, options: CpOptions): Promise<boolean>;
34
+ }
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ /**
3
+ * @file Native ChRIS VFS Provider.
4
+ *
5
+ * Implements filesystem operations mapping directly to CUBE storage.
6
+ *
7
+ * @module
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.NativeVfsProvider = void 0;
14
+ const cumin_1 = require("@fnndsc/cumin");
15
+ const index_js_1 = require("../../files/index.js");
16
+ const path_1 = __importDefault(require("path"));
17
+ /**
18
+ * Standard sort utility for VFS items.
19
+ *
20
+ * @param items - Items list to sort.
21
+ * @param sortField - Field to sort by.
22
+ * @param reverse - Whether to reverse sort order.
23
+ * @returns Sorted array.
24
+ */
25
+ function vfs_sortItems(items, sortField, reverse) {
26
+ const field = sortField || "name";
27
+ const sorted = [...items].sort((a, b) => {
28
+ const valA = a[field];
29
+ const valB = b[field];
30
+ if (typeof valA === "string" && typeof valB === "string") {
31
+ return valA.localeCompare(valB);
32
+ }
33
+ if (typeof valA === "number" && typeof valB === "number") {
34
+ return valA - valB;
35
+ }
36
+ return 0;
37
+ });
38
+ if (reverse) {
39
+ sorted.reverse();
40
+ }
41
+ return sorted;
42
+ }
43
+ /**
44
+ * Native ChRIS filesystem provider operating on absolute CUBE folders.
45
+ */
46
+ class NativeVfsProvider {
47
+ /** Prefix matches everything. */
48
+ prefix = "";
49
+ /**
50
+ * Lists native ChRIS folder contents (dirs, files, links).
51
+ *
52
+ * @param pathStr - Absolute directory path.
53
+ * @param options - Sorting parameters.
54
+ */
55
+ async list(pathStr, options) {
56
+ try {
57
+ const fetchOpts = { limit: 1000, offset: 0 };
58
+ const resolvedPath = pathStr || "/";
59
+ // Parallelize files/dirs/links API requests
60
+ const results = await Promise.allSettled([
61
+ (0, index_js_1.files_listAll)(fetchOpts, "dirs", resolvedPath),
62
+ (0, index_js_1.files_listAll)(fetchOpts, "files", resolvedPath),
63
+ (0, index_js_1.files_listAll)(fetchOpts, "links", resolvedPath),
64
+ ]);
65
+ const [dirsResult, filesResult, linksResult] = results;
66
+ const items = [];
67
+ const mapToItem = (raw, type) => {
68
+ let name = raw.fname || raw.path || "";
69
+ if (name.includes("/")) {
70
+ name = name.split("/").pop() || name;
71
+ }
72
+ if (type === "link" && name.endsWith(".chrislink")) {
73
+ name = name.slice(0, -10);
74
+ }
75
+ const targetPath = raw.path
76
+ ? raw.path.startsWith("/")
77
+ ? raw.path
78
+ : "/" + raw.path
79
+ : undefined;
80
+ return {
81
+ name,
82
+ type,
83
+ size: raw.fsize || 0,
84
+ owner: raw.owner_username || "unknown",
85
+ date: raw.creation_date || "",
86
+ target: targetPath,
87
+ };
88
+ };
89
+ if (dirsResult.status === "fulfilled" && dirsResult.value?.tableData) {
90
+ dirsResult.value.tableData.forEach((d) => items.push(mapToItem(d, "dir")));
91
+ }
92
+ if (filesResult.status === "fulfilled" && filesResult.value?.tableData) {
93
+ filesResult.value.tableData.forEach((f) => items.push(mapToItem(f, "file")));
94
+ }
95
+ if (linksResult.status === "fulfilled" && linksResult.value?.tableData) {
96
+ linksResult.value.tableData.forEach((l) => items.push(mapToItem(l, "link")));
97
+ }
98
+ const sorted = vfs_sortItems(items, options?.sort, options?.reverse);
99
+ return (0, cumin_1.Ok)(sorted);
100
+ }
101
+ catch (error) {
102
+ const msg = error instanceof Error ? error.message : String(error);
103
+ cumin_1.errorStack.stack_push("error", `Native VFS list failed: ${msg}`);
104
+ return (0, cumin_1.Err)();
105
+ }
106
+ }
107
+ /**
108
+ * Copies native files or folders using Salsa's files_copy algorithms.
109
+ *
110
+ * @param src - Source absolute path.
111
+ * @param dest - Destination absolute path.
112
+ * @param options - Copy options.
113
+ */
114
+ async cp(src, dest, options) {
115
+ try {
116
+ const srcIsDir = await path_checkIsDir(src);
117
+ if (srcIsDir && !options.recursive) {
118
+ cumin_1.errorStack.stack_push("error", `Source is a directory. Re-run with --recursive to copy: ${src}`);
119
+ return false;
120
+ }
121
+ const destIsDir = await path_checkIsDir(dest);
122
+ const destLooksDir = dest.endsWith("/");
123
+ const finalDest = (destIsDir || destLooksDir)
124
+ ? path_1.default.posix.join(dest, path_1.default.posix.basename(src))
125
+ : dest;
126
+ if (options.recursive) {
127
+ return await (0, index_js_1.files_copyRecursively)(src, finalDest);
128
+ }
129
+ else {
130
+ return await (0, index_js_1.files_copy)(src, finalDest);
131
+ }
132
+ }
133
+ catch (error) {
134
+ const msg = error instanceof Error ? error.message : String(error);
135
+ cumin_1.errorStack.stack_push("error", `Native VFS copy failed: ${msg}`);
136
+ return false;
137
+ }
138
+ }
139
+ }
140
+ exports.NativeVfsProvider = NativeVfsProvider;
141
+ /**
142
+ * Determines whether a given ChRIS path refers to a directory.
143
+ *
144
+ * @param targetPath - The absolute ChRIS path to check.
145
+ * @returns Promise<boolean> indicating directory existence.
146
+ */
147
+ async function path_checkIsDir(targetPath) {
148
+ const parent = path_1.default.posix.dirname(targetPath);
149
+ const name = path_1.default.posix.basename(targetPath);
150
+ try {
151
+ const results = await (0, index_js_1.files_listAll)({ limit: 1000, offset: 0 }, "dirs", parent);
152
+ if (!results || !results.tableData) {
153
+ return false;
154
+ }
155
+ return results.tableData.some((entry) => {
156
+ const candidate = typeof entry.path === "string" && entry.path.length > 0
157
+ ? entry.path
158
+ : typeof entry.fname === "string"
159
+ ? entry.fname
160
+ : "";
161
+ return candidate === targetPath || path_1.default.posix.basename(candidate) === name;
162
+ });
163
+ }
164
+ catch {
165
+ return false;
166
+ }
167
+ }
168
+ //# sourceMappingURL=native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native.js","sourceRoot":"","sources":["../../../src/vfs/providers/native.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file PACS VFS Provider.
3
+ *
4
+ * Implements virtual file browser directories and sequential synthetic PACS retrieves.
5
+ *
6
+ * @module
7
+ */
8
+ import { Result } from "@fnndsc/cumin";
9
+ import { VFSProvider, VFSItem, CpOptions } from "../provider.js";
10
+ type SortOptions = {
11
+ sort?: "name" | "size" | "date" | "owner";
12
+ reverse?: boolean;
13
+ };
14
+ /**
15
+ * Virtual PACS Search results VFS provider.
16
+ */
17
+ export declare class PacsVfsProvider implements VFSProvider {
18
+ /** Prefix matches /net/pacs and subdirectories. */
19
+ prefix: string;
20
+ /** Cache for decoded PACS query results to avoid redundant API hits. */
21
+ private _queryCache;
22
+ /**
23
+ * Fetches the decoded query result, leveraging a cache to prevent redundant API calls.
24
+ *
25
+ * @param queryId - The ID of the PACS query to decode.
26
+ * @returns Promise resolving to the decoded PACS query result, or null if fetch fails.
27
+ */
28
+ private queryResult_fetch;
29
+ /**
30
+ * Lazily lists virtual directory contents under `/net/pacs`.
31
+ */
32
+ list(pathStr: string, options?: SortOptions): Promise<Result<VFSItem[]>>;
33
+ /**
34
+ * Triggers sequential synthetic PACS pulls and link-copies results to feed destination.
35
+ *
36
+ * @param src - Source PACS absolute virtual path.
37
+ * @param dest - Destination native feed folder.
38
+ * @param options - Copy options like recursive.
39
+ */
40
+ cp(src: string, dest: string, _options: CpOptions): Promise<boolean>;
41
+ /**
42
+ * Reads virtual file content under '/net/pacs'.
43
+ *
44
+ * @param pathStr - The absolute virtual path of the file to read.
45
+ * @returns Promise resolving to a Result containing the file contents as a string.
46
+ */
47
+ read(pathStr: string): Promise<Result<string>>;
48
+ /**
49
+ * Reads virtual file binary content under '/net/pacs'.
50
+ *
51
+ * @param pathStr - The absolute virtual path of the file to read.
52
+ * @returns Promise resolving to a Result containing the file contents as a Buffer.
53
+ */
54
+ readBinary(pathStr: string): Promise<Result<Buffer>>;
55
+ }
56
+ export {};
@@ -0,0 +1,413 @@
1
+ "use strict";
2
+ /**
3
+ * @file PACS VFS Provider.
4
+ *
5
+ * Implements virtual file browser directories and sequential synthetic PACS retrieves.
6
+ *
7
+ * @module
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.PacsVfsProvider = void 0;
14
+ const cumin_1 = require("@fnndsc/cumin");
15
+ const pacsHelpers_js_1 = require("./pacsHelpers.js");
16
+ const index_js_1 = require("../../pacs/index.js");
17
+ const index_js_2 = require("../../files/index.js");
18
+ const path_1 = __importDefault(require("path"));
19
+ const chalk_1 = __importDefault(require("chalk"));
20
+ const pacs_content_js_1 = require("./pacs_content.js");
21
+ /**
22
+ * Standard sort utility for VFS items.
23
+ */
24
+ function items_sort(items, sortField, reverse) {
25
+ const field = sortField || "name";
26
+ const sorted = [...items].sort((a, b) => {
27
+ const valA = a[field];
28
+ const valB = b[field];
29
+ if (typeof valA === "string" && typeof valB === "string") {
30
+ return valA.localeCompare(valB);
31
+ }
32
+ if (typeof valA === "number" && typeof valB === "number") {
33
+ return valA - valB;
34
+ }
35
+ return 0;
36
+ });
37
+ if (reverse) {
38
+ sorted.reverse();
39
+ }
40
+ return sorted;
41
+ }
42
+ /**
43
+ * Resolves the folder path on ChRIS storage for a retrieved series.
44
+ *
45
+ * @param seriesInstanceUID - The Series Instance UID to resolve.
46
+ * @returns Promise resolving to the folder path string or null on failure.
47
+ */
48
+ async function series_getFolderPath(seriesInstanceUID) {
49
+ const client = await cumin_1.chrisConnection.client_get();
50
+ if (!client) {
51
+ return null;
52
+ }
53
+ const seriesList = await client.getPACSSeriesList({
54
+ SeriesInstanceUID: seriesInstanceUID,
55
+ limit: 1,
56
+ });
57
+ const seriesItems = seriesList.getItems();
58
+ if (!seriesItems || seriesItems.length === 0) {
59
+ return null;
60
+ }
61
+ const series = seriesItems[0];
62
+ return series?.data?.folder_path || null;
63
+ }
64
+ function pacsRoot_list() {
65
+ return (0, cumin_1.Ok)([{ name: "queries", type: "vfs", size: 0, owner: "root", date: new Date().toISOString() }]);
66
+ }
67
+ async function queries_list(options) {
68
+ const queriesResult = await (0, index_js_1.pacsQueries_list)({ limit: 100 });
69
+ if (!queriesResult || !queriesResult.ok || !queriesResult.value)
70
+ return (0, cumin_1.Ok)([]);
71
+ const items = queriesResult.value.tableData.map((row) => {
72
+ const queryId = String(row.id);
73
+ const title = typeof row.title === "string" ? row.title : "query";
74
+ const queryStr = typeof row.query === "string" ? row.query : "";
75
+ let queryObj = {};
76
+ try {
77
+ if (queryStr)
78
+ queryObj = JSON.parse(queryStr);
79
+ }
80
+ catch { /* ignore */ }
81
+ const queryParts = [];
82
+ for (const [k, v] of Object.entries(queryObj)) {
83
+ if (v !== undefined && v !== null && String(v).trim().length > 0)
84
+ queryParts.push(`${k}:${v}`);
85
+ }
86
+ let queryDesc = queryParts.join("_");
87
+ if (!queryDesc)
88
+ queryDesc = title.replace(/^pacs_query_\d+_\d+$/, "query").replace(/^pacs_query_/, "");
89
+ const hasResult = typeof row.result === "string" && row.result.trim().length > 0;
90
+ const noHitsSuffix = hasResult ? "" : "_no-hits";
91
+ const ownerUsername = typeof row.owner_username === "string" ? row.owner_username : "";
92
+ const userSuffix = ownerUsername ? `_${ownerUsername}` : "";
93
+ const creationDate = typeof row.creation_date === "string" ? row.creation_date : new Date().toISOString();
94
+ return {
95
+ name: `${queryDesc}_qid:${queryId}${userSuffix}${noHitsSuffix}`,
96
+ type: "dir",
97
+ size: 0,
98
+ owner: ownerUsername || "system",
99
+ date: creationDate,
100
+ };
101
+ });
102
+ return (0, cumin_1.Ok)(items_sort(items, options?.sort, options?.reverse));
103
+ }
104
+ function studies_list(studies, options) {
105
+ const items = studies.map((studyObj, idx) => {
106
+ const rawUID = studyObj.StudyInstanceUID || studyObj.uid;
107
+ const studyUID = (0, pacsHelpers_js_1.tag_extractValue)(rawUID || `study_${idx}`);
108
+ const studyDesc = (0, pacsHelpers_js_1.tag_extractValue)(studyObj.StudyDescription || "NoDescription");
109
+ return {
110
+ name: `Study_${studyUID}_${studyDesc.replace(/[\s/]/g, "_")}`,
111
+ type: "dir",
112
+ size: 0,
113
+ owner: "system",
114
+ date: "",
115
+ };
116
+ });
117
+ return (0, cumin_1.Ok)(items_sort(items, options?.sort, options?.reverse));
118
+ }
119
+ function series_list(seriesArr, options) {
120
+ const items = seriesArr.map((seriesObj, idx) => {
121
+ const rawUID = seriesObj.SeriesInstanceUID || seriesObj.uid;
122
+ const seriesUID = (0, pacsHelpers_js_1.tag_extractValue)(rawUID || `series_${idx}`);
123
+ const seriesDesc = (0, pacsHelpers_js_1.tag_extractValue)(seriesObj.SeriesDescription || "NoDescription");
124
+ return {
125
+ name: `Series_${seriesUID}_${seriesDesc.replace(/[\s/]/g, "_")}`,
126
+ type: "dir",
127
+ size: 0,
128
+ owner: "system",
129
+ date: "",
130
+ };
131
+ });
132
+ return (0, cumin_1.Ok)(items_sort(items, options?.sort, options?.reverse));
133
+ }
134
+ function seriesFiles_list(seriesObj) {
135
+ return (0, cumin_1.Ok)([
136
+ { name: "metadata.json", type: "file", size: JSON.stringify(seriesObj).length, owner: "system", date: "" },
137
+ { name: "image_slices.dcm", type: "file", size: 0, owner: "system", date: "" },
138
+ ]);
139
+ }
140
+ function seriesToRetrieve_build(decoded, studyUID, seriesUID, src) {
141
+ const studies = (0, pacsHelpers_js_1.studies_extractFromDecoded)(decoded.json);
142
+ const studyObj = (0, pacsHelpers_js_1.study_findByUID)(studies, studyUID);
143
+ if (!studyObj) {
144
+ cumin_1.errorStack.stack_push("error", `cp: Study with UID ${studyUID} not found in query results.`);
145
+ return (0, cumin_1.Err)();
146
+ }
147
+ const seriesArray = (0, pacsHelpers_js_1.series_extractFromStudy)(studyObj);
148
+ const seriesToRetrieve = [];
149
+ if (seriesUID) {
150
+ const seriesObj = (0, pacsHelpers_js_1.series_findByUID)(seriesArray, seriesUID);
151
+ const desc = seriesObj ? (0, pacsHelpers_js_1.tag_extractValue)(seriesObj.SeriesDescription || "Series") : "Series";
152
+ seriesToRetrieve.push({ uid: seriesUID, description: desc });
153
+ }
154
+ else {
155
+ for (const s of seriesArray) {
156
+ const sUID = (0, pacsHelpers_js_1.tag_extractValue)(s.SeriesInstanceUID || s.uid);
157
+ if (sUID) {
158
+ seriesToRetrieve.push({ uid: sUID, description: (0, pacsHelpers_js_1.tag_extractValue)(s.SeriesDescription || "Series") });
159
+ }
160
+ }
161
+ }
162
+ if (seriesToRetrieve.length === 0) {
163
+ cumin_1.errorStack.stack_push("error", `cp: No series found to retrieve from '${src}'`);
164
+ return (0, cumin_1.Err)();
165
+ }
166
+ return (0, cumin_1.Ok)(seriesToRetrieve);
167
+ }
168
+ async function pacsServer_resolve() {
169
+ const fromContext = await cumin_1.chrisContext.current_get(cumin_1.Context.PACSserver);
170
+ if (fromContext)
171
+ return (0, cumin_1.Ok)(fromContext);
172
+ const serversResult = await (0, index_js_1.pacsServers_list)();
173
+ if (serversResult.ok && serversResult.value.length > 0) {
174
+ return (0, cumin_1.Ok)(String(serversResult.value[0].id));
175
+ }
176
+ cumin_1.errorStack.stack_push("error", "cp: No PACS server available or configured in context.");
177
+ return (0, cumin_1.Err)();
178
+ }
179
+ async function retrieve_pollUntilComplete(syntheticQueryId, seriesUID, maxAttempts) {
180
+ let attempts = 0;
181
+ while (attempts < maxAttempts) {
182
+ attempts++;
183
+ await new Promise((resolve) => setTimeout(resolve, 5000));
184
+ const statusResult = await (0, index_js_1.pacsRetrieve_statusForQuery)(syntheticQueryId);
185
+ if (!statusResult.ok || !statusResult.value) {
186
+ console.log(chalk_1.default.yellow(` [Attempt ${attempts}/${maxAttempts}] Failed to fetch status report, retrying...`));
187
+ continue;
188
+ }
189
+ const statusReport = statusResult.value;
190
+ let totalExpected = 0;
191
+ let totalActual = 0;
192
+ let anyPulling = false;
193
+ let allPulled = true;
194
+ let anyError = false;
195
+ if (statusReport.studies) {
196
+ for (const s of statusReport.studies) {
197
+ if (s.series) {
198
+ for (const ser of s.series) {
199
+ if (String(ser.seriesInstanceUID) === String(seriesUID)) {
200
+ totalExpected = Number(ser.expectedFiles) || 0;
201
+ totalActual = Number(ser.actualFiles) || 0;
202
+ if (ser.status === "pulling")
203
+ anyPulling = true;
204
+ if (ser.status !== "pulled")
205
+ allPulled = false;
206
+ if (ser.status === "error")
207
+ anyError = true;
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+ const overallStatus = statusReport.retrieveStatus || "pending";
214
+ console.log(chalk_1.default.gray(` [Attempt ${attempts}/${maxAttempts}] CUBE Status: ${overallStatus} | Files: ${totalActual}/${totalExpected}`));
215
+ if (allPulled && totalExpected > 0 && totalActual === totalExpected) {
216
+ console.log(chalk_1.default.green(` ✓ Pull complete: ${totalActual}/${totalExpected} files retrieved.`));
217
+ return true;
218
+ }
219
+ if (overallStatus === "succeeded" || overallStatus === "completed") {
220
+ console.log(chalk_1.default.green(` ✓ Pull complete (Status: ${overallStatus}).`));
221
+ return true;
222
+ }
223
+ if (anyError || overallStatus === "error" || overallStatus === "failed") {
224
+ console.error(chalk_1.default.red(" ✗ PACS retrieve reported error status."));
225
+ return false;
226
+ }
227
+ }
228
+ console.error(chalk_1.default.red(" ✗ PACS retrieve timed out."));
229
+ return false;
230
+ }
231
+ async function series_retrieveAndCopy(seriesItem, studyUID, pacsserver, dest, idx, total) {
232
+ console.log(chalk_1.default.cyan(`\n[PACS Retrieve ${idx + 1}/${total}] Processing series: ${seriesItem.description} (${seriesItem.uid})...`));
233
+ const queryPayload = {
234
+ title: `Synthetic cp Query ${seriesItem.uid}`,
235
+ query: JSON.stringify({ SeriesInstanceUID: seriesItem.uid, StudyInstanceUID: studyUID }),
236
+ execute: false,
237
+ };
238
+ console.log(chalk_1.default.gray(" -> Registering synthetic query on CUBE..."));
239
+ const queryResult = await (0, index_js_1.pacsQueries_create)(pacsserver, queryPayload);
240
+ if (!queryResult.ok) {
241
+ console.error(chalk_1.default.red(` ✗ Failed to create synthetic query for series ${seriesItem.uid}`));
242
+ return false;
243
+ }
244
+ const syntheticQueryId = queryResult.value.id;
245
+ console.log(chalk_1.default.gray(` -> Triggering PACS retrieve (Query ID: ${syntheticQueryId})...`));
246
+ const retrieveResult = await (0, index_js_1.pacsRetrieve_create)(syntheticQueryId);
247
+ if (!retrieveResult.ok) {
248
+ console.error(chalk_1.default.red(` ✗ Failed to create PACS retrieve for query ${syntheticQueryId}`));
249
+ return false;
250
+ }
251
+ console.log(chalk_1.default.gray(" -> Pulling series data sequentially, polling progress..."));
252
+ const finished = await retrieve_pollUntilComplete(syntheticQueryId, seriesItem.uid, 60);
253
+ if (!finished)
254
+ return false;
255
+ console.log(chalk_1.default.gray(" -> Finding folder path on ChRIS storage..."));
256
+ const folderPath = await series_getFolderPath(seriesItem.uid);
257
+ if (!folderPath) {
258
+ console.error(chalk_1.default.red(` ✗ No registered folder path found for series UID ${seriesItem.uid}`));
259
+ return false;
260
+ }
261
+ const absoluteFolderPath = folderPath.startsWith("/") ? folderPath : "/" + folderPath;
262
+ const cleanDesc = seriesItem.description.replace(/[\s/]/g, "_");
263
+ const targetSeriesFolder = path_1.default.posix.join(dest, `Series_${seriesItem.uid}_${cleanDesc}`);
264
+ console.log(chalk_1.default.gray(` -> Copying series files to '${targetSeriesFolder}'...`));
265
+ const copySuccess = await (0, index_js_2.files_copyRecursively)(absoluteFolderPath, targetSeriesFolder);
266
+ if (!copySuccess) {
267
+ console.error(chalk_1.default.red(` ✗ Recursive copy failed from '${absoluteFolderPath}' to '${targetSeriesFolder}'`));
268
+ return false;
269
+ }
270
+ console.log(chalk_1.default.green(` ✓ Series '${seriesItem.description}' copied successfully.`));
271
+ return true;
272
+ }
273
+ /**
274
+ * Virtual PACS Search results VFS provider.
275
+ */
276
+ class PacsVfsProvider {
277
+ /** Prefix matches /net/pacs and subdirectories. */
278
+ prefix = "/net/pacs";
279
+ /** Cache for decoded PACS query results to avoid redundant API hits. */
280
+ _queryCache = new Map();
281
+ /**
282
+ * Fetches the decoded query result, leveraging a cache to prevent redundant API calls.
283
+ *
284
+ * @param queryId - The ID of the PACS query to decode.
285
+ * @returns Promise resolving to the decoded PACS query result, or null if fetch fails.
286
+ */
287
+ async queryResult_fetch(queryId) {
288
+ const cached = this._queryCache.get(queryId);
289
+ if (cached) {
290
+ return cached;
291
+ }
292
+ const decodedResult = await (0, index_js_1.pacsQuery_resultDecode)(queryId);
293
+ if (!decodedResult.ok || !decodedResult.value) {
294
+ return null;
295
+ }
296
+ this._queryCache.set(queryId, decodedResult.value);
297
+ return decodedResult.value;
298
+ }
299
+ /**
300
+ * Lazily lists virtual directory contents under `/net/pacs`.
301
+ */
302
+ async list(pathStr, options) {
303
+ try {
304
+ const effectivePath = (0, pacsHelpers_js_1.path_normalize)(pathStr);
305
+ if (effectivePath === "/net/pacs")
306
+ return pacsRoot_list();
307
+ if (effectivePath === "/net/pacs/queries")
308
+ return queries_list(options);
309
+ const parts = effectivePath.split("/").filter(Boolean);
310
+ if (parts.length >= 3 && parts[2] !== "queries") {
311
+ cumin_1.errorStack.stack_push("error", `'${effectivePath}': No such virtual directory. Valid paths under /net/pacs: /net/pacs/queries`);
312
+ return (0, cumin_1.Err)();
313
+ }
314
+ const queryFolder = parts[3];
315
+ if (!queryFolder)
316
+ return (0, cumin_1.Ok)([]);
317
+ const queryId = (0, pacsHelpers_js_1.queryId_extractFromFolder)(queryFolder);
318
+ if (Number.isNaN(queryId)) {
319
+ cumin_1.errorStack.stack_push("error", `'${effectivePath}': No such virtual directory. Use 'ls /net/pacs/queries' to see available queries.`);
320
+ return (0, cumin_1.Err)();
321
+ }
322
+ const decoded = await this.queryResult_fetch(queryId);
323
+ if (!decoded?.json) {
324
+ cumin_1.errorStack.stack_push("error", `PACS query ${queryId} has no structured study/series result payload.`);
325
+ return (0, cumin_1.Err)();
326
+ }
327
+ const studies = (0, pacsHelpers_js_1.studies_extractFromDecoded)(decoded.json);
328
+ if (parts.length === 4)
329
+ return studies_list(studies, options);
330
+ const studyUID = parts[4].replace(/^Study_/, "").split("_")[0];
331
+ const studyObj = (0, pacsHelpers_js_1.study_findByUID)(studies, studyUID);
332
+ if (!studyObj)
333
+ return (0, cumin_1.Ok)([]);
334
+ const seriesArr = (0, pacsHelpers_js_1.series_extractFromStudy)(studyObj);
335
+ if (parts.length === 5)
336
+ return series_list(seriesArr, options);
337
+ if (parts.length === 6) {
338
+ const seriesUID = parts[5].replace(/^Series_/, "").split("_")[0];
339
+ const seriesObj = (0, pacsHelpers_js_1.series_findByUID)(seriesArr, seriesUID);
340
+ if (!seriesObj)
341
+ return (0, cumin_1.Ok)([]);
342
+ return seriesFiles_list(seriesObj);
343
+ }
344
+ return (0, cumin_1.Ok)([]);
345
+ }
346
+ catch (error) {
347
+ const msg = error instanceof Error ? error.message : String(error);
348
+ cumin_1.errorStack.stack_push("error", `PACS VFS list failed: ${msg}`);
349
+ return (0, cumin_1.Err)();
350
+ }
351
+ }
352
+ /**
353
+ * Triggers sequential synthetic PACS pulls and link-copies results to feed destination.
354
+ *
355
+ * @param src - Source PACS absolute virtual path.
356
+ * @param dest - Destination native feed folder.
357
+ * @param options - Copy options like recursive.
358
+ */
359
+ async cp(src, dest, _options) {
360
+ try {
361
+ const parsedResult = (0, pacsHelpers_js_1.cpSrc_parse)(src);
362
+ if (!parsedResult.ok)
363
+ return false;
364
+ const { studyUID, seriesUID, queryId } = parsedResult.value;
365
+ const decoded = await this.queryResult_fetch(queryId);
366
+ if (!decoded?.json) {
367
+ cumin_1.errorStack.stack_push("error", `cp: Failed to decode query results for query ID ${queryId}`);
368
+ return false;
369
+ }
370
+ const seriesResult = seriesToRetrieve_build(decoded, studyUID, seriesUID, src);
371
+ if (!seriesResult.ok)
372
+ return false;
373
+ const seriesToRetrieve = seriesResult.value;
374
+ const serverResult = await pacsServer_resolve();
375
+ if (!serverResult.ok)
376
+ return false;
377
+ const pacsserver = serverResult.value;
378
+ console.log(chalk_1.default.cyan(`[PACS Retrieve] Initiating sequential gather of ${seriesToRetrieve.length} series...`));
379
+ let overallSuccess = true;
380
+ for (let i = 0; i < seriesToRetrieve.length; i++) {
381
+ const ok = await series_retrieveAndCopy(seriesToRetrieve[i], studyUID, pacsserver, dest, i, seriesToRetrieve.length);
382
+ if (!ok)
383
+ overallSuccess = false;
384
+ }
385
+ return overallSuccess;
386
+ }
387
+ catch (error) {
388
+ const msg = error instanceof Error ? error.message : String(error);
389
+ cumin_1.errorStack.stack_push("error", `PACS cp failed: ${msg}`);
390
+ return false;
391
+ }
392
+ }
393
+ /**
394
+ * Reads virtual file content under '/net/pacs'.
395
+ *
396
+ * @param pathStr - The absolute virtual path of the file to read.
397
+ * @returns Promise resolving to a Result containing the file contents as a string.
398
+ */
399
+ async read(pathStr) {
400
+ return (0, pacs_content_js_1.pacsVfs_read)(pathStr, (queryId) => this.queryResult_fetch(queryId));
401
+ }
402
+ /**
403
+ * Reads virtual file binary content under '/net/pacs'.
404
+ *
405
+ * @param pathStr - The absolute virtual path of the file to read.
406
+ * @returns Promise resolving to a Result containing the file contents as a Buffer.
407
+ */
408
+ async readBinary(pathStr) {
409
+ return (0, pacs_content_js_1.pacsVfs_readBinary)(pathStr, (queryId) => this.queryResult_fetch(queryId));
410
+ }
411
+ }
412
+ exports.PacsVfsProvider = PacsVfsProvider;
413
+ //# sourceMappingURL=pacs.js.map