@cosyte/synth 0.0.1

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 (58) hide show
  1. package/CHANGELOG.md +414 -0
  2. package/LICENSE +21 -0
  3. package/README.md +325 -0
  4. package/dist/astm/index.cjs +847 -0
  5. package/dist/astm/index.cjs.map +1 -0
  6. package/dist/astm/index.d.cts +418 -0
  7. package/dist/astm/index.d.ts +418 -0
  8. package/dist/astm/index.mjs +828 -0
  9. package/dist/astm/index.mjs.map +1 -0
  10. package/dist/ccda/index.cjs +1103 -0
  11. package/dist/ccda/index.cjs.map +1 -0
  12. package/dist/ccda/index.d.cts +380 -0
  13. package/dist/ccda/index.d.ts +380 -0
  14. package/dist/ccda/index.mjs +1077 -0
  15. package/dist/ccda/index.mjs.map +1 -0
  16. package/dist/deid/index.cjs +2809 -0
  17. package/dist/deid/index.cjs.map +1 -0
  18. package/dist/deid/index.d.cts +464 -0
  19. package/dist/deid/index.d.ts +464 -0
  20. package/dist/deid/index.mjs +2793 -0
  21. package/dist/deid/index.mjs.map +1 -0
  22. package/dist/example-codes-DeXcnCSK.d.cts +105 -0
  23. package/dist/example-codes-DeXcnCSK.d.ts +105 -0
  24. package/dist/fhir/index.cjs +1429 -0
  25. package/dist/fhir/index.cjs.map +1 -0
  26. package/dist/fhir/index.d.cts +772 -0
  27. package/dist/fhir/index.d.ts +772 -0
  28. package/dist/fhir/index.mjs +1384 -0
  29. package/dist/fhir/index.mjs.map +1 -0
  30. package/dist/hl7/index.cjs +1012 -0
  31. package/dist/hl7/index.cjs.map +1 -0
  32. package/dist/hl7/index.d.cts +548 -0
  33. package/dist/hl7/index.d.ts +548 -0
  34. package/dist/hl7/index.mjs +990 -0
  35. package/dist/hl7/index.mjs.map +1 -0
  36. package/dist/index.cjs +535 -0
  37. package/dist/index.cjs.map +1 -0
  38. package/dist/index.d.cts +407 -0
  39. package/dist/index.d.ts +407 -0
  40. package/dist/index.mjs +488 -0
  41. package/dist/index.mjs.map +1 -0
  42. package/dist/ncpdp/index.cjs +714 -0
  43. package/dist/ncpdp/index.cjs.map +1 -0
  44. package/dist/ncpdp/index.d.cts +432 -0
  45. package/dist/ncpdp/index.d.ts +432 -0
  46. package/dist/ncpdp/index.mjs +695 -0
  47. package/dist/ncpdp/index.mjs.map +1 -0
  48. package/dist/providers-OLz3zAc-.d.cts +343 -0
  49. package/dist/providers-OLz3zAc-.d.ts +343 -0
  50. package/dist/quirk-DmkgoZdh.d.cts +239 -0
  51. package/dist/quirk-JLyO1Ncj.d.ts +239 -0
  52. package/dist/x12/index.cjs +920 -0
  53. package/dist/x12/index.cjs.map +1 -0
  54. package/dist/x12/index.d.cts +484 -0
  55. package/dist/x12/index.d.ts +484 -0
  56. package/dist/x12/index.mjs +892 -0
  57. package/dist/x12/index.mjs.map +1 -0
  58. package/package.json +210 -0
