dicom-curate 0.20.1 → 0.21.0

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.
@@ -37199,6 +37199,45 @@ async function shouldProcessFileItem(s3Item, fileAnomalies) {
37199
37199
  return true;
37200
37200
  }
37201
37201
  }
37202
+ async function shouldProcessFileNode(filePath, fileName, fileSize, fileAnomalies) {
37203
+ const allExcludedFiletypes = [
37204
+ ...DEFAULT_EXCLUDED_FILETYPES,
37205
+ ...excludedFiletypes
37206
+ ];
37207
+ try {
37208
+ if (allExcludedFiletypes.some(
37209
+ (excluded) => fileName.toLowerCase() === excluded.toLowerCase()
37210
+ )) {
37211
+ fileAnomalies.push(`Skipped excluded file: ${fileName}`);
37212
+ return false;
37213
+ }
37214
+ if (fileSize < 132) {
37215
+ fileAnomalies.push(
37216
+ `Skipped very small file: ${fileName} (${fileSize} bytes)`
37217
+ );
37218
+ return false;
37219
+ }
37220
+ const fs = await import("fs/promises");
37221
+ const fh = await fs.open(filePath, "r");
37222
+ try {
37223
+ const buffer = Buffer.alloc(4);
37224
+ await fh.read(buffer, 0, 4, 128);
37225
+ const dicomSignature = buffer.toString("ascii");
37226
+ if (dicomSignature === "DICM") {
37227
+ return true;
37228
+ }
37229
+ } finally {
37230
+ await fh.close();
37231
+ }
37232
+ fileAnomalies.push(`Skipped file without DICOM signature: ${fileName}`);
37233
+ return false;
37234
+ } catch (error2) {
37235
+ fileAnomalies.push(
37236
+ `Unable to determine file validity - processing anyway: ${fileName} - ${error2}`
37237
+ );
37238
+ return true;
37239
+ }
37240
+ }
37202
37241
  fixupNodeWorkerEnvironment().then(() => {
37203
37242
  globalThis.addEventListener("message", (event) => {
37204
37243
  switch (event.data.request) {
@@ -37376,12 +37415,13 @@ async function scanDirectoryNode(dirPath) {
37376
37415
  if (entry.isFile() && keepScanning) {
37377
37416
  const filePath = path.join(currentPath, entry.name);
37378
37417
  const stats = await fs.stat(filePath);
37379
- const fileBuffer = await fs.readFile(filePath);
37380
- const file = new File([new Uint8Array(fileBuffer)], entry.name, {
37381
- type: "application/dicom"
37382
- });
37383
37418
  const fileAnomalies = [];
37384
- if (await shouldProcessFile(file, fileAnomalies)) {
37419
+ if (await shouldProcessFileNode(
37420
+ filePath,
37421
+ entry.name,
37422
+ stats.size,
37423
+ fileAnomalies
37424
+ )) {
37385
37425
  globalThis.postMessage({
37386
37426
  response: "file",
37387
37427
  fileIndex: fileIndex++,
@@ -159,6 +159,50 @@
159
159
  return true;
160
160
  }
161
161
  }
162
+ /**
163
+ * Node-specific file validation that reads only the bytes needed
164
+ * instead of loading the entire file into memory.
165
+ */
166
+ async function shouldProcessFileNode(filePath, fileName, fileSize, fileAnomalies) {
167
+ const allExcludedFiletypes = [
168
+ ...DEFAULT_EXCLUDED_FILETYPES,
169
+ ...excludedFiletypes,
170
+ ];
171
+ try {
172
+ // Check if the file is in the list of excluded files
173
+ if (allExcludedFiletypes.some((excluded) => fileName.toLowerCase() === excluded.toLowerCase())) {
174
+ fileAnomalies.push(`Skipped excluded file: ${fileName}`);
175
+ return false;
176
+ }
177
+ // Check filesize - (valid) DICOM files are at least 132 bytes (128-byte preamble + 4-byte signature)
178
+ if (fileSize < 132) {
179
+ fileAnomalies.push(`Skipped very small file: ${fileName} (${fileSize} bytes)`);
180
+ return false;
181
+ }
182
+ // Check for DICOM signature "DICM" at offset 128 by reading only 4 bytes
183
+ const fs = await import('fs/promises');
184
+ const fh = await fs.open(filePath, 'r');
185
+ try {
186
+ const buffer = Buffer.alloc(4);
187
+ await fh.read(buffer, 0, 4, 128);
188
+ const dicomSignature = buffer.toString('ascii');
189
+ if (dicomSignature === 'DICM') {
190
+ return true;
191
+ }
192
+ }
193
+ finally {
194
+ await fh.close();
195
+ }
196
+ // Don't parse file without DICOM signature
197
+ fileAnomalies.push(`Skipped file without DICOM signature: ${fileName}`);
198
+ return false;
199
+ }
200
+ catch (error) {
201
+ fileAnomalies.push(`Unable to determine file validity - processing anyway: ${fileName} - ${error}`);
202
+ // If vetting process fails, let the parser decide
203
+ return true;
204
+ }
205
+ }
162
206
  fixupNodeWorkerEnvironment().then(() => {
163
207
  globalThis.addEventListener('message', (event) => {
164
208
  switch (event.data.request) {
@@ -359,12 +403,8 @@
359
403
  if (entry.isFile() && keepScanning) {
360
404
  const filePath = path.join(currentPath, entry.name);
361
405
  const stats = await fs.stat(filePath);
362
- const fileBuffer = await fs.readFile(filePath);
363
- const file = new File([new Uint8Array(fileBuffer)], entry.name, {
364
- type: 'application/dicom',
365
- });
366
406
  const fileAnomalies = [];
367
- if (await shouldProcessFile(file, fileAnomalies)) {
407
+ if (await shouldProcessFileNode(filePath, entry.name, stats.size, fileAnomalies)) {
368
408
  // Send file to processing pipeline
369
409
  globalThis.postMessage({
370
410
  response: 'file',