@knopkem/dicomview 0.2.0 → 0.2.2

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
@@ -50,7 +50,12 @@ export class DICOMwebLoader {
50
50
  };
51
51
  requestFrame(() => {
52
52
  renderScheduled = false;
53
- viewer.render();
53
+ try {
54
+ viewer.render();
55
+ }
56
+ catch {
57
+ // Viewer may have been destroyed between scheduling and callback
58
+ }
54
59
  });
55
60
  };
56
61
  try {
@@ -85,7 +90,12 @@ export class DICOMwebLoader {
85
90
  }
86
91
  });
87
92
  await Promise.all(workers);
88
- viewer.render();
93
+ try {
94
+ viewer.render();
95
+ }
96
+ catch {
97
+ // Viewer may have been destroyed during load
98
+ }
89
99
  }
90
100
  finally {
91
101
  pool?.destroy();
@@ -99,7 +109,12 @@ export class DICOMwebLoader {
99
109
  return response.json();
100
110
  }
101
111
  async #fetchBytes(url, signal) {
102
- const response = await this.#fetch(url, signal, "application/dicom; transfer-syntax=*");
112
+ const response = await this.#fetch(url, signal, 'multipart/related; type="application/dicom"');
113
+ const contentType = response.headers.get("Content-Type") ?? "";
114
+ if (contentType.includes("multipart/related")) {
115
+ return extractMultipartDicom(new Uint8Array(await response.arrayBuffer()), contentType);
116
+ }
117
+ // Fallback: server returned a single-part DICOM response
103
118
  return new Uint8Array(await response.arrayBuffer());
104
119
  }
105
120
  async #fetch(url, signal, accept) {
@@ -343,3 +358,46 @@ function normalize(vector) {
343
358
  }
344
359
  return [vector[0] / length, vector[1] / length, vector[2] / length];
345
360
  }
361
+ /**
362
+ * Extract the DICOM file bytes from a WADO-RS multipart/related response.
363
+ *
364
+ * The response body contains one or more parts separated by a MIME boundary.
365
+ * We extract the first part (single-instance retrieval returns exactly one).
366
+ */
367
+ function extractMultipartDicom(body, contentType) {
368
+ const boundaryMatch = contentType.match(/boundary=([^\s;]+)/);
369
+ if (!boundaryMatch) {
370
+ // No boundary found — assume the body is the raw DICOM bytes
371
+ return body;
372
+ }
373
+ const boundary = boundaryMatch[1].replace(/^"(.*)"$/, "$1");
374
+ const boundaryBytes = new TextEncoder().encode("--" + boundary);
375
+ // Find the first boundary
376
+ const firstBoundary = indexOfBytes(body, boundaryBytes, 0);
377
+ if (firstBoundary === -1) {
378
+ return body;
379
+ }
380
+ // After the boundary line, skip until we find \r\n\r\n (end of part headers)
381
+ const headerStart = firstBoundary + boundaryBytes.length;
382
+ const headerEnd = indexOfBytes(body, new Uint8Array([0x0d, 0x0a, 0x0d, 0x0a]), headerStart);
383
+ if (headerEnd === -1) {
384
+ return body;
385
+ }
386
+ const partStart = headerEnd + 4;
387
+ // Find the next boundary (or end boundary) — the part data ends 2 bytes before it (\r\n)
388
+ const nextBoundary = indexOfBytes(body, boundaryBytes, partStart);
389
+ const partEnd = nextBoundary === -1 ? body.length : nextBoundary - 2;
390
+ return body.subarray(partStart, partEnd);
391
+ }
392
+ function indexOfBytes(haystack, needle, offset) {
393
+ const end = haystack.length - needle.length;
394
+ outer: for (let i = offset; i <= end; i++) {
395
+ for (let j = 0; j < needle.length; j++) {
396
+ if (haystack[i + j] !== needle[j]) {
397
+ continue outer;
398
+ }
399
+ }
400
+ return i;
401
+ }
402
+ return -1;
403
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knopkem/dicomview",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Rust/WASM medical imaging primitives for the web",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -1379,7 +1379,7 @@ function __wbg_get_imports() {
1379
1379
  return ret;
1380
1380
  },
1381
1381
  __wbindgen_cast_0000000000000002: function(arg0, arg1) {
1382
- // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 695, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
1382
+ // Cast intrinsic for `Closure(Closure { owned: true, function: Function { arguments: [Externref], shim_idx: 697, ret: Result(Unit), inner_ret: Some(Result(Unit)) }, mutable: true }) -> Externref`.
1383
1383
  const ret = makeMutClosure(arg0, arg1, wasm_bindgen__convert__closures_____invoke__h77f088fde8c66f5c);
1384
1384
  return ret;
1385
1385
  },
Binary file