@@ -0,0 +1,464 @@
1
+ import { Hl7MessageKind } from '../hl7/index.js';
2
+ import { DeidPolicy, GenericLocus } from '@cosyte/deid';
3
+ import '@cosyte/hl7';
4
+ import '../providers-OLz3zAc-.js';
5
+ import '../quirk-JLyO1Ncj.js';
6
+
7
+ /**
8
+ * The format-agnostic core of the **`@cosyte/deid` pairing loop** — a **closed-loop
9
+ * co-validation harness** for the `synth` ⇄ `deid` pair.
10
+ *
11
+ * The loop's shape is: **generate a spec-clean synthetic artifact → enumerate the distinctive synthetic
12
+ * PHI sentinels `@cosyte/synth` planted at its patient loci → run it through `@cosyte/deid` → verify
13
+ * every planted sentinel is gone from the de-identified output** (a surviving sentinel is a hard
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
16
+ * output.
17
+ *
18
+ * **Honesty line (governs the whole module).** This is a **co-validation harness**, not an independent
19
+ * audit of `@cosyte/deid` against real-world data. It proves *the pair works on `synth`'s own output*:
20
+ * that the synthetic PHI `synth` plants at the patient loci is removed by `deid`, and that `deid` does
21
+ * not over-scrub `synth`'s clinical payload. A sentinel that `deid` **blocks** rather than redacts still
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**
24
+ * (see `DEID_LOOP_SKIPPED` in `./index`), never silently.
25
+ *
26
+ * This module holds only the pure, parser-free pieces (the policy, the sentinel model, the sweep, the
27
+ * over-scrub check, the verdict). The per-format loops that drive the generators and the `deid` adapters
28
+ * live in `./index`.
29
+ *
30
+ * @module
31
+ */
32
+
33
+ /**
34
+ * The set of cosyte formats the loop covers — every format for which **both** `@cosyte/synth` generates
35
+ * and `@cosyte/deid` ships an adapter.
36
+ */
37
+ type DeidLoopFormat = "hl7" | "fhir" | "ccda" | "x12" | "ncpdp-telecom";
38
+ /**
39
+ * A single **planted sentinel**: one distinctive, synthetic-by-construction PHI token that
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 —
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.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const s: DeidSentinel = { token: "Examplewood", locus: "PID-5", category: "names" };
48
+ * ```
49
+ */
50
+ interface DeidSentinel {
51
+ /** The exact synthetic token planted at the locus — a literal substring of the spec-clean artifact. */
52
+ readonly token: string;
53
+ /** Where it lives — a format-neutral path (e.g. `"PID-5"`, `"recordTarget"`, `"NM1[0]-3"`). */
54
+ readonly locus: string;
55
+ /** The Safe Harbor category `@cosyte/deid` assigned to the locus, when known. */
56
+ readonly category?: string;
57
+ }
58
+ /**
59
+ * The verdict of one pass through the pairing loop for one artifact.
60
+ *
61
+ * `pass` is `true` **iff** at least one sentinel was planted, **no** planted sentinel survived
62
+ * de-identification, and **no** probed clinical value was over-scrubbed.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * import { hl7DeidLoop } from "@cosyte/synth/deid";
67
+ * const r = hl7DeidLoop({ seed: 42 });
68
+ * r.pass; // => true
69
+ * r.survivors.length; // => 0
70
+ * ```
71
+ */
72
+ interface DeidLoopResult {
73
+ /** The covered format. */
74
+ readonly format: DeidLoopFormat;
75
+ /** The concrete artifact label (e.g. `"ORU^R01"`, `"837P"`, `"CCD"`). */
76
+ readonly artifact: string;
77
+ /** The seed — the same seed yields the same artifact and the same verdict. */
78
+ readonly seed: number;
79
+ /** Every distinctive synthetic PHI sentinel `synth` planted at a patient locus of the artifact. */
80
+ readonly planted: readonly DeidSentinel[];
81
+ /** Planted sentinels still present in the de-identified output — **must be empty**. */
82
+ readonly survivors: readonly DeidSentinel[];
83
+ /** The non-PHI clinical code tokens probed for over-scrub (present in the spec-clean artifact). */
84
+ readonly clinicalProbed: readonly string[];
85
+ /** Probed clinical tokens missing after de-identification (over-scrub) — **must be empty**. */
86
+ readonly clinicalScrubbed: readonly string[];
87
+ /** The spec-clean serialized artifact (before de-identification). */
88
+ readonly original: string;
89
+ /** The serialized de-identified artifact (`@cosyte/deid`'s output). */
90
+ readonly deidentified: string;
91
+ /** `true` iff sentinels were planted, none survived, and nothing clinical was over-scrubbed. */
92
+ readonly pass: boolean;
93
+ }
94
+ /**
95
+ * The name of the removal-oriented policy the loop runs `@cosyte/deid` under.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * import { DEID_LOOP_POLICY_NAME } from "@cosyte/synth/deid";
100
+ * DEID_LOOP_POLICY_NAME; // => "synth-deid-loop-removal"
101
+ * ```
102
+ */
103
+ declare const DEID_LOOP_POLICY_NAME = "synth-deid-loop-removal";
104
+ /**
105
+ * Build the **removal-oriented** de-identification policy the loop runs `@cosyte/deid` under: HIPAA Safe
106
+ * Harbor, but with the three keyed-by-default identifier categories (MRN, health-plan-beneficiary,
107
+ * account) switched from `pseudonymize` to `redact`.
108
+ *
109
+ * Two reasons this is the right policy for a co-validation loop: (1) it needs **no key context**, so the
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 —
112
+ * every PHI locus is **removed or generalized away**, never replaced by a surrogate that a naive sweep
113
+ * might mistake for a survivor. `pseudonymize` also removes the original value, so this is a stricter,
114
+ * not weaker, check.
115
+ *
116
+ * @returns A frozen {@link DeidPolicy} suitable for the `policy` option of any `@cosyte/deid` adapter.
117
+ * @example
118
+ * ```ts
119
+ * import { deidLoopPolicy } from "@cosyte/synth/deid";
120
+ * import { deidentifyHl7 } from "@cosyte/deid/hl7";
121
+ * const { document } = deidentifyHl7(msg, { policy: deidLoopPolicy() });
122
+ * ```
123
+ */
124
+ declare function deidLoopPolicy(): DeidPolicy;
125
+ /**
126
+ * Enumerate the distinctive synthetic PHI sentinels from a de-identifier's **patient-scoped**
127
+ * identifier loci, decomposed to **literal** tokens of the spec-clean artifact.
128
+ *
129
+ * The loci come from `@cosyte/deid`'s own per-format extractor (`extractHl7Loci` / `extractFhirLoci` /
130
+ * `extractX12Loci` / `extractTelecomLoci`), which locates PHI **structurally** — so provider and
131
+ * organization names, which a de-identifier legitimately retains, are never extracted and never become
132
+ * false sentinels. Only `identifier`-kind loci are used (names, SSNs, phones, emails, member/account
133
+ * ids); dates (generalized to a year, not removed) and ZIPs (generalized to three digits) are handled by
134
+ * the format's own contract, not asserted here.
135
+ *
136
+ * Requiring each token to be a **literal substring of `original`** keeps the removal check non-vacuous:
137
+ * a sentinel the sweep later looks for is one that is provably present before de-identification.
138
+ *
139
+ * @param loci - The located candidate values from a `@cosyte/deid` extractor.
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.
142
+ * @example
143
+ * ```ts
144
+ * import { extractHl7Loci } from "@cosyte/deid/hl7";
145
+ * const sentinels = identifierSentinels(extractHl7Loci(msg).loci, msg.toString());
146
+ * ```
147
+ */
148
+ declare function identifierSentinels(loci: readonly GenericLocus[], original: string): readonly DeidSentinel[];
149
+ /**
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` /
152
+ * `custodian`, which carry provider identity a de-identifier retains) is what keeps the sentinels
153
+ * patient-PHI only. Used for C-CDA because `@cosyte/deid`'s C-CDA extractor operates on a raw DOM, which
154
+ * this zero-dependency module does not construct.
155
+ *
156
+ * @param xml - The serialized spec-clean C-CDA document.
157
+ * @returns The distinctive synthetic patient sentinels (given/family names, the patient id extensions).
158
+ * @example
159
+ * ```ts
160
+ * import { serializeCcda } from "@cosyte/ccda";
161
+ * const sentinels = recordTargetSentinels(serializeCcda(generateCcd({ seed: 1 })));
162
+ * ```
163
+ */
164
+ declare function recordTargetSentinels(xml: string): readonly DeidSentinel[];
165
+ /**
166
+ * Sweep the **PHI-locus residue** of a de-identified artifact for **surviving** planted sentinels — the
167
+ * heart of the removal check. A sentinel survives if its exact token still appears in the values that
168
+ * remain at the (former) PHI loci after de-identification; a non-empty result is a **hard failure** (real
169
+ * PHI-shaped data leaked through de-identification).
170
+ *
171
+ * **Why the PHI-locus residue and not the whole document.** `@cosyte/synth` draws patient *and*
172
+ * provider/organization names, addresses, and phones from the same synthetic pools, and a de-identifier
173
+ * legitimately **retains** provider identity. So the same synthetic token can sit at both a patient locus
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
177
+ * genuine leak. It remains independent of the de-identifier's manifest: it reads the actual serialized
178
+ * output, so a de-identifier that locates a locus but fails to strip it is still caught.
179
+ *
180
+ * @param phiResidue - The de-identified values that remain at the former PHI loci (for the model formats,
181
+ * the re-extracted identifier-locus values joined; for C-CDA, the de-identified `<recordTarget>` block).
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.
184
+ * @example
185
+ * ```ts
186
+ * sweepSurvivors(deidPhiResidue, planted); // => [] (all removed)
187
+ * ```
188
+ */
189
+ declare function sweepSurvivors(phiResidue: string, sentinels: readonly DeidSentinel[]): readonly DeidSentinel[];
190
+ /**
191
+ * The over-scrub side of the loop: of the clinical (non-PHI) code tokens that appear in the spec-clean
192
+ * artifact, which are **missing** after de-identification. A de-identifier must keep clinical content; a
193
+ * non-empty result means it over-scrubbed. Only structured code tokens present before de-identification
194
+ * are probed (a code that appears solely inside a free-text narrative a de-identifier legitimately blocks
195
+ * is not a structured-content loss).
196
+ *
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`,
200
+ * 11-digit NDCs, CPT `99213`) do not collide this way. An artifact whose clinical codes are all short
201
+ * simply yields an empty probe (the guard is skipped, never falsely tripped).
202
+ *
203
+ * @param original - The spec-clean serialized artifact.
204
+ * @param deidentified - The serialized de-identified artifact.
205
+ * @param clinicalCodes - Candidate structured clinical code tokens (e.g. LOINC/ICD/NDC from the example
206
+ * pools) to probe.
207
+ * @returns `{ probed, scrubbed }` — the distinctive codes present before, and the subset absent after.
208
+ * @example
209
+ * ```ts
210
+ * const { probed, scrubbed } = clinicalRetention(before, after, ["4548-4", "44054006"]);
211
+ * scrubbed; // => [] (clinical content retained)
212
+ * ```
213
+ */
214
+ declare function clinicalRetention(original: string, deidentified: string, clinicalCodes: readonly string[]): {
215
+ readonly probed: readonly string[];
216
+ readonly scrubbed: readonly string[];
217
+ };
218
+ /**
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).
221
+ *
222
+ * @param parts - The pieces gathered by a per-format loop.
223
+ * @returns The immutable verdict.
224
+ * @example
225
+ * ```ts
226
+ * const result = assembleVerdict({
227
+ * format: "hl7", artifact: "ORU^R01", seed: 1,
228
+ * planted, original, deidentified, clinicalCodes,
229
+ * });
230
+ * ```
231
+ */
232
+ declare function assembleVerdict(parts: {
233
+ readonly format: DeidLoopFormat;
234
+ readonly artifact: string;
235
+ readonly seed: number;
236
+ readonly planted: readonly DeidSentinel[];
237
+ readonly original: string;
238
+ readonly deidentified: string;
239
+ /**
240
+ * The de-identified values that remain at the former PHI loci — what the survivor sweep reads (see
241
+ * {@link sweepSurvivors}). Defaults to `deidentified` when omitted (a whole-document sweep), but the
242
+ * per-format loops supply the locus-scoped residue so provider/organization identity a de-identifier
243
+ * legitimately retains never reads as a false survivor.
244
+ */
245
+ readonly phiResidue?: string;
246
+ readonly clinicalCodes: readonly string[];
247
+ }): DeidLoopResult;
248
+
249
+ /**
250
+ * `@cosyte/synth/deid` — the **`@cosyte/deid` pairing loop**: a deterministic,
251
+ * seeded **co-validation harness** for the `synth` ⇄ `deid` pair.
252
+ *
253
+ * For each covered format it **generates** a spec-clean synthetic artifact through `@cosyte/synth`'s own
254
+ * generators, **enumerates** the distinctive synthetic PHI sentinels planted at the patient loci,
255
+ * **de-identifies** it through `@cosyte/deid`, and **verifies** that every planted sentinel is gone from
256
+ * the de-identified output while the clinical payload survives. A surviving sentinel is a hard failure;
257
+ * an over-scrubbed clinical value is a hard failure.
258
+ *
259
+ * **Scope & honesty.** This proves *the pair works on `synth`'s own output* — it
260
+ * is **not** an independent audit of `@cosyte/deid` against real-world documents. The sentinels are
261
+ * synthetic-by-construction (never realistic); a sentinel `deid` **blocks** rather than redacts still
262
+ * passes (blocked = gone). The loop covers the five formats both packages support
263
+ * ({@link DEID_LOOP_COVERED_FORMATS}); NCPDP **SCRIPT**, **ASTM**, and **DICOM** are **skipped and
264
+ * named** ({@link DEID_LOOP_SKIPPED}), never silently. `@cosyte/deid` is an **optional peer dependency**
265
+ * (the `mllp`/`ncpdp` vendoring pattern); importing this subpath pulls it in.
266
+ *
267
+ * @module
268
+ */
269
+
270
+ /**
271
+ * The cosyte formats the pairing loop covers — every format for which **both** `@cosyte/synth`
272
+ * generates and `@cosyte/deid` ships an adapter.
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * import { DEID_LOOP_COVERED_FORMATS } from "@cosyte/synth/deid";
277
+ * DEID_LOOP_COVERED_FORMATS; // ["hl7","fhir","ccda","x12","ncpdp-telecom"]
278
+ * ```
279
+ */
280
+ declare const DEID_LOOP_COVERED_FORMATS: readonly ["hl7", "fhir", "ccda", "x12", "ncpdp-telecom"];
281
+ /**
282
+ * The format paths the loop **deliberately skips**, each with the honest reason — so a coverage gap is
283
+ * named, never silent.
284
+ *
285
+ * @example
286
+ * ```ts
287
+ * import { DEID_LOOP_SKIPPED } from "@cosyte/synth/deid";
288
+ * DEID_LOOP_SKIPPED.map((s) => s.format); // ["ncpdp-script","astm","dicom"]
289
+ * ```
290
+ */
291
+ declare const DEID_LOOP_SKIPPED: readonly [Readonly<{
292
+ format: "ncpdp-script";
293
+ reason: "@cosyte/deid ships no NCPDP SCRIPT locus map; synth generates SCRIPT but the loop cannot pair it.";
294
+ }>, Readonly<{
295
+ format: "astm";
296
+ reason: "@cosyte/deid ships no ASTM adapter; synth generates ASTM but the loop cannot pair it.";
297
+ }>, Readonly<{
298
+ format: "dicom";
299
+ reason: "@cosyte/synth does not generate DICOM, so there is nothing to pair.";
300
+ }>];
301
+ /** Options accepted by {@link hl7DeidLoop}. */
302
+ interface Hl7DeidLoopOptions {
303
+ /** The seed — the same seed yields the same artifact and the same verdict. Defaults to `0`. */
304
+ readonly seed?: number;
305
+ /** The HL7 v2 message family to generate. Defaults to `"ORU^R01"` (a PHI- and clinically-dense report). */
306
+ readonly kind?: Hl7MessageKind;
307
+ }
308
+ /**
309
+ * Run the pairing loop for **HL7 v2**: generate a spec-clean message through `@cosyte/hl7`'s builder,
310
+ * plant PHI sentinels at the `PID`, de-identify through `@cosyte/deid/hl7`, and verify removal +
311
+ * clinical retention.
312
+ *
313
+ * @param options - The seed and message family. See {@link Hl7DeidLoopOptions}.
314
+ * @returns The {@link DeidLoopResult} for the message.
315
+ * @example
316
+ * ```ts
317
+ * import { hl7DeidLoop } from "@cosyte/synth/deid";
318
+ * const { pass, survivors } = hl7DeidLoop({ seed: 42, kind: "ORU^R01" });
319
+ * // pass === true, survivors.length === 0
320
+ * ```
321
+ */
322
+ declare function hl7DeidLoop(options?: Hl7DeidLoopOptions): DeidLoopResult;
323
+ /** Options accepted by {@link fhirDeidLoop}. */
324
+ interface FhirDeidLoopOptions {
325
+ /** The seed. Defaults to `0`. */
326
+ readonly seed?: number;
327
+ }
328
+ /**
329
+ * Run the pairing loop for **FHIR R4**: generate a spec-clean `Bundle` (US-Core `Patient` + a clinical
330
+ * spine) through `@cosyte/fhir`'s model, plant PHI sentinels at the `Patient` demographics, de-identify
331
+ * through `@cosyte/deid/fhir`, and verify removal + clinical retention. A `Bundle` (not a bare
332
+ * `Patient`) is used so there is clinical content to prove is **not** over-scrubbed.
333
+ *
334
+ * @param options - The seed. See {@link FhirDeidLoopOptions}.
335
+ * @returns The {@link DeidLoopResult} for the bundle.
336
+ * @example
337
+ * ```ts
338
+ * import { fhirDeidLoop } from "@cosyte/synth/deid";
339
+ * fhirDeidLoop({ seed: 7 }).pass; // => true
340
+ * ```
341
+ */
342
+ declare function fhirDeidLoop(options?: FhirDeidLoopOptions): DeidLoopResult;
343
+ /** The X12 transaction families the loop can pair. */
344
+ type X12DeidVariant = "837P" | "837I" | "837D" | "271" | "835";
345
+ /** Options accepted by {@link x12DeidLoop}. */
346
+ interface X12DeidLoopOptions {
347
+ /** The seed. Defaults to `0`. */
348
+ readonly seed?: number;
349
+ /** The transaction family. Defaults to `"837P"`. */
350
+ readonly variant?: X12DeidVariant;
351
+ }
352
+ /**
353
+ * Run the pairing loop for **X12 005010**: generate a spec-clean transaction through `@cosyte/x12`'s
354
+ * builders, plant PHI sentinels across the subscriber/patient loops, de-identify through
355
+ * `@cosyte/deid/x12`, and verify removal + clinical retention.
356
+ *
357
+ * @param options - The seed and transaction family. See {@link X12DeidLoopOptions}.
358
+ * @returns The {@link DeidLoopResult} for the transaction.
359
+ * @example
360
+ * ```ts
361
+ * import { x12DeidLoop } from "@cosyte/synth/deid";
362
+ * x12DeidLoop({ seed: 1, variant: "837P" }).survivors; // => []
363
+ * ```
364
+ */
365
+ declare function x12DeidLoop(options?: X12DeidLoopOptions): DeidLoopResult;
366
+ /** The NCPDP Telecom transactions the loop can pair. */
367
+ type NcpdpTelecomVariant = "B1" | "B2" | "B3";
368
+ /** Options accepted by {@link ncpdpTelecomDeidLoop}. */
369
+ interface NcpdpTelecomDeidLoopOptions {
370
+ /** The seed. Defaults to `0`. */
371
+ readonly seed?: number;
372
+ /** The Telecom transaction. Defaults to `"B1"` (a claim, the PHI- and clinically-densest). */
373
+ readonly transaction?: NcpdpTelecomVariant;
374
+ }
375
+ /**
376
+ * Run the pairing loop for **NCPDP Telecom** (pharmacy claims): generate a spec-clean transaction
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** —
379
+ * `@cosyte/deid` ships no SCRIPT adapter ({@link DEID_LOOP_SKIPPED}).
380
+ *
381
+ * @param options - The seed and transaction. See {@link NcpdpTelecomDeidLoopOptions}.
382
+ * @returns The {@link DeidLoopResult} for the transaction.
383
+ * @example
384
+ * ```ts
385
+ * import { ncpdpTelecomDeidLoop } from "@cosyte/synth/deid";
386
+ * ncpdpTelecomDeidLoop({ seed: 3, transaction: "B1" }).pass; // => true
387
+ * ```
388
+ */
389
+ declare function ncpdpTelecomDeidLoop(options?: NcpdpTelecomDeidLoopOptions): DeidLoopResult;
390
+ /** The C-CDA document types the loop can pair. */
391
+ type CcdaDeidDocumentType = "ccd" | "referral";
392
+ /** Options accepted by {@link ccdaDeidLoop}. */
393
+ interface CcdaDeidLoopOptions {
394
+ /** The seed. Defaults to `0`. */
395
+ readonly seed?: number;
396
+ /** The document type. Defaults to `"ccd"`. */
397
+ readonly documentType?: CcdaDeidDocumentType;
398
+ }
399
+ /**
400
+ * Run the pairing loop for **C-CDA R2.1**: generate a spec-clean document through `@cosyte/ccda`'s
401
+ * `buildCcda`, plant PHI sentinels at the `recordTarget` patient participation, de-identify through
402
+ * `@cosyte/deid/ccda`, and verify removal + clinical retention.
403
+ *
404
+ * Sentinels are read from the `<recordTarget>` element of the serialized XML (not `@cosyte/deid`'s DOM
405
+ * extractor) so that this zero-dependency subpath constructs no XML DOM; scoping to `recordTarget` keeps
406
+ * the sentinels patient-PHI only (`author`/`custodian` provider identity is legitimately retained).
407
+ *
408
+ * @param options - The seed and document type. See {@link CcdaDeidLoopOptions}.
409
+ * @returns The {@link DeidLoopResult} for the document.
410
+ * @example
411
+ * ```ts
412
+ * import { ccdaDeidLoop } from "@cosyte/synth/deid";
413
+ * ccdaDeidLoop({ seed: 5, documentType: "ccd" }).survivors; // => []
414
+ * ```
415
+ */
416
+ declare function ccdaDeidLoop(options?: CcdaDeidLoopOptions): DeidLoopResult;
417
+ /** A per-format row of a {@link DeidCoverageSummary}. */
418
+ interface DeidCoverageRow {
419
+ /** The covered format. */
420
+ readonly format: string;
421
+ /** The concrete artifacts run for this format. */
422
+ readonly artifacts: readonly string[];
423
+ /** Total distinctive PHI sentinels planted across the runs. */
424
+ readonly planted: number;
425
+ /** Total surviving sentinels — **0** on a clean pass. */
426
+ readonly survivors: number;
427
+ /** Total clinical tokens probed for over-scrub. */
428
+ readonly clinicalProbed: number;
429
+ /** Total clinical tokens over-scrubbed — **0** on a clean pass. */
430
+ readonly clinicalScrubbed: number;
431
+ /** `true` iff every run for this format passed. */
432
+ readonly pass: boolean;
433
+ }
434
+ /** The aggregate coverage summary across a set of loop runs. */
435
+ interface DeidCoverageSummary {
436
+ /** One row per format. */
437
+ readonly byFormat: readonly DeidCoverageRow[];
438
+ /** Total sentinels planted across every run. */
439
+ readonly totalPlanted: number;
440
+ /** Total surviving sentinels across every run — **0** on a clean pass. */
441
+ readonly totalSurvivors: number;
442
+ /** Total clinical over-scrubs across every run — **0** on a clean pass. */
443
+ readonly totalClinicalScrubbed: number;
444
+ /** `true` iff every run passed. */
445
+ readonly allPass: boolean;
446
+ /** The named, deliberately-skipped format paths (from {@link DEID_LOOP_SKIPPED}). */
447
+ readonly skipped: typeof DEID_LOOP_SKIPPED;
448
+ }
449
+ /**
450
+ * Summarize a set of loop runs into a per-format coverage report — the "coverage summary per format" the
451
+ * harness reports.
452
+ *
453
+ * @param results - The loop results to aggregate.
454
+ * @returns The {@link DeidCoverageSummary}.
455
+ * @example
456
+ * ```ts
457
+ * import { hl7DeidLoop, fhirDeidLoop, summarizeDeidCoverage } from "@cosyte/synth/deid";
458
+ * const summary = summarizeDeidCoverage([hl7DeidLoop(), fhirDeidLoop()]);
459
+ * summary.allPass; // => true
460
+ * ```
461
+ */
462
+ declare function summarizeDeidCoverage(results: readonly DeidLoopResult[]): DeidCoverageSummary;
463
+
464
+ export { type CcdaDeidDocumentType, type CcdaDeidLoopOptions, DEID_LOOP_COVERED_FORMATS, DEID_LOOP_POLICY_NAME, DEID_LOOP_SKIPPED, type DeidCoverageRow, type DeidCoverageSummary, type DeidLoopFormat, type DeidLoopResult, type DeidSentinel, type FhirDeidLoopOptions, type Hl7DeidLoopOptions, type NcpdpTelecomDeidLoopOptions, type NcpdpTelecomVariant, type X12DeidLoopOptions, type X12DeidVariant, assembleVerdict, ccdaDeidLoop, clinicalRetention, deidLoopPolicy, fhirDeidLoop, hl7DeidLoop, identifierSentinels, ncpdpTelecomDeidLoop, recordTargetSentinels, summarizeDeidCoverage, sweepSurvivors, x12DeidLoop };