dcmjs 0.49.3 → 0.50.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.
- package/README.md +50 -0
- package/build/dcmjs.es.js +1071 -112
- package/build/dcmjs.es.js.map +1 -1
- package/build/dcmjs.js +1071 -112
- package/build/dcmjs.js.map +1 -1
- package/build/dcmjs.min.js +2 -2
- package/build/dcmjs.min.js.map +1 -1
- package/generate/dictionary.mjs +56029 -0
- package/package.json +18 -2
- package/.babelrc +0 -9
- package/.github/workflows/lint-and-format.yml +0 -27
- package/.github/workflows/publish-package.yml +0 -45
- package/.github/workflows/tests.yml +0 -24
- package/.prettierrc +0 -5
- package/.vscode/extensions.json +0 -7
- package/.vscode/settings.json +0 -8
- package/changelog.md +0 -31
- package/docs/ArrayBufferExpanderListener.md +0 -303
- package/docs/AsyncDicomReader-skill.md +0 -730
- package/eslint.config.mjs +0 -30
- package/generate-dictionary.js +0 -145
- package/jest.setup.js +0 -39
- package/netlify.toml +0 -22
- package/rollup.config.mjs +0 -57
- package/test/ArrayBufferExpanderListener.test.js +0 -365
- package/test/DICOMWEB.test.js +0 -1
- package/test/DicomMetaDictionary.test.js +0 -73
- package/test/SequenceOfItems.test.js +0 -86
- package/test/adapters.test.js +0 -43
- package/test/anonymizer.test.js +0 -176
- package/test/arrayItem.json +0 -351
- package/test/async-data.test.js +0 -575
- package/test/data-encoding.test.js +0 -59
- package/test/data-options.test.js +0 -199
- package/test/data.test.js +0 -1776
- package/test/derivations.test.js +0 -1
- package/test/helper/DicomDataReadBufferStreamBuilder.js +0 -89
- package/test/information-filter.test.js +0 -165
- package/test/integration/DicomMessage.readFile.test.js +0 -50
- package/test/lossless-read-write.test.js +0 -1407
- package/test/mocks/minimal_fields_dataset.json +0 -17
- package/test/mocks/null_number_vrs_dataset.json +0 -102
- package/test/normalizers.test.js +0 -38
- package/test/odd-frame-bit-data.js +0 -138
- package/test/rawTags.js +0 -170
- package/test/readBufferStream.test.js +0 -158
- package/test/sample-dicom.json +0 -904
- package/test/sample-op.lei +0 -0
- package/test/sample-sr.json +0 -997
- package/test/sr-tid.test.js +0 -251
- package/test/testUtils.js +0 -85
- package/test/utilities/deepEqual.test.js +0 -87
- package/test/utilities.test.js +0 -205
- package/test/video-test-dict.js +0 -40
- package/test/writeBufferStream.test.js +0 -149
package/eslint.config.mjs
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// eslint.config.js
|
|
2
|
-
import js from "@eslint/js";
|
|
3
|
-
import prettier from "eslint-config-prettier";
|
|
4
|
-
import globals from "globals";
|
|
5
|
-
|
|
6
|
-
export default [
|
|
7
|
-
js.configs.recommended,
|
|
8
|
-
prettier,
|
|
9
|
-
|
|
10
|
-
{
|
|
11
|
-
languageOptions: {
|
|
12
|
-
ecmaVersion: 2024,
|
|
13
|
-
sourceType: "module",
|
|
14
|
-
globals: {
|
|
15
|
-
...globals.browser,
|
|
16
|
-
...globals.node,
|
|
17
|
-
...globals.jest
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
rules: {
|
|
21
|
-
"no-loss-of-precision": "off",
|
|
22
|
-
"no-unused-vars": [
|
|
23
|
-
"error",
|
|
24
|
-
{
|
|
25
|
-
argsIgnorePattern: "^_" // for function parameters
|
|
26
|
-
}
|
|
27
|
-
]
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
];
|
package/generate-dictionary.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create the dictionary.js DICOM dictionary file from the Standard.
|
|
3
|
-
* Reformat The DICOM dictionary PS3.6 and PS3.7 docbook XML files (from e.g. standard docs) to JavaScript syntax.
|
|
4
|
-
* Based on https://github.com/pydicom/pydicom/blob/8112bb69bfc0423c3a08cb89e7960defbe7237bf/source/generate_dict/generate_dicom_dict.py
|
|
5
|
-
*/
|
|
6
|
-
const fs = require('fs/promises');
|
|
7
|
-
const https = require('https');
|
|
8
|
-
const xml2js = require('xml2js');
|
|
9
|
-
|
|
10
|
-
require('@babel/register');
|
|
11
|
-
const DICTIONARY_PATH = './src/dictionary.js';
|
|
12
|
-
const dictionary = require(DICTIONARY_PATH).default;
|
|
13
|
-
const { Tag } = require('./src/Tag');
|
|
14
|
-
|
|
15
|
-
async function main() {
|
|
16
|
-
const tags = [];
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Collect DICOM tags from XML documents
|
|
20
|
-
*/
|
|
21
|
-
const part06 = await getDocbook('part06/part06.xml');
|
|
22
|
-
const part06Rows = part06.book.chapter.find(chapter => chapter.$['xml:id'] === 'chapter_6').table[0].tbody[0].tr;
|
|
23
|
-
tags.push(...part06Rows.map(row => {
|
|
24
|
-
const retired = getCellData(row.td[5])?.startsWith('RET');
|
|
25
|
-
const name = getCellData(row.td[2]);
|
|
26
|
-
return {
|
|
27
|
-
tag: getCellData(row.td[0]),
|
|
28
|
-
vr: getCellData(row.td[3]),
|
|
29
|
-
name: retired ? `RETIRED_${name}` : name,
|
|
30
|
-
vm: getCellData(row.td[4]),
|
|
31
|
-
version: retired ? 'DICOM/retired' : 'DICOM',
|
|
32
|
-
}
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
const part07 = await getDocbook('part07/part07.xml');
|
|
36
|
-
const chapterE = part07.book.chapter.find(chapter => chapter.$['xml:id'] === 'chapter_E');
|
|
37
|
-
const commandFields = chapterE.section[0].table[0].tbody[0].tr;
|
|
38
|
-
tags.push(...commandFields.map(row => {
|
|
39
|
-
return {
|
|
40
|
-
tag: getCellData(row.td[0]),
|
|
41
|
-
vr: getCellData(row.td[3]),
|
|
42
|
-
name: getCellData(row.td[2]),
|
|
43
|
-
vm: getCellData(row.td[4]),
|
|
44
|
-
version: 'DICOM',
|
|
45
|
-
}
|
|
46
|
-
}));
|
|
47
|
-
const retiredCommandFields = chapterE.section[1].table[0].tbody[0].tr;
|
|
48
|
-
tags.push(...retiredCommandFields.map(row => {
|
|
49
|
-
return {
|
|
50
|
-
tag: getCellData(row.td[0]),
|
|
51
|
-
vr: getCellData(row.td[3]),
|
|
52
|
-
name: `RETIRED_${getCellData(row.td[2])}`,
|
|
53
|
-
vm: getCellData(row.td[4]),
|
|
54
|
-
version: 'DICOM/retired',
|
|
55
|
-
}
|
|
56
|
-
}));
|
|
57
|
-
|
|
58
|
-
const newTags = tags.filter(tag => tag.vr && tag.name && tag.vm)
|
|
59
|
-
.filter(tag => !(tag.tag in dictionary)) // filter already defined
|
|
60
|
-
.filter(tag => !/[(,\dA-F]x+[A-F\d,)]/.test(tag.tag)); // filter repeater tags
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Insert new tags into dictionary, ordered among tags with the same version
|
|
64
|
-
*/
|
|
65
|
-
const dictionaryArray = Object.values(dictionary);
|
|
66
|
-
for (const newTag of newTags) {
|
|
67
|
-
const parsedTag = Tag.fromPString(newTag.tag);
|
|
68
|
-
const insertIndex = dictionaryArray.findIndex(tag => {
|
|
69
|
-
if (tag.version !== newTag.version) {
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
const thisTag = Tag.fromPString(tag.tag);
|
|
73
|
-
return thisTag.toCleanString() > parsedTag.toCleanString();
|
|
74
|
-
});
|
|
75
|
-
dictionaryArray.splice(insertIndex, 0, newTag);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
await writeDictionary(dictionaryArray);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function writeDictionary(tags) {
|
|
82
|
-
let data = 'const dictionary = {';
|
|
83
|
-
for (const tag of tags) {
|
|
84
|
-
if (!tag.tag) {
|
|
85
|
-
data += `
|
|
86
|
-
"": {
|
|
87
|
-
tag: ""
|
|
88
|
-
},`
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
const tagKey = tag.tag.includes('"') ? `'${tag.tag}'` : `"${tag.tag}"`;
|
|
92
|
-
data += `
|
|
93
|
-
${tagKey}: {
|
|
94
|
-
tag: ${tagKey},
|
|
95
|
-
vr: "${tag.vr}",
|
|
96
|
-
name: "${tag.name}",
|
|
97
|
-
vm: "${tag.vm}",
|
|
98
|
-
version: "${tag.version ?? 'PrivateTag'}"
|
|
99
|
-
},`;
|
|
100
|
-
}
|
|
101
|
-
data += `
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export default dictionary;
|
|
105
|
-
`;
|
|
106
|
-
|
|
107
|
-
await fs.writeFile(DICTIONARY_PATH, data);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
async function getDocbook(part) {
|
|
111
|
-
const source = await getUrl(`https://dicom.nema.org/medical/dicom/current/source/docbook/${part}`);
|
|
112
|
-
return xml2js.parseStringPromise(source);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function getCellData(td) {
|
|
116
|
-
const para = td.para?.[0];
|
|
117
|
-
if (!para) {
|
|
118
|
-
return undefined;
|
|
119
|
-
}
|
|
120
|
-
const text = para.emphasis ? para.emphasis[0]._ : para._;
|
|
121
|
-
return text?.trim().replace(/[\u200b\uffff]/g, '');
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function getUrl(url) {
|
|
125
|
-
return new Promise((resolve, reject) => {
|
|
126
|
-
https.get(url, request => {
|
|
127
|
-
let data = '';
|
|
128
|
-
request.on('error', () => {
|
|
129
|
-
reject(error);
|
|
130
|
-
});
|
|
131
|
-
request.on('end', () => {
|
|
132
|
-
resolve(data);
|
|
133
|
-
});
|
|
134
|
-
request.on('data', chunk => {
|
|
135
|
-
data += chunk;
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (require.main === module) {
|
|
142
|
-
main().catch(error => {
|
|
143
|
-
console.log(error);
|
|
144
|
-
});
|
|
145
|
-
}
|
package/jest.setup.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
const { Console } = require("console");
|
|
2
|
-
const nodeConsole = new Console(process.stdout, process.stderr);
|
|
3
|
-
|
|
4
|
-
// Mock loglevel
|
|
5
|
-
const createMockLogger = () => {
|
|
6
|
-
const logger = {
|
|
7
|
-
debug: jest.fn(),
|
|
8
|
-
info: jest.fn(),
|
|
9
|
-
warn: jest.fn(),
|
|
10
|
-
error: jest.fn(),
|
|
11
|
-
setLevel: jest.fn()
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
// Modify warn to print directly to stdout instead of console.warn
|
|
15
|
-
logger.warn.mockImplementation((...args) => {
|
|
16
|
-
// This uses Node's real util.inspect internally
|
|
17
|
-
nodeConsole.warn("[warn]", ...args);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
return logger;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const mockLog = createMockLogger();
|
|
24
|
-
|
|
25
|
-
mockLog.getLogger = jest.fn(name => {
|
|
26
|
-
const namedLogger = createMockLogger();
|
|
27
|
-
namedLogger.name = name;
|
|
28
|
-
return namedLogger;
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
jest.mock("loglevel", () => mockLog);
|
|
32
|
-
|
|
33
|
-
// Optional global access for assertions
|
|
34
|
-
global.mockLog = mockLog;
|
|
35
|
-
|
|
36
|
-
// Override console.warn to remove stack traces
|
|
37
|
-
console.warn = nodeConsole.warn;
|
|
38
|
-
console.time = nodeConsole.time;
|
|
39
|
-
console.timeEnd = nodeConsole.timeEnd;
|
package/netlify.toml
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Netlify Config
|
|
2
|
-
#
|
|
3
|
-
# TOML Reference:
|
|
4
|
-
# https://www.netlify.com/docs/netlify-toml-reference/
|
|
5
|
-
|
|
6
|
-
# Settings in the [build] context are global and are applied to all contexts
|
|
7
|
-
# unless otherwise overridden by more specific contexts.
|
|
8
|
-
[build]
|
|
9
|
-
# Directory to change to before starting a build.
|
|
10
|
-
# This is where we will look for package.json/.nvmrc/etc.
|
|
11
|
-
base = ""
|
|
12
|
-
publish = "examples/"
|
|
13
|
-
command = "npm run build:examples"
|
|
14
|
-
environment = { NODE_VERSION = "18.18.0" }
|
|
15
|
-
|
|
16
|
-
# Deploy Preview context: all deploys generated from a pull/merge request will
|
|
17
|
-
# inherit these settings.
|
|
18
|
-
[context.deploy-preview]
|
|
19
|
-
base = ""
|
|
20
|
-
publish = "examples/"
|
|
21
|
-
command = "npm run build:examples"
|
|
22
|
-
environment = { NODE_VERSION = "18.18.0" }
|
package/rollup.config.mjs
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import babel from "@rollup/plugin-babel";
|
|
2
|
-
import resolve from "@rollup/plugin-node-resolve";
|
|
3
|
-
// import globals from "rollup-plugin-node-globals";
|
|
4
|
-
// import builtins from "rollup-plugin-node-builtins";
|
|
5
|
-
import commonjs from "@rollup/plugin-commonjs";
|
|
6
|
-
// import babelRuntime from "@rollup/plugin-transform-runtime"
|
|
7
|
-
import json from "@rollup/plugin-json";
|
|
8
|
-
import replace from "@rollup/plugin-replace";
|
|
9
|
-
import terser from "@rollup/plugin-terser";
|
|
10
|
-
import { readFileSync } from "fs";
|
|
11
|
-
|
|
12
|
-
const pkg = JSON.parse(
|
|
13
|
-
readFileSync(new URL("./package.json", import.meta.url), "utf8")
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
export default {
|
|
17
|
-
input: "src/index.js",
|
|
18
|
-
output: [
|
|
19
|
-
{
|
|
20
|
-
file: pkg.main,
|
|
21
|
-
format: "umd",
|
|
22
|
-
name: "dcmjs",
|
|
23
|
-
sourcemap: true
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
file: pkg.main.replace(/\.js$/, ".min.js"),
|
|
27
|
-
format: "umd",
|
|
28
|
-
name: "dcmjs",
|
|
29
|
-
sourcemap: true,
|
|
30
|
-
plugins: [terser()]
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
file: pkg.module,
|
|
34
|
-
format: "es",
|
|
35
|
-
sourcemap: true
|
|
36
|
-
}
|
|
37
|
-
],
|
|
38
|
-
plugins: [
|
|
39
|
-
replace({
|
|
40
|
-
"process.env.LOG_LEVEL": JSON.stringify(
|
|
41
|
-
process.env.LOG_LEVEL || "warn"
|
|
42
|
-
),
|
|
43
|
-
preventAssignment: true
|
|
44
|
-
}),
|
|
45
|
-
resolve({
|
|
46
|
-
browser: true
|
|
47
|
-
}),
|
|
48
|
-
commonjs(),
|
|
49
|
-
// globals(),
|
|
50
|
-
//builtins(),
|
|
51
|
-
// babelRuntime(),
|
|
52
|
-
babel({
|
|
53
|
-
exclude: "node_modules/**"
|
|
54
|
-
}),
|
|
55
|
-
json()
|
|
56
|
-
]
|
|
57
|
-
};
|
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
import { DicomMetadataListener } from "../src/utilities/DicomMetadataListener.js";
|
|
2
|
-
import { ArrayBufferExpanderFilter } from "../src/utilities/ArrayBufferExpanderListener.js";
|
|
3
|
-
|
|
4
|
-
describe("ArrayBufferExpanderFilter", () => {
|
|
5
|
-
describe("filter integration", () => {
|
|
6
|
-
it("can be used as a filter in DicomMetadataListener", () => {
|
|
7
|
-
const listener = new DicomMetadataListener(
|
|
8
|
-
ArrayBufferExpanderFilter
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
expect(listener.filters).toContain(ArrayBufferExpanderFilter);
|
|
12
|
-
expect(typeof listener.value).toBe("function");
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
describe("filter behavior", () => {
|
|
17
|
-
it("allows normal listener operations to work", () => {
|
|
18
|
-
const listener = new DicomMetadataListener(
|
|
19
|
-
ArrayBufferExpanderFilter
|
|
20
|
-
);
|
|
21
|
-
|
|
22
|
-
const tag = "00100010";
|
|
23
|
-
const tagInfo = { vr: "PN", length: 10 };
|
|
24
|
-
|
|
25
|
-
listener.startObject({});
|
|
26
|
-
listener.addTag(tag, tagInfo);
|
|
27
|
-
|
|
28
|
-
expect(listener.current.tag).toBe(tag);
|
|
29
|
-
expect(listener.current.vr).toBe("PN");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("maintains listener state correctly", () => {
|
|
33
|
-
const listener = new DicomMetadataListener(
|
|
34
|
-
ArrayBufferExpanderFilter
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const dest = { testKey: "testValue" };
|
|
38
|
-
listener.startObject(dest);
|
|
39
|
-
|
|
40
|
-
expect(listener.current.dest).toBe(dest);
|
|
41
|
-
expect(listener.current.type).toBe("object");
|
|
42
|
-
|
|
43
|
-
const result = listener.pop();
|
|
44
|
-
expect(result).toBe(dest);
|
|
45
|
-
expect(listener.current).toBe(null);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe("value() method - ArrayBuffer[] expansion", () => {
|
|
50
|
-
it("expands ArrayBuffer[] into startObject[] + multiple value calls + pop", () => {
|
|
51
|
-
// Track the sequence of calls using a custom tracking filter
|
|
52
|
-
const callSequence = [];
|
|
53
|
-
const trackingFilter = {
|
|
54
|
-
startObject(next, dest) {
|
|
55
|
-
callSequence.push({ method: "startObject" });
|
|
56
|
-
return next(dest);
|
|
57
|
-
},
|
|
58
|
-
value(next, v) {
|
|
59
|
-
callSequence.push({ method: "value", value: v });
|
|
60
|
-
return next(v);
|
|
61
|
-
},
|
|
62
|
-
pop(next) {
|
|
63
|
-
callSequence.push({ method: "pop" });
|
|
64
|
-
return next();
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Create listener with ArrayBufferExpanderFilter first, then tracking
|
|
69
|
-
const listener = new DicomMetadataListener(
|
|
70
|
-
ArrayBufferExpanderFilter,
|
|
71
|
-
trackingFilter
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
// Setup: create a tag to hold the value
|
|
75
|
-
listener.startObject({});
|
|
76
|
-
listener.addTag("7FE00010", { vr: "OB" }); // Pixel Data
|
|
77
|
-
|
|
78
|
-
// Clear the sequence after setup
|
|
79
|
-
callSequence.length = 0;
|
|
80
|
-
|
|
81
|
-
// Create ArrayBuffer array
|
|
82
|
-
const buffer1 = new ArrayBuffer(100);
|
|
83
|
-
const buffer2 = new ArrayBuffer(200);
|
|
84
|
-
const buffer3 = new ArrayBuffer(300);
|
|
85
|
-
const arrayBuffers = [buffer1, buffer2, buffer3];
|
|
86
|
-
|
|
87
|
-
// Pass the ArrayBuffer[] to the listener
|
|
88
|
-
listener.value(arrayBuffers);
|
|
89
|
-
|
|
90
|
-
// Verify the call sequence
|
|
91
|
-
// startObject([]) calls value([]) internally, then 3 fragment values, then pop
|
|
92
|
-
expect(callSequence.length).toBe(6); // startObject([]) + value([]) + 3 fragment values + pop
|
|
93
|
-
expect(callSequence[0].method).toBe("startObject");
|
|
94
|
-
expect(callSequence[1].method).toBe("value");
|
|
95
|
-
expect(Array.isArray(callSequence[1].value)).toBe(true); // value([]) from startObject
|
|
96
|
-
expect(callSequence[2].method).toBe("value");
|
|
97
|
-
expect(callSequence[2].value).toBe(buffer1);
|
|
98
|
-
expect(callSequence[3].method).toBe("value");
|
|
99
|
-
expect(callSequence[3].value).toBe(buffer2);
|
|
100
|
-
expect(callSequence[4].method).toBe("value");
|
|
101
|
-
expect(callSequence[4].value).toBe(buffer3);
|
|
102
|
-
expect(callSequence[5].method).toBe("pop");
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("expands Uint8Array[] (typed array views) into fragments", () => {
|
|
106
|
-
const callSequence = [];
|
|
107
|
-
const trackingFilter = {
|
|
108
|
-
startObject(next, dest) {
|
|
109
|
-
callSequence.push({ method: "startObject" });
|
|
110
|
-
return next(dest);
|
|
111
|
-
},
|
|
112
|
-
value(next, v) {
|
|
113
|
-
callSequence.push({ method: "value", value: v });
|
|
114
|
-
return next(v);
|
|
115
|
-
},
|
|
116
|
-
pop(next) {
|
|
117
|
-
callSequence.push({ method: "pop" });
|
|
118
|
-
return next();
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const listener = new DicomMetadataListener(
|
|
123
|
-
ArrayBufferExpanderFilter,
|
|
124
|
-
trackingFilter
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
// Setup
|
|
128
|
-
listener.startObject({});
|
|
129
|
-
listener.addTag("7FE00010", { vr: "OB" });
|
|
130
|
-
|
|
131
|
-
// Clear the sequence after setup
|
|
132
|
-
callSequence.length = 0;
|
|
133
|
-
|
|
134
|
-
// Create typed array views
|
|
135
|
-
const view1 = new Uint8Array(50);
|
|
136
|
-
const view2 = new Uint8Array(150);
|
|
137
|
-
const typedArrays = [view1, view2];
|
|
138
|
-
|
|
139
|
-
// Pass the typed array views to the listener
|
|
140
|
-
listener.value(typedArrays);
|
|
141
|
-
|
|
142
|
-
// Verify expansion
|
|
143
|
-
// startObject([]) calls value([]) internally, then 2 fragment values, then pop
|
|
144
|
-
expect(callSequence.length).toBe(5); // startObject([]) + value([]) + 2 fragment values + pop
|
|
145
|
-
expect(callSequence[0].method).toBe("startObject");
|
|
146
|
-
expect(callSequence[1].method).toBe("value");
|
|
147
|
-
expect(Array.isArray(callSequence[1].value)).toBe(true); // value([]) from startObject
|
|
148
|
-
expect(callSequence[2].method).toBe("value");
|
|
149
|
-
expect(callSequence[2].value).toBe(view1);
|
|
150
|
-
expect(callSequence[3].method).toBe("value");
|
|
151
|
-
expect(callSequence[3].value).toBe(view2);
|
|
152
|
-
expect(callSequence[4].method).toBe("pop");
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it("passes through non-ArrayBuffer[] values unchanged", () => {
|
|
156
|
-
const listener = new DicomMetadataListener(
|
|
157
|
-
ArrayBufferExpanderFilter
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
// Setup
|
|
161
|
-
listener.startObject({});
|
|
162
|
-
listener.addTag("00100010", { vr: "PN" });
|
|
163
|
-
|
|
164
|
-
// Pass through string value
|
|
165
|
-
listener.value("Test^Patient");
|
|
166
|
-
listener.pop();
|
|
167
|
-
|
|
168
|
-
const result = listener.pop();
|
|
169
|
-
expect(result["00100010"].Value).toEqual(["Test^Patient"]);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("passes through number values unchanged", () => {
|
|
173
|
-
const listener = new DicomMetadataListener(
|
|
174
|
-
ArrayBufferExpanderFilter
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
// Setup
|
|
178
|
-
listener.startObject({});
|
|
179
|
-
listener.addTag("00200013", { vr: "IS" }); // Instance Number
|
|
180
|
-
|
|
181
|
-
// Pass through number value
|
|
182
|
-
listener.value(1);
|
|
183
|
-
listener.pop();
|
|
184
|
-
|
|
185
|
-
const result = listener.pop();
|
|
186
|
-
expect(result["00200013"].Value).toEqual([1]);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("passes through single ArrayBuffer unchanged", () => {
|
|
190
|
-
const listener = new DicomMetadataListener(
|
|
191
|
-
ArrayBufferExpanderFilter
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
// Setup
|
|
195
|
-
listener.startObject({});
|
|
196
|
-
listener.addTag("7FE00010", { vr: "OB" });
|
|
197
|
-
|
|
198
|
-
// Pass through single ArrayBuffer (not in array)
|
|
199
|
-
const buffer = new ArrayBuffer(1000);
|
|
200
|
-
listener.value(buffer);
|
|
201
|
-
listener.pop();
|
|
202
|
-
|
|
203
|
-
const result = listener.pop();
|
|
204
|
-
expect(result["7FE00010"].Value).toEqual([buffer]);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it("passes through arrays of strings unchanged", () => {
|
|
208
|
-
const listener = new DicomMetadataListener(
|
|
209
|
-
ArrayBufferExpanderFilter
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
// Setup
|
|
213
|
-
listener.startObject({});
|
|
214
|
-
listener.addTag("00080060", { vr: "CS" }); // Modality
|
|
215
|
-
|
|
216
|
-
// Pass through string array (not ArrayBuffer[])
|
|
217
|
-
const stringArray = ["CT", "MR"];
|
|
218
|
-
listener.value(stringArray);
|
|
219
|
-
listener.pop();
|
|
220
|
-
|
|
221
|
-
const result = listener.pop();
|
|
222
|
-
expect(result["00080060"].Value).toEqual([stringArray]);
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it("does not expand empty arrays", () => {
|
|
226
|
-
const listener = new DicomMetadataListener(
|
|
227
|
-
ArrayBufferExpanderFilter
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
// Setup
|
|
231
|
-
listener.startObject({});
|
|
232
|
-
listener.addTag("7FE00010", { vr: "OB" });
|
|
233
|
-
|
|
234
|
-
// Pass through empty array
|
|
235
|
-
const emptyArray = [];
|
|
236
|
-
listener.value(emptyArray);
|
|
237
|
-
listener.pop();
|
|
238
|
-
|
|
239
|
-
const result = listener.pop();
|
|
240
|
-
// Should pass through unchanged
|
|
241
|
-
expect(result["7FE00010"].Value).toEqual([emptyArray]);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("does not expand mixed arrays (ArrayBuffer + other types)", () => {
|
|
245
|
-
const listener = new DicomMetadataListener(
|
|
246
|
-
ArrayBufferExpanderFilter
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
// Setup
|
|
250
|
-
listener.startObject({});
|
|
251
|
-
listener.addTag("7FE00010", { vr: "OB" });
|
|
252
|
-
|
|
253
|
-
// Create mixed array (should not be expanded)
|
|
254
|
-
const buffer = new ArrayBuffer(100);
|
|
255
|
-
const mixedArray = [buffer, "not an array buffer"];
|
|
256
|
-
listener.value(mixedArray);
|
|
257
|
-
listener.pop();
|
|
258
|
-
|
|
259
|
-
const result = listener.pop();
|
|
260
|
-
// Should pass through unchanged
|
|
261
|
-
expect(result["7FE00010"].Value).toEqual([mixedArray]);
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
describe("listener methods integration", () => {
|
|
266
|
-
it("allows access to fmi property", () => {
|
|
267
|
-
const listener = new DicomMetadataListener(
|
|
268
|
-
ArrayBufferExpanderFilter
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
const fmi = { "00020010": { Value: ["1.2.840.10008.1.2.1"] } };
|
|
272
|
-
listener.fmi = fmi;
|
|
273
|
-
|
|
274
|
-
expect(listener.fmi).toBe(fmi);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it("allows access to dict property", () => {
|
|
278
|
-
const listener = new DicomMetadataListener(
|
|
279
|
-
ArrayBufferExpanderFilter
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
const dict = { "00100010": { Value: ["Test^Patient"] } };
|
|
283
|
-
listener.dict = dict;
|
|
284
|
-
|
|
285
|
-
expect(listener.dict).toBe(dict);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it("allows access to current property", () => {
|
|
289
|
-
const listener = new DicomMetadataListener(
|
|
290
|
-
ArrayBufferExpanderFilter
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
listener.startObject({});
|
|
294
|
-
const current = listener.current;
|
|
295
|
-
|
|
296
|
-
expect(current).toBeTruthy();
|
|
297
|
-
expect(current.type).toBe("object");
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
it("allows use of getTransferSyntaxUID", () => {
|
|
301
|
-
const listener = new DicomMetadataListener(
|
|
302
|
-
ArrayBufferExpanderFilter
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
listener.fmi = {
|
|
306
|
-
"00020010": { Value: ["1.2.840.10008.1.2.1"] }
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
expect(listener.getTransferSyntaxUID()).toBe("1.2.840.10008.1.2.1");
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
describe("real-world usage patterns", () => {
|
|
314
|
-
it("correctly builds nested structure with expanded fragments", () => {
|
|
315
|
-
const listener = new DicomMetadataListener(
|
|
316
|
-
ArrayBufferExpanderFilter
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
const dict = {};
|
|
320
|
-
listener.startObject(dict);
|
|
321
|
-
|
|
322
|
-
// Add pixel data tag
|
|
323
|
-
listener.addTag("7FE00010", { vr: "OB" });
|
|
324
|
-
|
|
325
|
-
// Pass array of ArrayBuffers (simulating fragmented frame)
|
|
326
|
-
const fragment1 = new ArrayBuffer(100);
|
|
327
|
-
const fragment2 = new ArrayBuffer(150);
|
|
328
|
-
const fragment3 = new ArrayBuffer(200);
|
|
329
|
-
listener.value([fragment1, fragment2, fragment3]);
|
|
330
|
-
|
|
331
|
-
listener.pop(); // Pop the tag
|
|
332
|
-
const result = listener.pop(); // Pop the object
|
|
333
|
-
|
|
334
|
-
// The resulting structure should have an array of fragments
|
|
335
|
-
expect(result["7FE00010"].Value).toBeInstanceOf(Array);
|
|
336
|
-
expect(result["7FE00010"].Value.length).toBe(3);
|
|
337
|
-
expect(result["7FE00010"].Value[0]).toBe(fragment1);
|
|
338
|
-
expect(result["7FE00010"].Value[1]).toBe(fragment2);
|
|
339
|
-
expect(result["7FE00010"].Value[2]).toBe(fragment3);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it("can be combined with other filters", () => {
|
|
343
|
-
let loggedValue = null;
|
|
344
|
-
const loggingFilter = {
|
|
345
|
-
value(next, v) {
|
|
346
|
-
loggedValue = v;
|
|
347
|
-
return next(v);
|
|
348
|
-
}
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
const listener = new DicomMetadataListener(
|
|
352
|
-
ArrayBufferExpanderFilter,
|
|
353
|
-
loggingFilter
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
listener.startObject({});
|
|
357
|
-
listener.addTag("00100010", { vr: "PN" });
|
|
358
|
-
listener.value("Test^Patient");
|
|
359
|
-
listener.pop();
|
|
360
|
-
listener.pop();
|
|
361
|
-
|
|
362
|
-
expect(loggedValue).toBe("Test^Patient");
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
});
|
package/test/DICOMWEB.test.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
it("No tests yet", () => {});
|