@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.
Files changed (57) hide show
  1. package/CHANGELOG.md +135 -17
  2. package/README.md +47 -45
  3. package/dist/astm/index.cjs +7 -7
  4. package/dist/astm/index.cjs.map +1 -1
  5. package/dist/astm/index.d.cts +47 -47
  6. package/dist/astm/index.d.ts +47 -47
  7. package/dist/astm/index.mjs +7 -7
  8. package/dist/astm/index.mjs.map +1 -1
  9. package/dist/ccda/index.cjs +15 -15
  10. package/dist/ccda/index.cjs.map +1 -1
  11. package/dist/ccda/index.d.cts +31 -31
  12. package/dist/ccda/index.d.ts +31 -31
  13. package/dist/ccda/index.mjs +15 -15
  14. package/dist/ccda/index.mjs.map +1 -1
  15. package/dist/deid/index.cjs +15 -15
  16. package/dist/deid/index.cjs.map +1 -1
  17. package/dist/deid/index.d.cts +37 -37
  18. package/dist/deid/index.d.ts +37 -37
  19. package/dist/deid/index.mjs +15 -15
  20. package/dist/deid/index.mjs.map +1 -1
  21. package/dist/{example-codes-DeXcnCSK.d.cts → example-codes-88f3_bQZ.d.cts} +8 -8
  22. package/dist/{example-codes-DeXcnCSK.d.ts → example-codes-88f3_bQZ.d.ts} +8 -8
  23. package/dist/fhir/index.cjs +12 -12
  24. package/dist/fhir/index.cjs.map +1 -1
  25. package/dist/fhir/index.d.cts +46 -46
  26. package/dist/fhir/index.d.ts +46 -46
  27. package/dist/fhir/index.mjs +12 -12
  28. package/dist/fhir/index.mjs.map +1 -1
  29. package/dist/hl7/index.cjs +6 -6
  30. package/dist/hl7/index.cjs.map +1 -1
  31. package/dist/hl7/index.d.cts +45 -45
  32. package/dist/hl7/index.d.ts +45 -45
  33. package/dist/hl7/index.mjs +6 -6
  34. package/dist/hl7/index.mjs.map +1 -1
  35. package/dist/index.cjs +9 -9
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +49 -49
  38. package/dist/index.d.ts +49 -49
  39. package/dist/index.mjs +9 -9
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/ncpdp/index.cjs +3 -3
  42. package/dist/ncpdp/index.cjs.map +1 -1
  43. package/dist/ncpdp/index.d.cts +42 -42
  44. package/dist/ncpdp/index.d.ts +42 -42
  45. package/dist/ncpdp/index.mjs +3 -3
  46. package/dist/ncpdp/index.mjs.map +1 -1
  47. package/dist/{providers-OLz3zAc-.d.cts → providers-B9uVinAe.d.cts} +24 -24
  48. package/dist/{providers-OLz3zAc-.d.ts → providers-B9uVinAe.d.ts} +24 -24
  49. package/dist/{quirk-C9t9CkPS.d.ts → quirk-HZdznAkM.d.ts} +23 -23
  50. package/dist/{quirk-DYMDojVw.d.cts → quirk-IaHp4z7N.d.cts} +23 -23
  51. package/dist/x12/index.cjs +6 -6
  52. package/dist/x12/index.cjs.map +1 -1
  53. package/dist/x12/index.d.cts +44 -44
  54. package/dist/x12/index.d.ts +44 -44
  55. package/dist/x12/index.mjs +6 -6
  56. package/dist/x12/index.mjs.map +1 -1
  57. package/package.json +3 -2
