@cosyte/synth 0.0.7 → 0.0.8
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/CHANGELOG.md +135 -17
- package/README.md +47 -45
- package/dist/astm/index.cjs +7 -7
- package/dist/astm/index.cjs.map +1 -1
- package/dist/astm/index.d.cts +47 -47
- package/dist/astm/index.d.ts +47 -47
- package/dist/astm/index.mjs +7 -7
- package/dist/astm/index.mjs.map +1 -1
- package/dist/ccda/index.cjs +15 -15
- package/dist/ccda/index.cjs.map +1 -1
- package/dist/ccda/index.d.cts +31 -31
- package/dist/ccda/index.d.ts +31 -31
- package/dist/ccda/index.mjs +15 -15
- package/dist/ccda/index.mjs.map +1 -1
- package/dist/deid/index.cjs +15 -15
- package/dist/deid/index.cjs.map +1 -1
- package/dist/deid/index.d.cts +37 -37
- package/dist/deid/index.d.ts +37 -37
- package/dist/deid/index.mjs +15 -15
- package/dist/deid/index.mjs.map +1 -1
- package/dist/{example-codes-DeXcnCSK.d.cts → example-codes-88f3_bQZ.d.cts} +8 -8
- package/dist/{example-codes-DeXcnCSK.d.ts → example-codes-88f3_bQZ.d.ts} +8 -8
- package/dist/fhir/index.cjs +12 -12
- package/dist/fhir/index.cjs.map +1 -1
- package/dist/fhir/index.d.cts +46 -46
- package/dist/fhir/index.d.ts +46 -46
- package/dist/fhir/index.mjs +12 -12
- package/dist/fhir/index.mjs.map +1 -1
- package/dist/hl7/index.cjs +6 -6
- package/dist/hl7/index.cjs.map +1 -1
- package/dist/hl7/index.d.cts +45 -45
- package/dist/hl7/index.d.ts +45 -45
- package/dist/hl7/index.mjs +6 -6
- package/dist/hl7/index.mjs.map +1 -1
- package/dist/index.cjs +9 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -49
- package/dist/index.d.ts +49 -49
- package/dist/index.mjs +9 -9
- package/dist/index.mjs.map +1 -1
- package/dist/ncpdp/index.cjs +3 -3
- package/dist/ncpdp/index.cjs.map +1 -1
- package/dist/ncpdp/index.d.cts +42 -42
- package/dist/ncpdp/index.d.ts +42 -42
- package/dist/ncpdp/index.mjs +3 -3
- package/dist/ncpdp/index.mjs.map +1 -1
- package/dist/{providers-OLz3zAc-.d.cts → providers-B9uVinAe.d.cts} +24 -24
- package/dist/{providers-OLz3zAc-.d.ts → providers-B9uVinAe.d.ts} +24 -24
- package/dist/{quirk-C9t9CkPS.d.ts → quirk-HZdznAkM.d.ts} +23 -23
- package/dist/{quirk-DYMDojVw.d.cts → quirk-IaHp4z7N.d.cts} +23 -23
- package/dist/x12/index.cjs +6 -6
- package/dist/x12/index.cjs.map +1 -1
- package/dist/x12/index.d.cts +44 -44
- package/dist/x12/index.d.ts +44 -44
- package/dist/x12/index.mjs +6 -6
- package/dist/x12/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/deid/index.d.cts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Hl7MessageKind } from '../hl7/index.cjs';
|
|
2
2
|
import { DeidPolicy, GenericLocus } from '@cosyte/deid';
|
|
3
3
|
import '@cosyte/hl7';
|
|
4
|
-
import '../providers-
|
|
5
|
-
import '../quirk-
|
|
4
|
+
import '../providers-B9uVinAe.cjs';
|
|
5
|
+
import '../quirk-IaHp4z7N.cjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* The format-agnostic core of the **`@cosyte/deid` pairing loop
|
|
8
|
+
* The format-agnostic core of the **`@cosyte/deid` pairing loop**: a **closed-loop
|
|
9
9
|
* co-validation harness** for the `synth` ⇄ `deid` pair.
|
|
10
10
|
*
|
|
11
11
|
* The loop's shape is: **generate a spec-clean synthetic artifact → enumerate the distinctive synthetic
|
|
12
12
|
* PHI sentinels `@cosyte/synth` planted at its patient loci → run it through `@cosyte/deid` → verify
|
|
13
13
|
* every planted sentinel is gone from the de-identified output** (a surviving sentinel is a hard
|
|
14
14
|
* failure), **and that the non-PHI clinical content survived** (the over-scrub guard). It is
|
|
15
|
-
* deterministic and seeded, and it does **not** change what the generators emit
|
|
15
|
+
* deterministic and seeded, and it does **not** change what the generators emit: it consumes their
|
|
16
16
|
* output.
|
|
17
17
|
*
|
|
18
18
|
* **Honesty line (governs the whole module).** This is a **co-validation harness**, not an independent
|
|
@@ -20,7 +20,7 @@ import '../quirk-DYMDojVw.cjs';
|
|
|
20
20
|
* that the synthetic PHI `synth` plants at the patient loci is removed by `deid`, and that `deid` does
|
|
21
21
|
* not over-scrub `synth`'s clinical payload. A sentinel that `deid` **blocks** rather than redacts still
|
|
22
22
|
* passes the removal check (blocked = gone from the wire). Formats where `@cosyte/deid` ships no adapter
|
|
23
|
-
* (NCPDP **SCRIPT**, **ASTM**)
|
|
23
|
+
* (NCPDP **SCRIPT**, **ASTM**), and DICOM, which `synth` does not generate, are **skipped and named**
|
|
24
24
|
* (see `DEID_LOOP_SKIPPED` in `./index`), never silently.
|
|
25
25
|
*
|
|
26
26
|
* This module holds only the pure, parser-free pieces (the policy, the sentinel model, the sweep, the
|
|
@@ -31,16 +31,16 @@ import '../quirk-DYMDojVw.cjs';
|
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* The set of cosyte formats the loop covers
|
|
34
|
+
* The set of cosyte formats the loop covers: every format for which **both** `@cosyte/synth` generates
|
|
35
35
|
* and `@cosyte/deid` ships an adapter.
|
|
36
36
|
*/
|
|
37
37
|
type DeidLoopFormat = "hl7" | "fhir" | "ccda" | "x12" | "ncpdp-telecom";
|
|
38
38
|
/**
|
|
39
39
|
* A single **planted sentinel**: one distinctive, synthetic-by-construction PHI token that
|
|
40
40
|
* `@cosyte/synth` placed at a patient locus of a generated artifact, tracked so the loop can prove
|
|
41
|
-
* `@cosyte/deid` removed it. The token is always drawn from a synthetic-safety provider
|
|
41
|
+
* `@cosyte/deid` removed it. The token is always drawn from a synthetic-safety provider:
|
|
42
42
|
* a fake-name-pool name, a `900`-range SSN, a `555-01xx` phone, a synthetic-assigning-authority
|
|
43
|
-
* identifier
|
|
43
|
+
* identifier, so a sentinel can never be, or collide with, a real person's PHI.
|
|
44
44
|
*
|
|
45
45
|
* @example
|
|
46
46
|
* ```ts
|
|
@@ -48,9 +48,9 @@ type DeidLoopFormat = "hl7" | "fhir" | "ccda" | "x12" | "ncpdp-telecom";
|
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
50
|
interface DeidSentinel {
|
|
51
|
-
/** The exact synthetic token planted at the locus
|
|
51
|
+
/** The exact synthetic token planted at the locus, a literal substring of the spec-clean artifact. */
|
|
52
52
|
readonly token: string;
|
|
53
|
-
/** Where it lives
|
|
53
|
+
/** Where it lives, a format-neutral path (e.g. `"PID-5"`, `"recordTarget"`, `"NM1[0]-3"`). */
|
|
54
54
|
readonly locus: string;
|
|
55
55
|
/** The Safe Harbor category `@cosyte/deid` assigned to the locus, when known. */
|
|
56
56
|
readonly category?: string;
|
|
@@ -74,15 +74,15 @@ interface DeidLoopResult {
|
|
|
74
74
|
readonly format: DeidLoopFormat;
|
|
75
75
|
/** The concrete artifact label (e.g. `"ORU^R01"`, `"837P"`, `"CCD"`). */
|
|
76
76
|
readonly artifact: string;
|
|
77
|
-
/** The seed
|
|
77
|
+
/** The seed, the same seed yields the same artifact and the same verdict. */
|
|
78
78
|
readonly seed: number;
|
|
79
79
|
/** Every distinctive synthetic PHI sentinel `synth` planted at a patient locus of the artifact. */
|
|
80
80
|
readonly planted: readonly DeidSentinel[];
|
|
81
|
-
/** Planted sentinels still present in the de-identified output
|
|
81
|
+
/** Planted sentinels still present in the de-identified output, **must be empty**. */
|
|
82
82
|
readonly survivors: readonly DeidSentinel[];
|
|
83
83
|
/** The non-PHI clinical code tokens probed for over-scrub (present in the spec-clean artifact). */
|
|
84
84
|
readonly clinicalProbed: readonly string[];
|
|
85
|
-
/** Probed clinical tokens missing after de-identification (over-scrub)
|
|
85
|
+
/** Probed clinical tokens missing after de-identification (over-scrub), **must be empty**. */
|
|
86
86
|
readonly clinicalScrubbed: readonly string[];
|
|
87
87
|
/** The spec-clean serialized artifact (before de-identification). */
|
|
88
88
|
readonly original: string;
|
|
@@ -108,7 +108,7 @@ declare const DEID_LOOP_POLICY_NAME = "synth-deid-loop-removal";
|
|
|
108
108
|
*
|
|
109
109
|
* Two reasons this is the right policy for a co-validation loop: (1) it needs **no key context**, so the
|
|
110
110
|
* loop is a pure function of the seed (the default Safe Harbor policy pseudonymizes those three
|
|
111
|
-
* categories, which requires the consumer's HMAC key); and (2) it makes the removal contract crisp
|
|
111
|
+
* categories, which requires the consumer's HMAC key); and (2) it makes the removal contract crisp,
|
|
112
112
|
* every PHI locus is **removed or generalized away**, never replaced by a surrogate that a naive sweep
|
|
113
113
|
* might mistake for a survivor. `pseudonymize` also removes the original value, so this is a stricter,
|
|
114
114
|
* not weaker, check.
|
|
@@ -127,7 +127,7 @@ declare function deidLoopPolicy(): DeidPolicy;
|
|
|
127
127
|
* identifier loci, decomposed to **literal** tokens of the spec-clean artifact.
|
|
128
128
|
*
|
|
129
129
|
* The loci come from `@cosyte/deid`'s own per-format extractor (`extractHl7Loci` / `extractFhirLoci` /
|
|
130
|
-
* `extractX12Loci` / `extractTelecomLoci`), which locates PHI **structurally
|
|
130
|
+
* `extractX12Loci` / `extractTelecomLoci`), which locates PHI **structurally**, so provider and
|
|
131
131
|
* organization names, which a de-identifier legitimately retains, are never extracted and never become
|
|
132
132
|
* false sentinels. Only `identifier`-kind loci are used (names, SSNs, phones, emails, member/account
|
|
133
133
|
* ids); dates (generalized to a year, not removed) and ZIPs (generalized to three digits) are handled by
|
|
@@ -138,7 +138,7 @@ declare function deidLoopPolicy(): DeidPolicy;
|
|
|
138
138
|
*
|
|
139
139
|
* @param loci - The located candidate values from a `@cosyte/deid` extractor.
|
|
140
140
|
* @param original - The spec-clean serialized artifact the loci were extracted from.
|
|
141
|
-
* @returns The distinctive, literal, synthetic PHI sentinels
|
|
141
|
+
* @returns The distinctive, literal, synthetic PHI sentinels, de-duplicated by token.
|
|
142
142
|
* @example
|
|
143
143
|
* ```ts
|
|
144
144
|
* import { extractHl7Loci } from "@cosyte/deid/hl7";
|
|
@@ -148,7 +148,7 @@ declare function deidLoopPolicy(): DeidPolicy;
|
|
|
148
148
|
declare function identifierSentinels(loci: readonly GenericLocus[], original: string): readonly DeidSentinel[];
|
|
149
149
|
/**
|
|
150
150
|
* Enumerate the patient sentinels of a **C-CDA** document by reading the `<recordTarget>` element of the
|
|
151
|
-
* serialized XML
|
|
151
|
+
* serialized XML: the patient participation. Scoping to `recordTarget` (rather than `author` /
|
|
152
152
|
* `custodian`, which carry provider identity a de-identifier retains) is what keeps the sentinels
|
|
153
153
|
* patient-PHI only. Used for C-CDA because `@cosyte/deid`'s C-CDA extractor operates on a raw DOM, which
|
|
154
154
|
* this zero-dependency module does not construct.
|
|
@@ -163,7 +163,7 @@ declare function identifierSentinels(loci: readonly GenericLocus[], original: st
|
|
|
163
163
|
*/
|
|
164
164
|
declare function recordTargetSentinels(xml: string): readonly DeidSentinel[];
|
|
165
165
|
/**
|
|
166
|
-
* Sweep the **PHI-locus residue** of a de-identified artifact for **surviving** planted sentinels
|
|
166
|
+
* Sweep the **PHI-locus residue** of a de-identified artifact for **surviving** planted sentinels: the
|
|
167
167
|
* heart of the removal check. A sentinel survives if its exact token still appears in the values that
|
|
168
168
|
* remain at the (former) PHI loci after de-identification; a non-empty result is a **hard failure** (real
|
|
169
169
|
* PHI-shaped data leaked through de-identification).
|
|
@@ -172,15 +172,15 @@ declare function recordTargetSentinels(xml: string): readonly DeidSentinel[];
|
|
|
172
172
|
* provider/organization names, addresses, and phones from the same synthetic pools, and a de-identifier
|
|
173
173
|
* legitimately **retains** provider identity. So the same synthetic token can sit at both a patient locus
|
|
174
174
|
* (removed) and a provider locus (retained); a whole-document sweep would read the retained provider copy
|
|
175
|
-
* as a false survivor. Sweeping only the residue at the loci that were PHI
|
|
176
|
-
* de-identifier's own output
|
|
175
|
+
* as a false survivor. Sweeping only the residue at the loci that were PHI, re-read from the
|
|
176
|
+
* de-identifier's own output, is locus-scoped and collision-proof: a token still present *there* is a
|
|
177
177
|
* genuine leak. It remains independent of the de-identifier's manifest: it reads the actual serialized
|
|
178
178
|
* output, so a de-identifier that locates a locus but fails to strip it is still caught.
|
|
179
179
|
*
|
|
180
180
|
* @param phiResidue - The de-identified values that remain at the former PHI loci (for the model formats,
|
|
181
181
|
* the re-extracted identifier-locus values joined; for C-CDA, the de-identified `<recordTarget>` block).
|
|
182
182
|
* @param sentinels - The sentinels planted in the corresponding spec-clean artifact.
|
|
183
|
-
* @returns The subset of `sentinels` still present in `phiResidue
|
|
183
|
+
* @returns The subset of `sentinels` still present in `phiResidue`, empty on a clean pass.
|
|
184
184
|
* @example
|
|
185
185
|
* ```ts
|
|
186
186
|
* sweepSurvivors(deidPhiResidue, planted); // => [] (all removed)
|
|
@@ -195,8 +195,8 @@ declare function sweepSurvivors(phiResidue: string, sentinels: readonly DeidSent
|
|
|
195
195
|
* is not a structured-content loss).
|
|
196
196
|
*
|
|
197
197
|
* Only **distinctive** codes (four or more characters) are probed: a short 2–3 digit code (e.g. a CVX
|
|
198
|
-
* vaccine code) can appear by coincidence *inside* a PHI value a de-identifier removes
|
|
199
|
-
* SSN
|
|
198
|
+
* vaccine code) can appear by coincidence *inside* a PHI value a de-identifier removes: a timestamp, an
|
|
199
|
+
* SSN, and would read as a false over-scrub. Distinctive codes (LOINC `4548-4`, SNOMED `44054006`,
|
|
200
200
|
* 11-digit NDCs, CPT `99213`) do not collide this way. An artifact whose clinical codes are all short
|
|
201
201
|
* simply yields an empty probe (the guard is skipped, never falsely tripped).
|
|
202
202
|
*
|
|
@@ -204,7 +204,7 @@ declare function sweepSurvivors(phiResidue: string, sentinels: readonly DeidSent
|
|
|
204
204
|
* @param deidentified - The serialized de-identified artifact.
|
|
205
205
|
* @param clinicalCodes - Candidate structured clinical code tokens (e.g. LOINC/ICD/NDC from the example
|
|
206
206
|
* pools) to probe.
|
|
207
|
-
* @returns `{ probed, scrubbed }
|
|
207
|
+
* @returns `{ probed, scrubbed }`: the distinctive codes present before, and the subset absent after.
|
|
208
208
|
* @example
|
|
209
209
|
* ```ts
|
|
210
210
|
* const { probed, scrubbed } = clinicalRetention(before, after, ["4548-4", "44054006"]);
|
|
@@ -217,7 +217,7 @@ declare function clinicalRetention(original: string, deidentified: string, clini
|
|
|
217
217
|
};
|
|
218
218
|
/**
|
|
219
219
|
* Assemble a {@link DeidLoopResult} from the planted sentinels, the de-identified output, and the
|
|
220
|
-
* clinical probe
|
|
220
|
+
* clinical probe: computing the `pass` verdict (sentinels planted, none survived, nothing over-scrubbed).
|
|
221
221
|
*
|
|
222
222
|
* @param parts - The pieces gathered by a per-format loop.
|
|
223
223
|
* @returns The immutable verdict.
|
|
@@ -237,7 +237,7 @@ declare function assembleVerdict(parts: {
|
|
|
237
237
|
readonly original: string;
|
|
238
238
|
readonly deidentified: string;
|
|
239
239
|
/**
|
|
240
|
-
* The de-identified values that remain at the former PHI loci
|
|
240
|
+
* The de-identified values that remain at the former PHI loci, what the survivor sweep reads (see
|
|
241
241
|
* {@link sweepSurvivors}). Defaults to `deidentified` when omitted (a whole-document sweep), but the
|
|
242
242
|
* per-format loops supply the locus-scoped residue so provider/organization identity a de-identifier
|
|
243
243
|
* legitimately retains never reads as a false survivor.
|
|
@@ -247,7 +247,7 @@ declare function assembleVerdict(parts: {
|
|
|
247
247
|
}): DeidLoopResult;
|
|
248
248
|
|
|
249
249
|
/**
|
|
250
|
-
* `@cosyte/synth/deid
|
|
250
|
+
* `@cosyte/synth/deid`, the **`@cosyte/deid` pairing loop**: a deterministic,
|
|
251
251
|
* seeded **co-validation harness** for the `synth` ⇄ `deid` pair.
|
|
252
252
|
*
|
|
253
253
|
* For each covered format it **generates** a spec-clean synthetic artifact through `@cosyte/synth`'s own
|
|
@@ -256,7 +256,7 @@ declare function assembleVerdict(parts: {
|
|
|
256
256
|
* the de-identified output while the clinical payload survives. A surviving sentinel is a hard failure;
|
|
257
257
|
* an over-scrubbed clinical value is a hard failure.
|
|
258
258
|
*
|
|
259
|
-
* **Scope & honesty.** This proves *the pair works on `synth`'s own output
|
|
259
|
+
* **Scope & honesty.** This proves *the pair works on `synth`'s own output*: it
|
|
260
260
|
* is **not** an independent audit of `@cosyte/deid` against real-world documents. The sentinels are
|
|
261
261
|
* synthetic-by-construction (never realistic); a sentinel `deid` **blocks** rather than redacts still
|
|
262
262
|
* passes (blocked = gone). The loop covers the five formats both packages support
|
|
@@ -268,7 +268,7 @@ declare function assembleVerdict(parts: {
|
|
|
268
268
|
*/
|
|
269
269
|
|
|
270
270
|
/**
|
|
271
|
-
* The cosyte formats the pairing loop covers
|
|
271
|
+
* The cosyte formats the pairing loop covers: every format for which **both** `@cosyte/synth`
|
|
272
272
|
* generates and `@cosyte/deid` ships an adapter.
|
|
273
273
|
*
|
|
274
274
|
* @example
|
|
@@ -279,7 +279,7 @@ declare function assembleVerdict(parts: {
|
|
|
279
279
|
*/
|
|
280
280
|
declare const DEID_LOOP_COVERED_FORMATS: readonly ["hl7", "fhir", "ccda", "x12", "ncpdp-telecom"];
|
|
281
281
|
/**
|
|
282
|
-
* The format paths the loop **deliberately skips**, each with the honest reason
|
|
282
|
+
* The format paths the loop **deliberately skips**, each with the honest reason, so a coverage gap is
|
|
283
283
|
* named, never silent.
|
|
284
284
|
*
|
|
285
285
|
* @example
|
|
@@ -300,7 +300,7 @@ declare const DEID_LOOP_SKIPPED: readonly [Readonly<{
|
|
|
300
300
|
}>];
|
|
301
301
|
/** Options accepted by {@link hl7DeidLoop}. */
|
|
302
302
|
interface Hl7DeidLoopOptions {
|
|
303
|
-
/** The seed
|
|
303
|
+
/** The seed: the same seed yields the same artifact and the same verdict. Defaults to `0`. */
|
|
304
304
|
readonly seed?: number;
|
|
305
305
|
/** The HL7 v2 message family to generate. Defaults to `"ORU^R01"` (a PHI- and clinically-dense report). */
|
|
306
306
|
readonly kind?: Hl7MessageKind;
|
|
@@ -375,7 +375,7 @@ interface NcpdpTelecomDeidLoopOptions {
|
|
|
375
375
|
/**
|
|
376
376
|
* Run the pairing loop for **NCPDP Telecom** (pharmacy claims): generate a spec-clean transaction
|
|
377
377
|
* through `@cosyte/ncpdp`, plant PHI sentinels at the patient/cardholder segments, de-identify through
|
|
378
|
-
* `@cosyte/deid/ncpdp`, and verify removal + clinical retention. **NCPDP SCRIPT is out of scope
|
|
378
|
+
* `@cosyte/deid/ncpdp`, and verify removal + clinical retention. **NCPDP SCRIPT is out of scope**:
|
|
379
379
|
* `@cosyte/deid` ships no SCRIPT adapter ({@link DEID_LOOP_SKIPPED}).
|
|
380
380
|
*
|
|
381
381
|
* @param options - The seed and transaction. See {@link NcpdpTelecomDeidLoopOptions}.
|
|
@@ -422,11 +422,11 @@ interface DeidCoverageRow {
|
|
|
422
422
|
readonly artifacts: readonly string[];
|
|
423
423
|
/** Total distinctive PHI sentinels planted across the runs. */
|
|
424
424
|
readonly planted: number;
|
|
425
|
-
/** Total surviving sentinels
|
|
425
|
+
/** Total surviving sentinels, **0** on a clean pass. */
|
|
426
426
|
readonly survivors: number;
|
|
427
427
|
/** Total clinical tokens probed for over-scrub. */
|
|
428
428
|
readonly clinicalProbed: number;
|
|
429
|
-
/** Total clinical tokens over-scrubbed
|
|
429
|
+
/** Total clinical tokens over-scrubbed, **0** on a clean pass. */
|
|
430
430
|
readonly clinicalScrubbed: number;
|
|
431
431
|
/** `true` iff every run for this format passed. */
|
|
432
432
|
readonly pass: boolean;
|
|
@@ -437,9 +437,9 @@ interface DeidCoverageSummary {
|
|
|
437
437
|
readonly byFormat: readonly DeidCoverageRow[];
|
|
438
438
|
/** Total sentinels planted across every run. */
|
|
439
439
|
readonly totalPlanted: number;
|
|
440
|
-
/** Total surviving sentinels across every run
|
|
440
|
+
/** Total surviving sentinels across every run, **0** on a clean pass. */
|
|
441
441
|
readonly totalSurvivors: number;
|
|
442
|
-
/** Total clinical over-scrubs across every run
|
|
442
|
+
/** Total clinical over-scrubs across every run, **0** on a clean pass. */
|
|
443
443
|
readonly totalClinicalScrubbed: number;
|
|
444
444
|
/** `true` iff every run passed. */
|
|
445
445
|
readonly allPass: boolean;
|
|
@@ -447,7 +447,7 @@ interface DeidCoverageSummary {
|
|
|
447
447
|
readonly skipped: typeof DEID_LOOP_SKIPPED;
|
|
448
448
|
}
|
|
449
449
|
/**
|
|
450
|
-
* Summarize a set of loop runs into a per-format coverage report
|
|
450
|
+
* Summarize a set of loop runs into a per-format coverage report: the "coverage summary per format" the
|
|
451
451
|
* harness reports.
|
|
452
452
|
*
|
|
453
453
|
* @param results - The loop results to aggregate.
|
package/dist/deid/index.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Hl7MessageKind } from '../hl7/index.js';
|
|
2
2
|
import { DeidPolicy, GenericLocus } from '@cosyte/deid';
|
|
3
3
|
import '@cosyte/hl7';
|
|
4
|
-
import '../providers-
|
|
5
|
-
import '../quirk-
|
|
4
|
+
import '../providers-B9uVinAe.js';
|
|
5
|
+
import '../quirk-HZdznAkM.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* The format-agnostic core of the **`@cosyte/deid` pairing loop
|
|
8
|
+
* The format-agnostic core of the **`@cosyte/deid` pairing loop**: a **closed-loop
|
|
9
9
|
* co-validation harness** for the `synth` ⇄ `deid` pair.
|
|
10
10
|
*
|
|
11
11
|
* The loop's shape is: **generate a spec-clean synthetic artifact → enumerate the distinctive synthetic
|
|
12
12
|
* PHI sentinels `@cosyte/synth` planted at its patient loci → run it through `@cosyte/deid` → verify
|
|
13
13
|
* every planted sentinel is gone from the de-identified output** (a surviving sentinel is a hard
|
|
14
14
|
* failure), **and that the non-PHI clinical content survived** (the over-scrub guard). It is
|
|
15
|
-
* deterministic and seeded, and it does **not** change what the generators emit
|
|
15
|
+
* deterministic and seeded, and it does **not** change what the generators emit: it consumes their
|
|
16
16
|
* output.
|
|
17
17
|
*
|
|
18
18
|
* **Honesty line (governs the whole module).** This is a **co-validation harness**, not an independent
|
|
@@ -20,7 +20,7 @@ import '../quirk-C9t9CkPS.js';
|
|
|
20
20
|
* that the synthetic PHI `synth` plants at the patient loci is removed by `deid`, and that `deid` does
|
|
21
21
|
* not over-scrub `synth`'s clinical payload. A sentinel that `deid` **blocks** rather than redacts still
|
|
22
22
|
* passes the removal check (blocked = gone from the wire). Formats where `@cosyte/deid` ships no adapter
|
|
23
|
-
* (NCPDP **SCRIPT**, **ASTM**)
|
|
23
|
+
* (NCPDP **SCRIPT**, **ASTM**), and DICOM, which `synth` does not generate, are **skipped and named**
|
|
24
24
|
* (see `DEID_LOOP_SKIPPED` in `./index`), never silently.
|
|
25
25
|
*
|
|
26
26
|
* This module holds only the pure, parser-free pieces (the policy, the sentinel model, the sweep, the
|
|
@@ -31,16 +31,16 @@ import '../quirk-C9t9CkPS.js';
|
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* The set of cosyte formats the loop covers
|
|
34
|
+
* The set of cosyte formats the loop covers: every format for which **both** `@cosyte/synth` generates
|
|
35
35
|
* and `@cosyte/deid` ships an adapter.
|
|
36
36
|
*/
|
|
37
37
|
type DeidLoopFormat = "hl7" | "fhir" | "ccda" | "x12" | "ncpdp-telecom";
|
|
38
38
|
/**
|
|
39
39
|
* A single **planted sentinel**: one distinctive, synthetic-by-construction PHI token that
|
|
40
40
|
* `@cosyte/synth` placed at a patient locus of a generated artifact, tracked so the loop can prove
|
|
41
|
-
* `@cosyte/deid` removed it. The token is always drawn from a synthetic-safety provider
|
|
41
|
+
* `@cosyte/deid` removed it. The token is always drawn from a synthetic-safety provider:
|
|
42
42
|
* a fake-name-pool name, a `900`-range SSN, a `555-01xx` phone, a synthetic-assigning-authority
|
|
43
|
-
* identifier
|
|
43
|
+
* identifier, so a sentinel can never be, or collide with, a real person's PHI.
|
|
44
44
|
*
|
|
45
45
|
* @example
|
|
46
46
|
* ```ts
|
|
@@ -48,9 +48,9 @@ type DeidLoopFormat = "hl7" | "fhir" | "ccda" | "x12" | "ncpdp-telecom";
|
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
50
|
interface DeidSentinel {
|
|
51
|
-
/** The exact synthetic token planted at the locus
|
|
51
|
+
/** The exact synthetic token planted at the locus, a literal substring of the spec-clean artifact. */
|
|
52
52
|
readonly token: string;
|
|
53
|
-
/** Where it lives
|
|
53
|
+
/** Where it lives, a format-neutral path (e.g. `"PID-5"`, `"recordTarget"`, `"NM1[0]-3"`). */
|
|
54
54
|
readonly locus: string;
|
|
55
55
|
/** The Safe Harbor category `@cosyte/deid` assigned to the locus, when known. */
|
|
56
56
|
readonly category?: string;
|
|
@@ -74,15 +74,15 @@ interface DeidLoopResult {
|
|
|
74
74
|
readonly format: DeidLoopFormat;
|
|
75
75
|
/** The concrete artifact label (e.g. `"ORU^R01"`, `"837P"`, `"CCD"`). */
|
|
76
76
|
readonly artifact: string;
|
|
77
|
-
/** The seed
|
|
77
|
+
/** The seed, the same seed yields the same artifact and the same verdict. */
|
|
78
78
|
readonly seed: number;
|
|
79
79
|
/** Every distinctive synthetic PHI sentinel `synth` planted at a patient locus of the artifact. */
|
|
80
80
|
readonly planted: readonly DeidSentinel[];
|
|
81
|
-
/** Planted sentinels still present in the de-identified output
|
|
81
|
+
/** Planted sentinels still present in the de-identified output, **must be empty**. */
|
|
82
82
|
readonly survivors: readonly DeidSentinel[];
|
|
83
83
|
/** The non-PHI clinical code tokens probed for over-scrub (present in the spec-clean artifact). */
|
|
84
84
|
readonly clinicalProbed: readonly string[];
|
|
85
|
-
/** Probed clinical tokens missing after de-identification (over-scrub)
|
|
85
|
+
/** Probed clinical tokens missing after de-identification (over-scrub), **must be empty**. */
|
|
86
86
|
readonly clinicalScrubbed: readonly string[];
|
|
87
87
|
/** The spec-clean serialized artifact (before de-identification). */
|
|
88
88
|
readonly original: string;
|
|
@@ -108,7 +108,7 @@ declare const DEID_LOOP_POLICY_NAME = "synth-deid-loop-removal";
|
|
|
108
108
|
*
|
|
109
109
|
* Two reasons this is the right policy for a co-validation loop: (1) it needs **no key context**, so the
|
|
110
110
|
* loop is a pure function of the seed (the default Safe Harbor policy pseudonymizes those three
|
|
111
|
-
* categories, which requires the consumer's HMAC key); and (2) it makes the removal contract crisp
|
|
111
|
+
* categories, which requires the consumer's HMAC key); and (2) it makes the removal contract crisp,
|
|
112
112
|
* every PHI locus is **removed or generalized away**, never replaced by a surrogate that a naive sweep
|
|
113
113
|
* might mistake for a survivor. `pseudonymize` also removes the original value, so this is a stricter,
|
|
114
114
|
* not weaker, check.
|
|
@@ -127,7 +127,7 @@ declare function deidLoopPolicy(): DeidPolicy;
|
|
|
127
127
|
* identifier loci, decomposed to **literal** tokens of the spec-clean artifact.
|
|
128
128
|
*
|
|
129
129
|
* The loci come from `@cosyte/deid`'s own per-format extractor (`extractHl7Loci` / `extractFhirLoci` /
|
|
130
|
-
* `extractX12Loci` / `extractTelecomLoci`), which locates PHI **structurally
|
|
130
|
+
* `extractX12Loci` / `extractTelecomLoci`), which locates PHI **structurally**, so provider and
|
|
131
131
|
* organization names, which a de-identifier legitimately retains, are never extracted and never become
|
|
132
132
|
* false sentinels. Only `identifier`-kind loci are used (names, SSNs, phones, emails, member/account
|
|
133
133
|
* ids); dates (generalized to a year, not removed) and ZIPs (generalized to three digits) are handled by
|
|
@@ -138,7 +138,7 @@ declare function deidLoopPolicy(): DeidPolicy;
|
|
|
138
138
|
*
|
|
139
139
|
* @param loci - The located candidate values from a `@cosyte/deid` extractor.
|
|
140
140
|
* @param original - The spec-clean serialized artifact the loci were extracted from.
|
|
141
|
-
* @returns The distinctive, literal, synthetic PHI sentinels
|
|
141
|
+
* @returns The distinctive, literal, synthetic PHI sentinels, de-duplicated by token.
|
|
142
142
|
* @example
|
|
143
143
|
* ```ts
|
|
144
144
|
* import { extractHl7Loci } from "@cosyte/deid/hl7";
|
|
@@ -148,7 +148,7 @@ declare function deidLoopPolicy(): DeidPolicy;
|
|
|
148
148
|
declare function identifierSentinels(loci: readonly GenericLocus[], original: string): readonly DeidSentinel[];
|
|
149
149
|
/**
|
|
150
150
|
* Enumerate the patient sentinels of a **C-CDA** document by reading the `<recordTarget>` element of the
|
|
151
|
-
* serialized XML
|
|
151
|
+
* serialized XML: the patient participation. Scoping to `recordTarget` (rather than `author` /
|
|
152
152
|
* `custodian`, which carry provider identity a de-identifier retains) is what keeps the sentinels
|
|
153
153
|
* patient-PHI only. Used for C-CDA because `@cosyte/deid`'s C-CDA extractor operates on a raw DOM, which
|
|
154
154
|
* this zero-dependency module does not construct.
|
|
@@ -163,7 +163,7 @@ declare function identifierSentinels(loci: readonly GenericLocus[], original: st
|
|
|
163
163
|
*/
|
|
164
164
|
declare function recordTargetSentinels(xml: string): readonly DeidSentinel[];
|
|
165
165
|
/**
|
|
166
|
-
* Sweep the **PHI-locus residue** of a de-identified artifact for **surviving** planted sentinels
|
|
166
|
+
* Sweep the **PHI-locus residue** of a de-identified artifact for **surviving** planted sentinels: the
|
|
167
167
|
* heart of the removal check. A sentinel survives if its exact token still appears in the values that
|
|
168
168
|
* remain at the (former) PHI loci after de-identification; a non-empty result is a **hard failure** (real
|
|
169
169
|
* PHI-shaped data leaked through de-identification).
|
|
@@ -172,15 +172,15 @@ declare function recordTargetSentinels(xml: string): readonly DeidSentinel[];
|
|
|
172
172
|
* provider/organization names, addresses, and phones from the same synthetic pools, and a de-identifier
|
|
173
173
|
* legitimately **retains** provider identity. So the same synthetic token can sit at both a patient locus
|
|
174
174
|
* (removed) and a provider locus (retained); a whole-document sweep would read the retained provider copy
|
|
175
|
-
* as a false survivor. Sweeping only the residue at the loci that were PHI
|
|
176
|
-
* de-identifier's own output
|
|
175
|
+
* as a false survivor. Sweeping only the residue at the loci that were PHI, re-read from the
|
|
176
|
+
* de-identifier's own output, is locus-scoped and collision-proof: a token still present *there* is a
|
|
177
177
|
* genuine leak. It remains independent of the de-identifier's manifest: it reads the actual serialized
|
|
178
178
|
* output, so a de-identifier that locates a locus but fails to strip it is still caught.
|
|
179
179
|
*
|
|
180
180
|
* @param phiResidue - The de-identified values that remain at the former PHI loci (for the model formats,
|
|
181
181
|
* the re-extracted identifier-locus values joined; for C-CDA, the de-identified `<recordTarget>` block).
|
|
182
182
|
* @param sentinels - The sentinels planted in the corresponding spec-clean artifact.
|
|
183
|
-
* @returns The subset of `sentinels` still present in `phiResidue
|
|
183
|
+
* @returns The subset of `sentinels` still present in `phiResidue`, empty on a clean pass.
|
|
184
184
|
* @example
|
|
185
185
|
* ```ts
|
|
186
186
|
* sweepSurvivors(deidPhiResidue, planted); // => [] (all removed)
|
|
@@ -195,8 +195,8 @@ declare function sweepSurvivors(phiResidue: string, sentinels: readonly DeidSent
|
|
|
195
195
|
* is not a structured-content loss).
|
|
196
196
|
*
|
|
197
197
|
* Only **distinctive** codes (four or more characters) are probed: a short 2–3 digit code (e.g. a CVX
|
|
198
|
-
* vaccine code) can appear by coincidence *inside* a PHI value a de-identifier removes
|
|
199
|
-
* SSN
|
|
198
|
+
* vaccine code) can appear by coincidence *inside* a PHI value a de-identifier removes: a timestamp, an
|
|
199
|
+
* SSN, and would read as a false over-scrub. Distinctive codes (LOINC `4548-4`, SNOMED `44054006`,
|
|
200
200
|
* 11-digit NDCs, CPT `99213`) do not collide this way. An artifact whose clinical codes are all short
|
|
201
201
|
* simply yields an empty probe (the guard is skipped, never falsely tripped).
|
|
202
202
|
*
|
|
@@ -204,7 +204,7 @@ declare function sweepSurvivors(phiResidue: string, sentinels: readonly DeidSent
|
|
|
204
204
|
* @param deidentified - The serialized de-identified artifact.
|
|
205
205
|
* @param clinicalCodes - Candidate structured clinical code tokens (e.g. LOINC/ICD/NDC from the example
|
|
206
206
|
* pools) to probe.
|
|
207
|
-
* @returns `{ probed, scrubbed }
|
|
207
|
+
* @returns `{ probed, scrubbed }`: the distinctive codes present before, and the subset absent after.
|
|
208
208
|
* @example
|
|
209
209
|
* ```ts
|
|
210
210
|
* const { probed, scrubbed } = clinicalRetention(before, after, ["4548-4", "44054006"]);
|
|
@@ -217,7 +217,7 @@ declare function clinicalRetention(original: string, deidentified: string, clini
|
|
|
217
217
|
};
|
|
218
218
|
/**
|
|
219
219
|
* Assemble a {@link DeidLoopResult} from the planted sentinels, the de-identified output, and the
|
|
220
|
-
* clinical probe
|
|
220
|
+
* clinical probe: computing the `pass` verdict (sentinels planted, none survived, nothing over-scrubbed).
|
|
221
221
|
*
|
|
222
222
|
* @param parts - The pieces gathered by a per-format loop.
|
|
223
223
|
* @returns The immutable verdict.
|
|
@@ -237,7 +237,7 @@ declare function assembleVerdict(parts: {
|
|
|
237
237
|
readonly original: string;
|
|
238
238
|
readonly deidentified: string;
|
|
239
239
|
/**
|
|
240
|
-
* The de-identified values that remain at the former PHI loci
|
|
240
|
+
* The de-identified values that remain at the former PHI loci, what the survivor sweep reads (see
|
|
241
241
|
* {@link sweepSurvivors}). Defaults to `deidentified` when omitted (a whole-document sweep), but the
|
|
242
242
|
* per-format loops supply the locus-scoped residue so provider/organization identity a de-identifier
|
|
243
243
|
* legitimately retains never reads as a false survivor.
|
|
@@ -247,7 +247,7 @@ declare function assembleVerdict(parts: {
|
|
|
247
247
|
}): DeidLoopResult;
|
|
248
248
|
|
|
249
249
|
/**
|
|
250
|
-
* `@cosyte/synth/deid
|
|
250
|
+
* `@cosyte/synth/deid`, the **`@cosyte/deid` pairing loop**: a deterministic,
|
|
251
251
|
* seeded **co-validation harness** for the `synth` ⇄ `deid` pair.
|
|
252
252
|
*
|
|
253
253
|
* For each covered format it **generates** a spec-clean synthetic artifact through `@cosyte/synth`'s own
|
|
@@ -256,7 +256,7 @@ declare function assembleVerdict(parts: {
|
|
|
256
256
|
* the de-identified output while the clinical payload survives. A surviving sentinel is a hard failure;
|
|
257
257
|
* an over-scrubbed clinical value is a hard failure.
|
|
258
258
|
*
|
|
259
|
-
* **Scope & honesty.** This proves *the pair works on `synth`'s own output
|
|
259
|
+
* **Scope & honesty.** This proves *the pair works on `synth`'s own output*: it
|
|
260
260
|
* is **not** an independent audit of `@cosyte/deid` against real-world documents. The sentinels are
|
|
261
261
|
* synthetic-by-construction (never realistic); a sentinel `deid` **blocks** rather than redacts still
|
|
262
262
|
* passes (blocked = gone). The loop covers the five formats both packages support
|
|
@@ -268,7 +268,7 @@ declare function assembleVerdict(parts: {
|
|
|
268
268
|
*/
|
|
269
269
|
|
|
270
270
|
/**
|
|
271
|
-
* The cosyte formats the pairing loop covers
|
|
271
|
+
* The cosyte formats the pairing loop covers: every format for which **both** `@cosyte/synth`
|
|
272
272
|
* generates and `@cosyte/deid` ships an adapter.
|
|
273
273
|
*
|
|
274
274
|
* @example
|
|
@@ -279,7 +279,7 @@ declare function assembleVerdict(parts: {
|
|
|
279
279
|
*/
|
|
280
280
|
declare const DEID_LOOP_COVERED_FORMATS: readonly ["hl7", "fhir", "ccda", "x12", "ncpdp-telecom"];
|
|
281
281
|
/**
|
|
282
|
-
* The format paths the loop **deliberately skips**, each with the honest reason
|
|
282
|
+
* The format paths the loop **deliberately skips**, each with the honest reason, so a coverage gap is
|
|
283
283
|
* named, never silent.
|
|
284
284
|
*
|
|
285
285
|
* @example
|
|
@@ -300,7 +300,7 @@ declare const DEID_LOOP_SKIPPED: readonly [Readonly<{
|
|
|
300
300
|
}>];
|
|
301
301
|
/** Options accepted by {@link hl7DeidLoop}. */
|
|
302
302
|
interface Hl7DeidLoopOptions {
|
|
303
|
-
/** The seed
|
|
303
|
+
/** The seed: the same seed yields the same artifact and the same verdict. Defaults to `0`. */
|
|
304
304
|
readonly seed?: number;
|
|
305
305
|
/** The HL7 v2 message family to generate. Defaults to `"ORU^R01"` (a PHI- and clinically-dense report). */
|
|
306
306
|
readonly kind?: Hl7MessageKind;
|
|
@@ -375,7 +375,7 @@ interface NcpdpTelecomDeidLoopOptions {
|
|
|
375
375
|
/**
|
|
376
376
|
* Run the pairing loop for **NCPDP Telecom** (pharmacy claims): generate a spec-clean transaction
|
|
377
377
|
* through `@cosyte/ncpdp`, plant PHI sentinels at the patient/cardholder segments, de-identify through
|
|
378
|
-
* `@cosyte/deid/ncpdp`, and verify removal + clinical retention. **NCPDP SCRIPT is out of scope
|
|
378
|
+
* `@cosyte/deid/ncpdp`, and verify removal + clinical retention. **NCPDP SCRIPT is out of scope**:
|
|
379
379
|
* `@cosyte/deid` ships no SCRIPT adapter ({@link DEID_LOOP_SKIPPED}).
|
|
380
380
|
*
|
|
381
381
|
* @param options - The seed and transaction. See {@link NcpdpTelecomDeidLoopOptions}.
|
|
@@ -422,11 +422,11 @@ interface DeidCoverageRow {
|
|
|
422
422
|
readonly artifacts: readonly string[];
|
|
423
423
|
/** Total distinctive PHI sentinels planted across the runs. */
|
|
424
424
|
readonly planted: number;
|
|
425
|
-
/** Total surviving sentinels
|
|
425
|
+
/** Total surviving sentinels, **0** on a clean pass. */
|
|
426
426
|
readonly survivors: number;
|
|
427
427
|
/** Total clinical tokens probed for over-scrub. */
|
|
428
428
|
readonly clinicalProbed: number;
|
|
429
|
-
/** Total clinical tokens over-scrubbed
|
|
429
|
+
/** Total clinical tokens over-scrubbed, **0** on a clean pass. */
|
|
430
430
|
readonly clinicalScrubbed: number;
|
|
431
431
|
/** `true` iff every run for this format passed. */
|
|
432
432
|
readonly pass: boolean;
|
|
@@ -437,9 +437,9 @@ interface DeidCoverageSummary {
|
|
|
437
437
|
readonly byFormat: readonly DeidCoverageRow[];
|
|
438
438
|
/** Total sentinels planted across every run. */
|
|
439
439
|
readonly totalPlanted: number;
|
|
440
|
-
/** Total surviving sentinels across every run
|
|
440
|
+
/** Total surviving sentinels across every run, **0** on a clean pass. */
|
|
441
441
|
readonly totalSurvivors: number;
|
|
442
|
-
/** Total clinical over-scrubs across every run
|
|
442
|
+
/** Total clinical over-scrubs across every run, **0** on a clean pass. */
|
|
443
443
|
readonly totalClinicalScrubbed: number;
|
|
444
444
|
/** `true` iff every run passed. */
|
|
445
445
|
readonly allPass: boolean;
|
|
@@ -447,7 +447,7 @@ interface DeidCoverageSummary {
|
|
|
447
447
|
readonly skipped: typeof DEID_LOOP_SKIPPED;
|
|
448
448
|
}
|
|
449
449
|
/**
|
|
450
|
-
* Summarize a set of loop runs into a per-format coverage report
|
|
450
|
+
* Summarize a set of loop runs into a per-format coverage report: the "coverage summary per format" the
|
|
451
451
|
* harness reports.
|
|
452
452
|
*
|
|
453
453
|
* @param results - The loop results to aggregate.
|
package/dist/deid/index.mjs
CHANGED
|
@@ -45,15 +45,15 @@ function sfc32Next(s) {
|
|
|
45
45
|
var SYNTH_FATAL_CODES = {
|
|
46
46
|
/** A concept's code-system URI has no OID mapping in the C-CDA example-code table. Fatal. */
|
|
47
47
|
SYNTH_UNMAPPED_CODE_SYSTEM: "SYNTH_UNMAPPED_CODE_SYSTEM",
|
|
48
|
-
/** A money value could not be read as an X12 decimal. Fatal
|
|
48
|
+
/** A money value could not be read as an X12 decimal. Fatal: a generator never rounds to a float. */
|
|
49
49
|
SYNTH_INVALID_DECIMAL: "SYNTH_INVALID_DECIMAL",
|
|
50
50
|
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
51
51
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
52
|
-
/** A value was drawn from an empty pool. Fatal
|
|
52
|
+
/** A value was drawn from an empty pool. Fatal, never a fabricated substitute. */
|
|
53
53
|
SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
|
|
54
54
|
/**
|
|
55
55
|
* A caller-supplied selector (a message kind, a document type, a corpus mix entry, a claim
|
|
56
|
-
* variant, a Bundle type, a resource profile) is not in the closed set that governs it. Fatal
|
|
56
|
+
* variant, a Bundle type, a resource profile) is not in the closed set that governs it. Fatal:
|
|
57
57
|
* see `resolveKind`: a selector union is erased at run time, and a selector that falls through
|
|
58
58
|
* either mislabels the fixture or hands the value to a peer builder that quotes it back.
|
|
59
59
|
*/
|
|
@@ -134,7 +134,7 @@ function componentsField(components) {
|
|
|
134
134
|
var SYNTHETIC_ASSIGNING_AUTHORITY = Object.freeze({
|
|
135
135
|
/** The human-readable assigning-authority namespace id (HL7 HD.1). */
|
|
136
136
|
namespaceId: "COSYTE-SYNTH",
|
|
137
|
-
/** The universal id
|
|
137
|
+
/** The universal id, an OID under HL7's example arc `2.16.840.1.113883.19` (HD.2). */
|
|
138
138
|
universalId: "2.16.840.1.113883.19.999",
|
|
139
139
|
/** The universal id type (HD.3). */
|
|
140
140
|
universalIdType: "ISO"
|
|
@@ -832,23 +832,23 @@ var SYSTEM = Object.freeze({
|
|
|
832
832
|
IDENTIFIER_TYPE: "http://terminology.hl7.org/CodeSystem/v2-0203",
|
|
833
833
|
/** OMB race & ethnicity category system (US Core race/ethnicity `ombCategory`). */
|
|
834
834
|
OMB_RACE_ETHNICITY: "urn:oid:2.16.840.1.113883.6.238",
|
|
835
|
-
/** LOINC
|
|
835
|
+
/** LOINC, `Observation.code` (lab + vital-signs). */
|
|
836
836
|
LOINC: "http://loinc.org",
|
|
837
|
-
/** SNOMED CT
|
|
837
|
+
/** SNOMED CT, `Condition.code`. */
|
|
838
838
|
SNOMED: "http://snomed.info/sct",
|
|
839
|
-
/** RxNorm
|
|
839
|
+
/** RxNorm, `MedicationRequest.medicationCodeableConcept` + an allergen substance. */
|
|
840
840
|
RXNORM: "http://www.nlm.nih.gov/research/umls/rxnorm",
|
|
841
|
-
/** UCUM
|
|
841
|
+
/** UCUM, `Quantity.system` for units of measure. */
|
|
842
842
|
UCUM: "http://unitsofmeasure.org",
|
|
843
|
-
/** CVX (CDC vaccine administered)
|
|
843
|
+
/** CVX (CDC vaccine administered), `Immunization.vaccineCode`. */
|
|
844
844
|
CVX: "http://hl7.org/fhir/sid/cvx",
|
|
845
|
-
/** HL7 v3 `ActCode
|
|
845
|
+
/** HL7 v3 `ActCode`, `Encounter.class`. */
|
|
846
846
|
V3_ACT_CODE: "http://terminology.hl7.org/CodeSystem/v3-ActCode",
|
|
847
|
-
/** HL7 v2 `0074` diagnostic-service-section
|
|
847
|
+
/** HL7 v2 `0074` diagnostic-service-section, `DiagnosticReport.category` (`LAB`). */
|
|
848
848
|
DIAGNOSTIC_SERVICE_SECTION: "http://terminology.hl7.org/CodeSystem/v2-0074",
|
|
849
|
-
/** HL7 Terminology `allergyintolerance-clinical
|
|
849
|
+
/** HL7 Terminology `allergyintolerance-clinical`, `AllergyIntolerance.clinicalStatus`. */
|
|
850
850
|
ALLERGY_CLINICAL: "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical",
|
|
851
|
-
/** HL7 Terminology `allergyintolerance-verification
|
|
851
|
+
/** HL7 Terminology `allergyintolerance-verification`, `AllergyIntolerance.verificationStatus`. */
|
|
852
852
|
ALLERGY_VERIFICATION: "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification"
|
|
853
853
|
});
|
|
854
854
|
|
|
@@ -1987,7 +1987,7 @@ function specOf(rng, variant) {
|
|
|
1987
1987
|
claims: [
|
|
1988
1988
|
{
|
|
1989
1989
|
...claim,
|
|
1990
|
-
// Rendering provider (Loop 2310B, NM1 fields round-trip)
|
|
1990
|
+
// Rendering provider (Loop 2310B, NM1 fields round-trip), a person with an invalid-Luhn NPI.
|
|
1991
1991
|
providers: [
|
|
1992
1992
|
{
|
|
1993
1993
|
entityIdentifierCode: "82",
|
|
@@ -2014,7 +2014,7 @@ function specOf(rng, variant) {
|
|
|
2014
2014
|
state: org.address.state,
|
|
2015
2015
|
postalCode: org.address.zip
|
|
2016
2016
|
},
|
|
2017
|
-
// Provider tax id as a never-issued (900-range) SSN at REF*SY
|
|
2017
|
+
// Provider tax id as a never-issued (900-range) SSN at REF*SY, provably synthetic (roadmap §4.1).
|
|
2018
2018
|
references: [{ qualifier: "SY", value: org.taxIdSsn }]
|
|
2019
2019
|
},
|
|
2020
2020
|
subscribers: [subscriber]
|