@fnndsc/salsa 3.15.0 → 3.17.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.
@@ -0,0 +1,80 @@
1
+ import { type Result } from '@fnndsc/cumin';
2
+ import { type DicomTagGroup } from './dictionary.js';
3
+ /** One element of a DICOM dataset, as every surface sees it. */
4
+ export interface DicomTag {
5
+ /** `(gggg,eeee)`, upper-case hex. */
6
+ tag: string;
7
+ /** Dictionary name; `PrivateTag` or `Unknown` when the dictionary has none. */
8
+ name: string;
9
+ vr: string;
10
+ /** The value in text. Multi-valued elements join with `\`; bulk data reads `<N bytes>`. */
11
+ value: string;
12
+ group: DicomTagGroup;
13
+ /** True when the tag identifies a person or an institution. */
14
+ phi: boolean;
15
+ /** Words for a coded value, when the kernel knows them. */
16
+ decoded?: string;
17
+ /** Sequence items, each a dataset of its own. */
18
+ items?: DicomTag[][];
19
+ }
20
+ /** Everything read from one file. */
21
+ export interface DicomTagSet {
22
+ tags: DicomTag[];
23
+ /** From the file meta group; absent for a bare dataset. */
24
+ transferSyntaxUID?: string;
25
+ }
26
+ /** A tag whose value changes across a folder. */
27
+ export interface DicomVaryingTag {
28
+ tag: string;
29
+ name: string;
30
+ vr: string;
31
+ group: DicomTagGroup;
32
+ phi: boolean;
33
+ /** How many distinct values the folder holds. */
34
+ distinct: number;
35
+ /** The value in the first file, in folder order. */
36
+ first: string;
37
+ /** The value in the last file, in folder order. */
38
+ last: string;
39
+ /** Every file's value, in folder order. Files without the tag carry `null`. */
40
+ values: Array<{
41
+ path: string;
42
+ value: string | null;
43
+ }>;
44
+ }
45
+ /** What a folder of files shares and what changes across it. */
46
+ export interface DicomTagsSummary {
47
+ constant: DicomTag[];
48
+ varying: DicomVaryingTag[];
49
+ }
50
+ /**
51
+ * Reads the elements of a DICOM file.
52
+ *
53
+ * @param bytes - The whole file, Part 10 header included.
54
+ * @returns The tag set, or an error when the bytes are not a DICOM file.
55
+ */
56
+ export declare function dicomTags_read(bytes: Buffer): Result<DicomTagSet>;
57
+ /**
58
+ * Finds one tag by name in a tag set's top level.
59
+ *
60
+ * @param tags - The tags to search.
61
+ * @param name - The dictionary name.
62
+ * @returns The tag, or undefined.
63
+ */
64
+ export declare function dicomTag_find(tags: DicomTag[], name: string): DicomTag | undefined;
65
+ /**
66
+ * Tells constant from varying across a folder's files, in folder order.
67
+ *
68
+ * A tag is constant when every file carries it with the same value
69
+ * (sequences compare by their items). Anything else, including a tag some
70
+ * files lack, is varying, with its first and last value and its distinct
71
+ * count. Files that failed to read are the caller's to report; they are not
72
+ * passed here.
73
+ *
74
+ * @param files - Each file's path and tag set, in folder order.
75
+ * @returns The summary.
76
+ */
77
+ export declare function dicomTags_summarize(files: Array<{
78
+ path: string;
79
+ tags: DicomTagSet;
80
+ }>): DicomTagsSummary;
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.dicomTags_read = dicomTags_read;
7
+ exports.dicomTag_find = dicomTag_find;
8
+ exports.dicomTags_summarize = dicomTags_summarize;
9
+ /**
10
+ * @file DICOM tag reading: one parser for every surface.
11
+ *
12
+ * A DICOM file is a dictionary of elements. Reading one here gives every
13
+ * surface the same answer: the tag, its dictionary name and VR, its value in
14
+ * text, the module it belongs to, whether it identifies someone, and words
15
+ * for the coded values a reader meets first. Sequences keep their items.
16
+ * Across a folder, the same reader tells constant from varying: what the
17
+ * series shares against what changes slice by slice.
18
+ *
19
+ * Bulk values (pixel data and friends) are never rendered as bytes; the
20
+ * listing says how many bytes there were. Long text is carried whole on the
21
+ * model; a surface that bounds it says so.
22
+ *
23
+ * @module
24
+ */
25
+ const dcmjs_1 = __importDefault(require("dcmjs"));
26
+ const cumin_1 = require("@fnndsc/cumin");
27
+ const dictionary_js_1 = require("./dictionary.js");
28
+ /** VRs whose values are bulk bytes, never text. */
29
+ const BULK_VRS = new Set(['OB', 'OW', 'OF', 'OD', 'OL', 'OV', 'UN']);
30
+ /** Group numbers whose every element is a sequence item boundary or padding. */
31
+ const STRUCTURAL_TAGS = new Set(['FFFEE000', 'FFFEE00D', 'FFFEE0DD']);
32
+ /**
33
+ * Reads the elements of a DICOM file.
34
+ *
35
+ * @param bytes - The whole file, Part 10 header included.
36
+ * @returns The tag set, or an error when the bytes are not a DICOM file.
37
+ */
38
+ function dicomTags_read(bytes) {
39
+ const buffer = new Uint8Array(bytes).slice().buffer;
40
+ try {
41
+ const message = dcmjs_1.default.data.DicomMessage.readFile(buffer, { ignoreErrors: true });
42
+ const meta = dict_flatten(message.meta);
43
+ const body = dict_flatten(message.dict);
44
+ const syntax = meta.find((tag) => tag.name === 'TransferSyntaxUID');
45
+ return (0, cumin_1.Ok)({
46
+ tags: [...meta, ...body],
47
+ ...(syntax !== undefined ? { transferSyntaxUID: syntax.value } : {}),
48
+ });
49
+ }
50
+ catch {
51
+ return (0, cumin_1.Err)();
52
+ }
53
+ }
54
+ /**
55
+ * Turns a raw dictionary into tags, in tag order, sequences nested.
56
+ *
57
+ * @param dict - A dcmjs dictionary keyed by `ggggeeee`.
58
+ * @returns The tags.
59
+ */
60
+ function dict_flatten(dict) {
61
+ const keys = Object.keys(dict)
62
+ .filter((key) => /^[0-9A-Fa-f]{8}$/.test(key) && !STRUCTURAL_TAGS.has(key.toUpperCase()))
63
+ .sort();
64
+ return keys.map((key) => element_toTag(key, dict[key]));
65
+ }
66
+ /**
67
+ * Renders one element as a tag.
68
+ *
69
+ * @param key - The `ggggeeee` key.
70
+ * @param element - The raw element.
71
+ * @returns The tag.
72
+ */
73
+ function element_toTag(key, element) {
74
+ const upper = key.toUpperCase();
75
+ const tag = `(${upper.slice(0, 4)},${upper.slice(4)})`;
76
+ const groupNumber = parseInt(upper.slice(0, 4), 16);
77
+ const entry = dcmjs_1.default.data.DicomMetaDictionary.dictionary[tag];
78
+ const isPrivate = groupNumber % 2 === 1;
79
+ const name = entry?.name ?? (isPrivate ? 'PrivateTag' : 'Unknown');
80
+ const vr = element.vr || entry?.vr || 'UN';
81
+ const values = element.Value ?? [];
82
+ const result = {
83
+ tag,
84
+ name,
85
+ vr,
86
+ value: '',
87
+ group: (0, dictionary_js_1.tagGroup_of)(groupNumber, entry?.name),
88
+ phi: dictionary_js_1.DICOM_PHI_TAGS.has(name),
89
+ };
90
+ if (vr === 'SQ') {
91
+ const items = values.map((item) => dict_flatten(item));
92
+ result.items = items;
93
+ result.value = `${items.length} item${items.length === 1 ? '' : 's'}`;
94
+ return result;
95
+ }
96
+ if (BULK_VRS.has(vr)) {
97
+ result.value = `<${bulk_bytes(values)} bytes>`;
98
+ return result;
99
+ }
100
+ result.value = values.map((value) => value_text(value, vr)).join('\\');
101
+ const decoded = decoded_of(name, result.value);
102
+ if (decoded !== undefined)
103
+ result.decoded = decoded;
104
+ return result;
105
+ }
106
+ /**
107
+ * Sums the bytes of a bulk value list.
108
+ *
109
+ * @param values - Buffers or typed arrays.
110
+ * @returns Total byte length.
111
+ */
112
+ function bulk_bytes(values) {
113
+ let total = 0;
114
+ for (const value of values) {
115
+ if (value instanceof ArrayBuffer)
116
+ total += value.byteLength;
117
+ else if (ArrayBuffer.isView(value))
118
+ total += value.byteLength;
119
+ }
120
+ return total;
121
+ }
122
+ /**
123
+ * One value in text. Person names are alphabetic components; everything
124
+ * else is its string form.
125
+ *
126
+ * @param value - The raw value.
127
+ * @param vr - The element's VR.
128
+ * @returns The text.
129
+ */
130
+ function value_text(value, vr) {
131
+ if (vr === 'PN' && value !== null && typeof value === 'object') {
132
+ const parts = value;
133
+ return String(parts.Alphabetic ?? parts.Ideographic ?? parts.Phonetic ?? '');
134
+ }
135
+ if (value === null || value === undefined)
136
+ return '';
137
+ return String(value);
138
+ }
139
+ /**
140
+ * Words for a coded value: the short dictionary list, plus SOP class UIDs
141
+ * named through dcmjs.
142
+ *
143
+ * @param name - The tag's name.
144
+ * @param value - The tag's text value.
145
+ * @returns Words, or nothing.
146
+ */
147
+ function decoded_of(name, value) {
148
+ if (name === 'SOPClassUID' || name === 'MediaStorageSOPClassUID') {
149
+ return dcmjs_1.default.data.DicomMetaDictionary.sopClassNamesByUID[value];
150
+ }
151
+ return (0, dictionary_js_1.tagValue_decode)(name, value);
152
+ }
153
+ /**
154
+ * Finds one tag by name in a tag set's top level.
155
+ *
156
+ * @param tags - The tags to search.
157
+ * @param name - The dictionary name.
158
+ * @returns The tag, or undefined.
159
+ */
160
+ function dicomTag_find(tags, name) {
161
+ return tags.find((tag) => tag.name === name);
162
+ }
163
+ /**
164
+ * Tells constant from varying across a folder's files, in folder order.
165
+ *
166
+ * A tag is constant when every file carries it with the same value
167
+ * (sequences compare by their items). Anything else, including a tag some
168
+ * files lack, is varying, with its first and last value and its distinct
169
+ * count. Files that failed to read are the caller's to report; they are not
170
+ * passed here.
171
+ *
172
+ * @param files - Each file's path and tag set, in folder order.
173
+ * @returns The summary.
174
+ */
175
+ function dicomTags_summarize(files) {
176
+ const order = [];
177
+ const byTag = new Map();
178
+ for (const file of files) {
179
+ const seen = new Map();
180
+ for (const tag of file.tags.tags) {
181
+ if (!byTag.has(tag.tag)) {
182
+ byTag.set(tag.tag, new Map());
183
+ order.push(tag.tag);
184
+ }
185
+ seen.set(tag.tag, tag);
186
+ }
187
+ for (const [key, tag] of seen)
188
+ byTag.get(key).set(file.path, tag);
189
+ }
190
+ const constant = [];
191
+ const varying = [];
192
+ for (const key of order.sort()) {
193
+ const perFile = byTag.get(key);
194
+ const values = files.map((file) => {
195
+ const tag = perFile.get(file.path);
196
+ return { path: file.path, value: tag === undefined ? null : tag_comparable(tag) };
197
+ });
198
+ const distinctValues = new Set(values.map((entry) => entry.value));
199
+ const sample = perFile.values().next().value;
200
+ if (distinctValues.size === 1 && !distinctValues.has(null)) {
201
+ constant.push(sample);
202
+ continue;
203
+ }
204
+ const present = values.filter((entry) => entry.value !== null);
205
+ varying.push({
206
+ tag: sample.tag,
207
+ name: sample.name,
208
+ vr: sample.vr,
209
+ group: sample.group,
210
+ phi: sample.phi,
211
+ distinct: distinctValues.size - (distinctValues.has(null) ? 1 : 0),
212
+ first: present[0].value,
213
+ last: present[present.length - 1].value,
214
+ values: values.map((entry) => ({
215
+ path: entry.path,
216
+ value: entry.value === null ? null : perFile.get(entry.path).value,
217
+ })),
218
+ });
219
+ }
220
+ return { constant, varying };
221
+ }
222
+ /**
223
+ * The value a tag is compared by across files: its text, or for a sequence
224
+ * the text of its items.
225
+ *
226
+ * @param tag - The tag.
227
+ * @returns A comparable string.
228
+ */
229
+ function tag_comparable(tag) {
230
+ if (tag.items === undefined)
231
+ return tag.value;
232
+ return JSON.stringify(tag.items.map((item) => item.map((inner) => `${inner.tag}=${tag_comparable(inner)}`)));
233
+ }
234
+ //# sourceMappingURL=tags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags.js","sourceRoot":"","sources":["../../src/dicom/tags.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export * from './context/index.js';
10
10
  export * from './store/index.js';