@@ -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-OLz3zAc-.cjs';
5
- import '../quirk-DYMDojVw.cjs';
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** — a **closed-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 — it consumes their
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**) — and DICOM, which `synth` does not generate — are **skipped and named**
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 — every format for which **both** `@cosyte/synth` generates
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 — so a sentinel can never be, or collide with, a real person's PHI.
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 — a literal substring of the spec-clean artifact. */
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 — a format-neutral path (e.g. `"PID-5"`, `"recordTarget"`, `"NM1[0]-3"`). */
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 — the same seed yields the same artifact and the same verdict. */
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 — **must be empty**. */
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) — **must be empty**. */
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** — so provider and
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 — de-duplicated by token.
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 — the patient participation. Scoping to `recordTarget` (rather than `author` /
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 — the
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 — re-read from the
176
- * de-identifier's own output — is locus-scoped and collision-proof: a token still present *there* is a
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` — empty on a clean pass.
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 — a timestamp, an
199
- * SSN — and would read as a false over-scrub. Distinctive codes (LOINC `4548-4`, SNOMED `44054006`,
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 }` — the distinctive codes present before, and the subset absent after.
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 — computing the `pass` verdict (sentinels planted, none survived, nothing over-scrubbed).
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 — what the survivor sweep reads (see
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` — the **`@cosyte/deid` pairing loop**: a deterministic,
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* — it
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 — every format for which **both** `@cosyte/synth`
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 — so a coverage gap is
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 — the same seed yields the same artifact and the same verdict. Defaults to `0`. */
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 — **0** on a clean pass. */
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 — **0** on a clean pass. */
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 — **0** on a clean pass. */
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 — **0** on a clean pass. */
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 — the "coverage summary per format" the
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.
@@ -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-OLz3zAc-.js';
5
- import '../quirk-C9t9CkPS.js';
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** — a **closed-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 — it consumes their
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**) — and DICOM, which `synth` does not generate — are **skipped and named**
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 — every format for which **both** `@cosyte/synth` generates
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 — so a sentinel can never be, or collide with, a real person's PHI.
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 — a literal substring of the spec-clean artifact. */
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 — a format-neutral path (e.g. `"PID-5"`, `"recordTarget"`, `"NM1[0]-3"`). */
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 — the same seed yields the same artifact and the same verdict. */
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 — **must be empty**. */
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) — **must be empty**. */
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** — so provider and
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 — de-duplicated by token.
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 — the patient participation. Scoping to `recordTarget` (rather than `author` /
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 — the
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 — re-read from the
176
- * de-identifier's own output — is locus-scoped and collision-proof: a token still present *there* is a
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` — empty on a clean pass.
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 — a timestamp, an
199
- * SSN — and would read as a false over-scrub. Distinctive codes (LOINC `4548-4`, SNOMED `44054006`,
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 }` — the distinctive codes present before, and the subset absent after.
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 — computing the `pass` verdict (sentinels planted, none survived, nothing over-scrubbed).
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 — what the survivor sweep reads (see
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` — the **`@cosyte/deid` pairing loop**: a deterministic,
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* — it
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 — every format for which **both** `@cosyte/synth`
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 — so a coverage gap is
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 — the same seed yields the same artifact and the same verdict. Defaults to `0`. */
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 — **0** on a clean pass. */
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 — **0** on a clean pass. */
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 — **0** on a clean pass. */
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 — **0** on a clean pass. */
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 — the "coverage summary per format" the
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.
@@ -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 — a generator never rounds to a float. */
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 — never a fabricated substitute. */
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 — an OID under HL7's example arc `2.16.840.1.113883.19` (HD.2). */
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 — `Observation.code` (lab + vital-signs). */
835
+ /** LOINC, `Observation.code` (lab + vital-signs). */
836
836
  LOINC: "http://loinc.org",
837
- /** SNOMED CT — `Condition.code`. */
837
+ /** SNOMED CT, `Condition.code`. */
838
838
  SNOMED: "http://snomed.info/sct",
839
- /** RxNorm — `MedicationRequest.medicationCodeableConcept` + an allergen substance. */
839
+ /** RxNorm, `MedicationRequest.medicationCodeableConcept` + an allergen substance. */
840
840
  RXNORM: "http://www.nlm.nih.gov/research/umls/rxnorm",
841
- /** UCUM — `Quantity.system` for units of measure. */
841
+ /** UCUM, `Quantity.system` for units of measure. */
842
842
  UCUM: "http://unitsofmeasure.org",
843
- /** CVX (CDC vaccine administered) — `Immunization.vaccineCode`. */
843
+ /** CVX (CDC vaccine administered), `Immunization.vaccineCode`. */
844
844
  CVX: "http://hl7.org/fhir/sid/cvx",
845
- /** HL7 v3 `ActCode` — `Encounter.class`. */
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 — `DiagnosticReport.category` (`LAB`). */
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` — `AllergyIntolerance.clinicalStatus`. */
849
+ /** HL7 Terminology `allergyintolerance-clinical`, `AllergyIntolerance.clinicalStatus`. */
850
850
  ALLERGY_CLINICAL: "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical",
851
- /** HL7 Terminology `allergyintolerance-verification` — `AllergyIntolerance.verificationStatus`. */
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) — a person with an invalid-Luhn NPI.
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 — provably synthetic (roadmap §4.1).
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]