@knopkem/dicomview 0.2.0 → 0.2.1

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/loader.js CHANGED
@@ -99,7 +99,12 @@ export class DICOMwebLoader {
99
99
  return response.json();
100
100
  }
101
101
  async #fetchBytes(url, signal) {
102
- const response = await this.#fetch(url, signal, "application/dicom; transfer-syntax=*");
102
+ const response = await this.#fetch(url, signal, 'multipart/related; type="application/dicom"');
103
+ const contentType = response.headers.get("Content-Type") ?? "";
104
+ if (contentType.includes("multipart/related")) {
105
+ return extractMultipartDicom(new Uint8Array(await response.arrayBuffer()), contentType);
106
+ }
107
+ // Fallback: server returned a single-part DICOM response
103
108
  return new Uint8Array(await response.arrayBuffer());
104
109
  }
105
110
  async #fetch(url, signal, accept) {
@@ -343,3 +348,46 @@ function normalize(vector) {
343
348
  }
344
349
  return [vector[0] / length, vector[1] / length, vector[2] / length];
345
350
  }
351
+ /**
352
+ * Extract the DICOM file bytes from a WADO-RS multipart/related response.
353
+ *
354
+ * The response body contains one or more parts separated by a MIME boundary.
355
+ * We extract the first part (single-instance retrieval returns exactly one).
356
+ */
357
+ function extractMultipartDicom(body, contentType) {
358
+ const boundaryMatch = contentType.match(/boundary=([^\s;]+)/);
359
+ if (!boundaryMatch) {
360
+ // No boundary found — assume the body is the raw DICOM bytes
361
+ return body;
362
+ }
363
+ const boundary = boundaryMatch[1].replace(/^"(.*)"$/, "$1");
364
+ const boundaryBytes = new TextEncoder().encode("--" + boundary);
365
+ // Find the first boundary
366
+ const firstBoundary = indexOfBytes(body, boundaryBytes, 0);
367
+ if (firstBoundary === -1) {
368
+ return body;
369
+ }
370
+ // After the boundary line, skip until we find \r\n\r\n (end of part headers)
371
+ const headerStart = firstBoundary + boundaryBytes.length;
372
+ const headerEnd = indexOfBytes(body, new Uint8Array([0x0d, 0x0a, 0x0d, 0x0a]), headerStart);
373
+ if (headerEnd === -1) {
374
+ return body;
375
+ }
376
+ const partStart = headerEnd + 4;
377
+ // Find the next boundary (or end boundary) — the part data ends 2 bytes before it (\r\n)
378
+ const nextBoundary = indexOfBytes(body, boundaryBytes, partStart);
379
+ const partEnd = nextBoundary === -1 ? body.length : nextBoundary - 2;
380
+ return body.subarray(partStart, partEnd);
381
+ }
382
+ function indexOfBytes(haystack, needle, offset) {
383
+ const end = haystack.length - needle.length;
384
+ outer: for (let i = offset; i <= end; i++) {
385
+ for (let j = 0; j < needle.length; j++) {
386
+ if (haystack[i + j] !== needle[j]) {
387
+ continue outer;
388
+ }
389
+ }
390
+ return i;
391
+ }
392
+ return -1;
393
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knopkem/dicomview",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Rust/WASM medical imaging primitives for the web",
5
5
  "type": "module",
6
6
  "sideEffects": false,
Binary file