11
11
  export * from './pipelines/index.js';
12
12
  export * from './pacs/index.js';
13
+ export * from './dicom/index.js';
13
14
  export * from './retrieve/watch.js';
14
15
  export * from './vfs/index.js';
15
16
  export * from './tags/index.js';
package/dist/index.js CHANGED
@@ -27,6 +27,7 @@ __exportStar(require("./context/index.js"), exports);
27
27
  __exportStar(require("./store/index.js"), exports);
28
28
  __exportStar(require("./pipelines/index.js"), exports);
29
29
  __exportStar(require("./pacs/index.js"), exports);
30
+ __exportStar(require("./dicom/index.js"), exports);
30
31
  __exportStar(require("./retrieve/watch.js"), exports);
31
32
  __exportStar(require("./vfs/index.js"), exports);
32
33
  __exportStar(require("./tags/index.js"), exports);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAEH,mDAAiC;AACjC,qDAAmC;AACnC,mDAAiC;AACjC,qDAAmC;AACnC,qDAAmC;AACnC,mDAAiC;AACjC,uDAAqC;AACrC,kDAAgC;AAChC,sDAAoC;AACpC,iDAA+B;AAC/B,kDAAgC;AAChC,oDAAkC;AAClC,yDAAuC;AACvC,6DAA2C;AAC3C,uDAAqC;AACrC,qDAAmC;AACnC,kDAAgC;AAChC,mDAAiC;AACjC,mDAAygB;AAAhgB,4GAAA,iBAAiB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,sHAAA,2BAA2B,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,gHAAA,qBAAqB,OAAA;AAAE,qHAAA,0BAA0B,OAAA;AAAE,sHAAA,2BAA2B,OAAA;AAAE,gHAAA,qBAAqB,OAAA;AAA8B,0GAAA,eAAe,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,yGAAA,cAAc,OAAA;AAAE,+GAAA,oBAAoB,OAAA;AAAE,0GAAA,eAAe,OAAA;AAAE,+GAAA,oBAAoB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,+GAAA,oBAAoB,OAAA;AAAE,0GAAA,eAAe,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,iHAAA,sBAAsB,OAAA;AAEve,mDAAwE;AAA/D,+GAAA,eAAe,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAE3C,mDAAyE;AAAhE,gHAAA,gBAAgB,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAC5C,iDAAyD;AAAhD,mHAAA,oBAAoB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAEH,mDAAiC;AACjC,qDAAmC;AACnC,mDAAiC;AACjC,qDAAmC;AACnC,qDAAmC;AACnC,mDAAiC;AACjC,uDAAqC;AACrC,kDAAgC;AAChC,mDAAiC;AACjC,sDAAoC;AACpC,iDAA+B;AAC/B,kDAAgC;AAChC,oDAAkC;AAClC,yDAAuC;AACvC,6DAA2C;AAC3C,uDAAqC;AACrC,qDAAmC;AACnC,kDAAgC;AAChC,mDAAiC;AACjC,mDAAygB;AAAhgB,4GAAA,iBAAiB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,sHAAA,2BAA2B,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,gHAAA,qBAAqB,OAAA;AAAE,qHAAA,0BAA0B,OAAA;AAAE,sHAAA,2BAA2B,OAAA;AAAE,gHAAA,qBAAqB,OAAA;AAA8B,0GAAA,eAAe,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,yGAAA,cAAc,OAAA;AAAE,+GAAA,oBAAoB,OAAA;AAAE,0GAAA,eAAe,OAAA;AAAE,+GAAA,oBAAoB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,+GAAA,oBAAoB,OAAA;AAAE,0GAAA,eAAe,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAAE,iHAAA,sBAAsB,OAAA;AAEve,mDAAwE;AAA/D,+GAAA,eAAe,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAE3C,mDAAyE;AAAhE,gHAAA,gBAAgB,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAC5C,iDAAyD;AAAhD,mHAAA,oBAAoB,OAAA"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @file Standing in for what a PACS answer says about a person.
