dicom-synth 1.17.0 → 1.19.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 +1 -1
- package/bin/dicom-synth-describe.mjs +12 -1
- package/dist/esm/collection/generate.js +977 -0
- package/dist/esm/collection/writer.js +243 -109
- package/dist/esm/describe/describe.js +281 -25
- package/dist/esm/index.js +535 -243
- package/dist/esm/nonStandardDicom/dicomdir.js +14 -15
- package/dist/esm/platform/bytes.js +39 -0
- package/dist/esm/platform/random.js +22 -0
- package/dist/esm/platform/sha256.js +131 -0
- package/dist/esm/schema/parametric.js +73 -4
- package/dist/esm/schema/validate.js +1 -0
- package/dist/esm/syntheticFixtures/generator.js +31 -14
- package/dist/esm/syntheticFixtures/streamWrite.js +156 -13
- package/dist/esm/syntheticFixtures/uid.js +148 -4
- package/dist/esm/syntheticFixtures/violations.js +18 -6
- package/dist/types/collection/generate.d.ts +43 -0
- package/dist/types/collection/writer.d.ts +0 -27
- package/dist/types/describe/describe.d.ts +12 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/nonStandardDicom/dicomdir.d.ts +1 -6
- package/dist/types/platform/bytes.d.ts +4 -0
- package/dist/types/platform/random.d.ts +2 -0
- package/dist/types/platform/sha256.d.ts +1 -0
- package/dist/types/schema/validate.d.ts +1 -0
- package/dist/types/syntheticFixtures/generator.d.ts +2 -2
- package/dist/types/syntheticFixtures/violations.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,156 @@
|
|
|
1
|
+
// src/platform/bytes.ts
|
|
2
|
+
function bytesToHex(bytes) {
|
|
3
|
+
let hex = "";
|
|
4
|
+
for (const byte of bytes) {
|
|
5
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
6
|
+
}
|
|
7
|
+
return hex;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/platform/random.ts
|
|
11
|
+
function randomHex(byteLength) {
|
|
12
|
+
return bytesToHex(
|
|
13
|
+
globalThis.crypto.getRandomValues(new Uint8Array(byteLength))
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/platform/sha256.ts
|
|
18
|
+
var K = new Uint32Array([
|
|
19
|
+
1116352408,
|
|
20
|
+
1899447441,
|
|
21
|
+
3049323471,
|
|
22
|
+
3921009573,
|
|
23
|
+
961987163,
|
|
24
|
+
1508970993,
|
|
25
|
+
2453635748,
|
|
26
|
+
2870763221,
|
|
27
|
+
3624381080,
|
|
28
|
+
310598401,
|
|
29
|
+
607225278,
|
|
30
|
+
1426881987,
|
|
31
|
+
1925078388,
|
|
32
|
+
2162078206,
|
|
33
|
+
2614888103,
|
|
34
|
+
3248222580,
|
|
35
|
+
3835390401,
|
|
36
|
+
4022224774,
|
|
37
|
+
264347078,
|
|
38
|
+
604807628,
|
|
39
|
+
770255983,
|
|
40
|
+
1249150122,
|
|
41
|
+
1555081692,
|
|
42
|
+
1996064986,
|
|
43
|
+
2554220882,
|
|
44
|
+
2821834349,
|
|
45
|
+
2952996808,
|
|
46
|
+
3210313671,
|
|
47
|
+
3336571891,
|
|
48
|
+
3584528711,
|
|
49
|
+
113926993,
|
|
50
|
+
338241895,
|
|
51
|
+
666307205,
|
|
52
|
+
773529912,
|
|
53
|
+
1294757372,
|
|
54
|
+
1396182291,
|
|
55
|
+
1695183700,
|
|
56
|
+
1986661051,
|
|
57
|
+
2177026350,
|
|
58
|
+
2456956037,
|
|
59
|
+
2730485921,
|
|
60
|
+
2820302411,
|
|
61
|
+
3259730800,
|
|
62
|
+
3345764771,
|
|
63
|
+
3516065817,
|
|
64
|
+
3600352804,
|
|
65
|
+
4094571909,
|
|
66
|
+
275423344,
|
|
67
|
+
430227734,
|
|
68
|
+
506948616,
|
|
69
|
+
659060556,
|
|
70
|
+
883997877,
|
|
71
|
+
958139571,
|
|
72
|
+
1322822218,
|
|
73
|
+
1537002063,
|
|
74
|
+
1747873779,
|
|
75
|
+
1955562222,
|
|
76
|
+
2024104815,
|
|
77
|
+
2227730452,
|
|
78
|
+
2361852424,
|
|
79
|
+
2428436474,
|
|
80
|
+
2756734187,
|
|
81
|
+
3204031479,
|
|
82
|
+
3329325298
|
|
83
|
+
]);
|
|
84
|
+
var rotr = (x, n) => x >>> n | x << 32 - n;
|
|
85
|
+
function sha256(input) {
|
|
86
|
+
const data = typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
87
|
+
const padded = new Uint8Array((data.length + 8 >> 6) + 1 << 6);
|
|
88
|
+
padded.set(data);
|
|
89
|
+
padded[data.length] = 128;
|
|
90
|
+
new DataView(padded.buffer).setBigUint64(
|
|
91
|
+
padded.length - 8,
|
|
92
|
+
BigInt(data.length * 8),
|
|
93
|
+
false
|
|
94
|
+
);
|
|
95
|
+
const h = new Uint32Array([
|
|
96
|
+
1779033703,
|
|
97
|
+
3144134277,
|
|
98
|
+
1013904242,
|
|
99
|
+
2773480762,
|
|
100
|
+
1359893119,
|
|
101
|
+
2600822924,
|
|
102
|
+
528734635,
|
|
103
|
+
1541459225
|
|
104
|
+
]);
|
|
105
|
+
const w = new Uint32Array(64);
|
|
106
|
+
const view = new DataView(padded.buffer);
|
|
107
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
108
|
+
for (let t = 0; t < 16; t++) w[t] = view.getUint32(offset + t * 4, false);
|
|
109
|
+
for (let t = 16; t < 64; t++) {
|
|
110
|
+
const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ w[t - 15] >>> 3;
|
|
111
|
+
const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ w[t - 2] >>> 10;
|
|
112
|
+
w[t] = w[t - 16] + s0 + w[t - 7] + s1 >>> 0;
|
|
113
|
+
}
|
|
114
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
115
|
+
for (let t = 0; t < 64; t++) {
|
|
116
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
117
|
+
const ch = e & f ^ ~e & g;
|
|
118
|
+
const temp1 = hh + S1 + ch + K[t] + w[t] >>> 0;
|
|
119
|
+
const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
120
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
121
|
+
const temp2 = S0 + maj >>> 0;
|
|
122
|
+
hh = g;
|
|
123
|
+
g = f;
|
|
124
|
+
f = e;
|
|
125
|
+
e = d + temp1 >>> 0;
|
|
126
|
+
d = c;
|
|
127
|
+
c = b;
|
|
128
|
+
b = a;
|
|
129
|
+
a = temp1 + temp2 >>> 0;
|
|
130
|
+
}
|
|
131
|
+
h[0] = h[0] + a >>> 0;
|
|
132
|
+
h[1] = h[1] + b >>> 0;
|
|
133
|
+
h[2] = h[2] + c >>> 0;
|
|
134
|
+
h[3] = h[3] + d >>> 0;
|
|
135
|
+
h[4] = h[4] + e >>> 0;
|
|
136
|
+
h[5] = h[5] + f >>> 0;
|
|
137
|
+
h[6] = h[6] + g >>> 0;
|
|
138
|
+
h[7] = h[7] + hh >>> 0;
|
|
139
|
+
}
|
|
140
|
+
const out = new Uint8Array(32);
|
|
141
|
+
const outView = new DataView(out.buffer);
|
|
142
|
+
for (let i = 0; i < 8; i++) outView.setUint32(i * 4, h[i], false);
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
|
|
1
146
|
// src/syntheticFixtures/uid.ts
|
|
2
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
3
147
|
function hashUid(salt, key) {
|
|
4
|
-
const digest =
|
|
5
|
-
const n = BigInt(`0x${digest.subarray(0, 16)
|
|
148
|
+
const digest = sha256(`${salt} ${key}`);
|
|
149
|
+
const n = BigInt(`0x${bytesToHex(digest.subarray(0, 16))}`);
|
|
6
150
|
return `2.25.${n.toString()}`;
|
|
7
151
|
}
|
|
8
152
|
function randomUid() {
|
|
9
|
-
const n = BigInt(`0x${
|
|
153
|
+
const n = BigInt(`0x${randomHex(16)}`);
|
|
10
154
|
return `2.25.${n.toString()}`;
|
|
11
155
|
}
|
|
12
156
|
function seedToSalt(seed) {
|
|
@@ -17,6 +17,17 @@ function loadDcmjs() {
|
|
|
17
17
|
}
|
|
18
18
|
var dcmjs = loadDcmjs();
|
|
19
19
|
|
|
20
|
+
// src/platform/bytes.ts
|
|
21
|
+
function concatBytes(...parts) {
|
|
22
|
+
const out = new Uint8Array(parts.reduce((sum, p) => sum + p.length, 0));
|
|
23
|
+
let offset = 0;
|
|
24
|
+
for (const part of parts) {
|
|
25
|
+
out.set(part, offset);
|
|
26
|
+
offset += part.length;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
// src/syntheticFixtures/violations.ts
|
|
21
32
|
var dcmjsAny = dcmjs;
|
|
22
33
|
function parseBuffer(buffer) {
|
|
@@ -27,7 +38,7 @@ function parseBuffer(buffer) {
|
|
|
27
38
|
return dcmjsAny.data.DicomMessage.readFile(ab);
|
|
28
39
|
}
|
|
29
40
|
function reserialize(parsed) {
|
|
30
|
-
return
|
|
41
|
+
return new Uint8Array(parsed.write({ allowInvalidVRLength: true }));
|
|
31
42
|
}
|
|
32
43
|
var OVERLONG_UID = `2.25.${"0".repeat(60)}`;
|
|
33
44
|
var VIOLATION_LEVEL = {
|
|
@@ -92,11 +103,12 @@ function applyByteLevel(buffer, violations) {
|
|
|
92
103
|
result = result.subarray(132);
|
|
93
104
|
break;
|
|
94
105
|
case "malformed-sq-delimiter": {
|
|
95
|
-
const delimiter =
|
|
96
|
-
delimiter.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
const delimiter = new Uint8Array(8);
|
|
107
|
+
const view = new DataView(delimiter.buffer);
|
|
108
|
+
view.setUint16(0, 65534, true);
|
|
109
|
+
view.setUint16(2, 57565, true);
|
|
110
|
+
view.setUint32(4, 4, true);
|
|
111
|
+
result = concatBytes(result, delimiter);
|
|
100
112
|
break;
|
|
101
113
|
}
|
|
102
114
|
default:
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { DatasetSpec, FileSpec } from '../schema/types.js';
|
|
2
|
+
import { type TagContext } from '../syntheticFixtures/tagTemplate.js';
|
|
3
|
+
import type { UidSet } from '../syntheticFixtures/uid.js';
|
|
4
|
+
export type GeneratedFile = {
|
|
5
|
+
filename: string;
|
|
6
|
+
relativePath: string;
|
|
7
|
+
buffer: Uint8Array;
|
|
8
|
+
type: FileSpec['type'];
|
|
9
|
+
index: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function resolveUid(salt: string | undefined, index: number, override?: Partial<UidSet>): UidSet;
|
|
12
|
+
export declare function effectiveSalt(spec: {
|
|
13
|
+
uidSalt?: string;
|
|
14
|
+
seed?: number;
|
|
15
|
+
}): string | undefined;
|
|
16
|
+
export declare function generateFile(spec: FileSpec, options?: {
|
|
17
|
+
index?: number;
|
|
18
|
+
seed?: number;
|
|
19
|
+
uidSalt?: string;
|
|
20
|
+
padWidth?: number;
|
|
21
|
+
uid?: Partial<UidSet>;
|
|
22
|
+
context?: Partial<TagContext>;
|
|
23
|
+
}): Promise<GeneratedFile>;
|
|
24
|
+
export declare function withResolvedSeed(spec: DatasetSpec): DatasetSpec;
|
|
25
|
+
export declare function withResolvedSalt(spec: DatasetSpec): DatasetSpec;
|
|
26
|
+
export type FilePlan = {
|
|
27
|
+
fileSpec: FileSpec;
|
|
28
|
+
index: number;
|
|
29
|
+
uidOverride?: Partial<UidSet>;
|
|
30
|
+
context: TagContext;
|
|
31
|
+
filename: string;
|
|
32
|
+
relativePath: string;
|
|
33
|
+
};
|
|
34
|
+
export declare function planCollection(spec: DatasetSpec): Generator<FilePlan>;
|
|
35
|
+
export declare function materialisePlan(plan: FilePlan, salt: string | undefined): Promise<GeneratedFile>;
|
|
36
|
+
export declare function generateCollectionFromSpec(spec: DatasetSpec): AsyncGenerator<GeneratedFile>;
|
|
37
|
+
export type CollectionPreviewItem = {
|
|
38
|
+
relativePath: string;
|
|
39
|
+
type: FileSpec['type'];
|
|
40
|
+
index: number;
|
|
41
|
+
approxBytes: number;
|
|
42
|
+
};
|
|
43
|
+
export declare function previewCollection(spec: DatasetSpec): AsyncGenerator<CollectionPreviewItem>;
|
|
@@ -1,34 +1,7 @@
|
|
|
1
1
|
import type { DatasetSpec, FileSpec } from '../schema/types.js';
|
|
2
|
-
import { type TagContext } from '../syntheticFixtures/tagTemplate.js';
|
|
3
|
-
import type { UidSet } from '../syntheticFixtures/uid.js';
|
|
4
|
-
export type GeneratedFile = {
|
|
5
|
-
filename: string;
|
|
6
|
-
relativePath: string;
|
|
7
|
-
buffer: Buffer;
|
|
8
|
-
type: FileSpec['type'];
|
|
9
|
-
index: number;
|
|
10
|
-
};
|
|
11
2
|
export type CollectionManifest = Array<{
|
|
12
3
|
path: string;
|
|
13
4
|
type: FileSpec['type'];
|
|
14
5
|
index: number;
|
|
15
6
|
}>;
|
|
16
|
-
export declare function generateFile(spec: FileSpec, options?: {
|
|
17
|
-
index?: number;
|
|
18
|
-
seed?: number;
|
|
19
|
-
uidSalt?: string;
|
|
20
|
-
padWidth?: number;
|
|
21
|
-
uid?: Partial<UidSet>;
|
|
22
|
-
context?: Partial<TagContext>;
|
|
23
|
-
}): Promise<GeneratedFile>;
|
|
24
|
-
export declare function withResolvedSeed(spec: DatasetSpec): DatasetSpec;
|
|
25
|
-
export declare function withResolvedSalt(spec: DatasetSpec): DatasetSpec;
|
|
26
|
-
export declare function generateCollectionFromSpec(spec: DatasetSpec): AsyncGenerator<GeneratedFile>;
|
|
27
|
-
export type CollectionPreviewItem = {
|
|
28
|
-
relativePath: string;
|
|
29
|
-
type: FileSpec['type'];
|
|
30
|
-
index: number;
|
|
31
|
-
approxBytes: number;
|
|
32
|
-
};
|
|
33
|
-
export declare function previewCollection(spec: DatasetSpec): AsyncGenerator<CollectionPreviewItem>;
|
|
34
7
|
export declare function writeCollectionFromSpec(spec: DatasetSpec, outDir: string): Promise<CollectionManifest>;
|
|
@@ -4,6 +4,7 @@ export type DescribeOptions = {
|
|
|
4
4
|
sizeTolerance?: number;
|
|
5
5
|
preserveUids?: boolean;
|
|
6
6
|
uidSalt?: string;
|
|
7
|
+
keepPrivateTags?: boolean;
|
|
7
8
|
captureTree?: boolean;
|
|
8
9
|
};
|
|
9
10
|
export type DescribeResult = {
|
|
@@ -13,8 +14,18 @@ export type DescribeResult = {
|
|
|
13
14
|
skipped: number;
|
|
14
15
|
unknownModality: number;
|
|
15
16
|
nonDicomFiles: number;
|
|
17
|
+
privateBinarySkipped: number;
|
|
16
18
|
};
|
|
17
19
|
};
|
|
18
|
-
|
|
20
|
+
type RawDictElement = {
|
|
21
|
+
vr?: string;
|
|
22
|
+
Value?: unknown[];
|
|
23
|
+
};
|
|
24
|
+
export type ParsedFile = {
|
|
25
|
+
record: Record<string, unknown>;
|
|
26
|
+
rawDict: Record<string, RawDictElement>;
|
|
27
|
+
};
|
|
28
|
+
export declare function readHeaderOnly(path: string): ParsedFile | null;
|
|
19
29
|
export declare function tagValuesEqual(a: unknown, b: unknown): boolean;
|
|
20
30
|
export declare function describeDirectory(dir: string, options?: DescribeOptions): DescribeResult;
|
|
31
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { type
|
|
1
|
+
export { type CollectionPreviewItem, type GeneratedFile, generateCollectionFromSpec, generateFile, previewCollection, withResolvedSalt, withResolvedSeed, } from './collection/generate.js';
|
|
2
|
+
export { type CollectionManifest, writeCollectionFromSpec, } from './collection/writer.js';
|
|
2
3
|
export { type DescribeOptions, type DescribeResult, describeDirectory, } from './describe/describe.js';
|
|
3
4
|
export { defaultPublicCasesPath, loadCaseById, loadCasesFromJson, loadDefaultCases, type PublicCaseRecord, type PublicCaseSource, } from './public-fixtures/catalog.js';
|
|
4
5
|
export { caseCachePath, fetchPublicCaseToCache, verifySha256, } from './public-fixtures/fetch.js';
|
|
@@ -7,9 +7,4 @@ export declare const DICOMDIR_SOP_CLASS_UID = "1.2.840.10008.1.3.10";
|
|
|
7
7
|
* so it passes signature checks but is detectable by SOP class UID.
|
|
8
8
|
* dcmjs cannot write DICOMDIR, so this uses a raw byte layout.
|
|
9
9
|
*/
|
|
10
|
-
export declare function buildDicomdirBuffer():
|
|
11
|
-
/**
|
|
12
|
-
* Write a minimal DICOMDIR-signature file to `filePath`.
|
|
13
|
-
* The parent directory is created if it does not exist.
|
|
14
|
-
*/
|
|
15
|
-
export declare function writeDicomdirFile(filePath: string): void;
|
|
10
|
+
export declare function buildDicomdirBuffer(): Uint8Array;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function bytesToHex(bytes: Uint8Array): string;
|
|
2
|
+
export declare function utf8Bytes(text: string): Uint8Array;
|
|
3
|
+
export declare function concatBytes(...parts: Uint8Array[]): Uint8Array;
|
|
4
|
+
export declare function patternBytes(length: number, pattern: string): Uint8Array;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sha256(input: string | Uint8Array): Uint8Array;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DatasetSpec, FileSpec, ParametricSpec } from './types.js';
|
|
2
2
|
export declare function isImageType(type: FileSpec['type']): boolean;
|
|
3
3
|
export declare const HEX_TAG_RE: RegExp;
|
|
4
|
+
export declare function isPrivateHexTag(key: string): boolean;
|
|
4
5
|
export declare function isRawTagValue(value: unknown): value is {
|
|
5
6
|
vr: string;
|
|
6
7
|
value: unknown;
|
|
@@ -9,9 +9,9 @@ export declare function applyTagOverrides(dataset: Record<string, unknown>, tags
|
|
|
9
9
|
export declare function syncMetaWithDataset(meta: Record<string, unknown>, dataset: Record<string, unknown>, rawElements?: RawElementMap): void;
|
|
10
10
|
export declare function buildMeta(uid: UidSet, modality: Modality, transferSyntax?: TransferSyntax): Record<string, unknown>;
|
|
11
11
|
export declare function buildBaseImageDataset(uid: UidSet, modality: Modality): Record<string, unknown>;
|
|
12
|
-
export declare function serializeDict(meta: Record<string, unknown>, dataset: Record<string, unknown>, rawElements?: RawElementMap):
|
|
12
|
+
export declare function serializeDict(meta: Record<string, unknown>, dataset: Record<string, unknown>, rawElements?: RawElementMap): Uint8Array;
|
|
13
13
|
export declare function sizedNonDicomBytes(spec: NonDicomSpec): number | undefined;
|
|
14
14
|
export declare function buildBufferForSpec(spec: FileSpec, uid: UidSet): {
|
|
15
|
-
buffer:
|
|
15
|
+
buffer: Uint8Array;
|
|
16
16
|
rawElements?: RawElementMap;
|
|
17
17
|
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ViolationClass } from '../schema/types.js';
|
|
2
2
|
import type { RawElementMap } from './generator.js';
|
|
3
|
-
export declare function applyViolations(buffer:
|
|
3
|
+
export declare function applyViolations(buffer: Uint8Array, violations: ViolationClass[], rawElements?: RawElementMap): Uint8Array;
|