3
+ *
4
+ * A demo runs against a live hospital PACS, and a live PACS answers with
5
+ * the names, record numbers and dates of real patients. This substitutes a
6
+ * stable stand-in for each of those before an answer reaches a screen, so a
7
+ * projector, a screenshot or a recording carries none of them.
8
+ *
9
+ * What this is NOT: anonymisation in the regulatory sense. A record number
10
+ * is seven digits, an accession eight, and a hash of a space that small is
11
+ * enumerable in under a second — so the salt below is what stands between a
12
+ * token and the value behind it. The salt is made once per daemon and never
13
+ * written down, which makes tokens stable through one demo and meaningless
14
+ * after it. Two runs of the same demo do not agree, and that is the point:
15
+ * a pairing leaked from one recording says nothing about any other.
16
+ *
17
+ * The DICOM identifiers are deliberately left alone. They are not shown in
18
+ * a listing and they are what the kernel reconciles an answer against, so
19
+ * standing in for them would break the answer while hiding nothing.
20
+ *
21
+ * @module
22
+ */
23
+ /**
24
+ * The stand-in for one value: stable for this session, opaque outside it.
25
+ *
26
+ * @param value - The real value.
27
+ * @param chars - How many hex characters to keep.
28
+ * @returns The stand-in, or an empty string for an empty value.
29
+ */
30
+ export declare function token_of(value: string, chars?: number): string;
31
+ /**
32
+ * A stand-in name, in the form DICOM writes one.
33
+ *
34
+ * The same person reads the same way everywhere in one answer, which is
35
+ * what makes a demo followable; a different session gives a different
36
+ * person the same name and nobody can tell.
37
+ *
38
+ * @param value - The real name.
39
+ * @returns A name of the same shape, carrying nothing of the original.
40
+ */
41
+ export declare function nameStandIn_of(value: string): string;
42
+ /**
43
+ * A study date, coarsened to the month it fell in.
44
+ *
45
+ * @param value - A DICOM date, `YYYYMMDD`.
46
+ * @returns The first of that month, or the value unchanged when it is not a date.
47
+ */
48
+ export declare function date_toMonth(value: string): string;
49
+ /**
50
+ * A birth date, coarsened to its year.
51
+ *
52
+ * Coarser than a study date on purpose: an age, with a modality and a small
53
+ * institution, identifies a person where a month of imaging does not.
54
+ *
55
+ * @param value - A DICOM date, `YYYYMMDD`.
56
+ * @returns The first of that year, or the value unchanged when it is not a date.
57
+ */
58
+ export declare function birthDate_toYear(value: string): string;
59
+ /** The fields this stands in for, for a caller that needs to know. */
60
+ export declare const STOOD_IN_FIELDS: ReadonlyArray<string>;
61
+ /**
62
+ * Stands in for one field's value.
63
+ *
64
+ * @param field - The DICOM keyword.
65
+ * @param value - What the PACS said.
66
+ * @returns The stand-in, or the value unchanged when the field identifies nobody.
67
+ */
68
+ export declare function field_standIn(field: string, value: string): string;
69
+ /**
70
+ * Walks a decoded PACS answer, standing in for every identifying value.
71
+ *
72
+ * Done on the DECODED payload rather than on the model or the rendering,
73
+ * because both are built from it: substituting here means a listing, a
74
+ * table, a CSV and a console line all say the same thing, and none of them
75
+ * has to know this happened.
76
+ *
77
+ * @param payload - The decoded answer, as DICOM-shaped objects.
78
+ * @returns A copy carrying stand-ins.
79
+ */
80
+ export declare function answer_standIn(payload: unknown): unknown;
81
+ /**
82
+ * Stands in for the values in a query's own terms.
83
+ *
84
+ * The line an operator typed is the first thing on a screen and it carries
85
+ * the record number they typed. This rewrites the terms so the echoed line
86
+ * names the same token the rows do — which also ties the line to the answer
87
+ * below it, rather than hiding it behind stars that a PACS would read as a
88
+ * wildcard.
89
+ *
90
+ * @param criteria - The question, as field to value.
91
+ * @returns The question with identifying values stood in for.
92
+ */
93
+ export declare function criteria_standIn(criteria: Record<string, string>): Record<string, string>;
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ /**
3
+ * @file Standing in for what a PACS answer says about a person.
4
+ *
5
+ * A demo runs against a live hospital PACS, and a live PACS answers with
6
+ * the names, record numbers and dates of real patients. This substitutes a
7
+ * stable stand-in for each of those before an answer reaches a screen, so a
8
+ * projector, a screenshot or a recording carries none of them.
9
+ *
10
+ * What this is NOT: anonymisation in the regulatory sense. A record number
11
+ * is seven digits, an accession eight, and a hash of a space that small is
12
+ * enumerable in under a second — so the salt below is what stands between a
13
+ * token and the value behind it. The salt is made once per daemon and never
14
+ * written down, which makes tokens stable through one demo and meaningless
15
+ * after it. Two runs of the same demo do not agree, and that is the point:
16
+ * a pairing leaked from one recording says nothing about any other.
17
+ *
18
+ * The DICOM identifiers are deliberately left alone. They are not shown in
19
+ * a listing and they are what the kernel reconciles an answer against, so
20
+ * standing in for them would break the answer while hiding nothing.
21
+ *
22
+ * @module
23
+ */
24
+ var __importDefault = (this && this.__importDefault) || function (mod) {
25
+ return (mod && mod.__esModule) ? mod : { "default": mod };
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.STOOD_IN_FIELDS = void 0;
29
+ exports.token_of = token_of;
30
+ exports.nameStandIn_of = nameStandIn_of;
31
+ exports.date_toMonth = date_toMonth;
32
+ exports.birthDate_toYear = birthDate_toYear;
33
+ exports.field_standIn = field_standIn;
34
+ exports.answer_standIn = answer_standIn;
35
+ exports.criteria_standIn = criteria_standIn;
36
+ const crypto_1 = __importDefault(require("crypto"));
37
+ /**
38
+ * The secret behind every token, made once per process.
39
+ *
40
+ * A daemon is a session, so this is a session's salt: stable while the
41
+ * demo runs, gone when it ends.
42
+ */
43
+ const SALT = crypto_1.default.randomBytes(32);
44
+ /** How many hex characters a stand-in identifier carries. */
45
+ const TOKEN_CHARS = 8;
46
+ /** Family names for the stand-in, chosen to read as names and nothing more. */
47
+ const SURNAMES = [
48
+ 'ASHDOWN', 'BELLWEATHER', 'CARRICK', 'DUNMORE', 'EASTWICK', 'FAIRHOLM',
49
+ 'GRANTLEY', 'HARBOURNE', 'IRVINGTON', 'JARRETT', 'KESTREL', 'LOWNDES',
50
+ 'MARCHETTI', 'NORWOOD', 'OAKLEY', 'PENHALE', 'QUILLON', 'RAVENSCAR',
51
+ 'STARLING', 'THORNBURY', 'UNDERHILL', 'VANTAGE', 'WESTMARCH', 'YARROW',
52
+ ];
53
+ /** Given names for the stand-in. */
54
+ const FORENAMES = [
55
+ 'ALDEN', 'BRIONY', 'CASPAR', 'DELPHINE', 'EAMON', 'FENELLA', 'GARETH',
56
+ 'HESTER', 'ISOLDE', 'JASPER', 'KERENSA', 'LOWELL', 'MERIEL', 'NEVIN',
57
+ 'ORLA', 'PERRIN', 'QUENBY', 'ROSALIND', 'SEVERIN', 'TAMSIN', 'ULRIC',
58
+ 'VERITY', 'WYNTER', 'XANTHE',
59
+ ];
60
+ /**
61
+ * The stand-in for one value: stable for this session, opaque outside it.
62
+ *
63
+ * @param value - The real value.
64
+ * @param chars - How many hex characters to keep.
65
+ * @returns The stand-in, or an empty string for an empty value.
66
+ */
67
+ function token_of(value, chars = TOKEN_CHARS) {
68
+ if (value === '')
69
+ return '';
70
+ return crypto_1.default.createHash('sha256').update(SALT).update(value).digest('hex').slice(0, chars);
71
+ }
72
+ /**
73
+ * A stand-in name, in the form DICOM writes one.
74
+ *
75
+ * The same person reads the same way everywhere in one answer, which is
76
+ * what makes a demo followable; a different session gives a different
77
+ * person the same name and nobody can tell.
78
+ *
79
+ * @param value - The real name.
80
+ * @returns A name of the same shape, carrying nothing of the original.
81
+ */
82
+ function nameStandIn_of(value) {
83
+ if (value === '')
84
+ return '';
85
+ const digest = crypto_1.default.createHash('sha256').update(SALT).update(value).digest();
86
+ const surname = SURNAMES[digest[0] % SURNAMES.length];
87
+ const forename = FORENAMES[digest[1] % FORENAMES.length];
88
+ return `${surname}^${forename}`;
89
+ }
90
+ /**
91
+ * A study date, coarsened to the month it fell in.
92
+ *
93
+ * @param value - A DICOM date, `YYYYMMDD`.
94
+ * @returns The first of that month, or the value unchanged when it is not a date.
95
+ */
96
+ function date_toMonth(value) {
97
+ return /^\d{8}$/.test(value) ? `${value.slice(0, 6)}01` : value;
98
+ }
99
+ /**
100
+ * A birth date, coarsened to its year.
101
+ *
102
+ * Coarser than a study date on purpose: an age, with a modality and a small
103
+ * institution, identifies a person where a month of imaging does not.
104
+ *
105
+ * @param value - A DICOM date, `YYYYMMDD`.
106
+ * @returns The first of that year, or the value unchanged when it is not a date.
107
+ */
108
+ function birthDate_toYear(value) {
109
+ return /^\d{8}$/.test(value) ? `${value.slice(0, 4)}0101` : value;
110
+ }
111
+ /** How each identifying field is stood in for. */
112
+ const STAND_INS = {
113
+ PatientID: (value) => token_of(value),
114
+ PatientName: nameStandIn_of,
115
+ OtherPatientIDs: (value) => token_of(value),
116
+ OtherPatientNames: nameStandIn_of,
117
+ PatientMotherBirthName: nameStandIn_of,
118
+ AccessionNumber: (value) => token_of(value),
119
+ PatientBirthDate: birthDate_toYear,
120
+ PatientBirthTime: () => '',
121
+ PatientAge: () => '',
122
+ PatientAddress: () => '',
123
+ PatientTelephoneNumbers: () => '',
124
+ StudyDate: date_toMonth,
125
+ SeriesDate: date_toMonth,
126
+ ReferringPhysicianName: nameStandIn_of,
127
+ PerformingPhysicianName: nameStandIn_of,
128
+ OperatorsName: nameStandIn_of,
129
+ PhysiciansOfRecord: nameStandIn_of,
130
+ NameOfPhysiciansReadingStudy: nameStandIn_of,
131
+ InstitutionName: (value) => (value === '' ? '' : `SITE ${token_of(value, 4)}`),
132
+ InstitutionAddress: () => '',
133
+ };
134
+ /** The fields this stands in for, for a caller that needs to know. */
135
+ exports.STOOD_IN_FIELDS = Object.keys(STAND_INS);
136
+ /**
137
+ * Stands in for one field's value.
138
+ *
139
+ * @param field - The DICOM keyword.
140
+ * @param value - What the PACS said.
141
+ * @returns The stand-in, or the value unchanged when the field identifies nobody.
142
+ */
143
+ function field_standIn(field, value) {
144
+ const rule = STAND_INS[field];
145
+ return rule === undefined ? value : rule(value);
146
+ }
147
+ /**
148
+ * Walks a decoded PACS answer, standing in for every identifying value.
149
+ *
150
+ * Done on the DECODED payload rather than on the model or the rendering,
151
+ * because both are built from it: substituting here means a listing, a
152
+ * table, a CSV and a console line all say the same thing, and none of them
153
+ * has to know this happened.
154
+ *
155
+ * @param payload - The decoded answer, as DICOM-shaped objects.
156
+ * @returns A copy carrying stand-ins.
157
+ */
158
+ function answer_standIn(payload) {
159
+ if (Array.isArray(payload))
160
+ return payload.map((one) => answer_standIn(one));
161
+ if (payload === null || typeof payload !== 'object')
162
+ return payload;
163
+ const out = {};
164
+ for (const [field, held] of Object.entries(payload)) {
165
+ if (held !== null && typeof held === 'object' && 'value' in held) {
166
+ const inner = held;
167
+ const value = inner.value;
168
+ out[field] = typeof value === 'string'
169
+ ? { ...inner, value: field_standIn(field, value) }
170
+ : { ...inner, value: answer_standIn(value) };
171
+ continue;
172
+ }
173
+ if (typeof held === 'string') {
174
+ out[field] = field_standIn(field, held);
175
+ continue;
176
+ }
177
+ out[field] = answer_standIn(held);
178
+ }
179
+ return out;
180
+ }
181
+ /**
182
+ * Stands in for the values in a query's own terms.
183
+ *
184
+ * The line an operator typed is the first thing on a screen and it carries
185
+ * the record number they typed. This rewrites the terms so the echoed line
186
+ * names the same token the rows do — which also ties the line to the answer
187
+ * below it, rather than hiding it behind stars that a PACS would read as a
188
+ * wildcard.
189
+ *
190
+ * @param criteria - The question, as field to value.
191
+ * @returns The question with identifying values stood in for.
192
+ */
193
+ function criteria_standIn(criteria) {
194
+ return Object.fromEntries(Object.entries(criteria).map(([field, value]) => [field, field_standIn(field, value)]));
195
+ }
196
+ //# sourceMappingURL=anonymise.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anonymise.js","sourceRoot":"","sources":["../../src/pacs/anonymise.ts"],"names":[],"mappings":"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"}
@@ -62,3 +62,4 @@ export declare function pacsRetrieve_delete(retrieveId: number): Promise<Result<
62
62
  * @returns Result containing PACSQueryStatusReport with series-level status.
63
63
  */
64
64
  export declare function pacsRetrieve_statusForQuery(queryId: number): Promise<Result<PACSQueryStatusReport>>;
65
+ export * from './anonymise.js';