@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,414 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+ Versions and publishing are managed with [Changesets](https://github.com/changesets/changesets);
8
+ this file is maintained by hand (Changesets handles the version bump and publish only).
9
+
10
+ ## [Unreleased]
11
+
12
+ The first pre-alpha release (`0.0.1`) will ship the initial public API surface. The package begins
13
+ its public history at `0.0.x`, per the cosyte version ladder (`0.0.x` until first alpha).
14
+
15
+ ### Added
16
+
17
+ - Project scaffold from the shared `@cosyte/*` parser template: the canonical toolchain (TypeScript
18
+ ES2023 + strict rigor via `@cosyte/tsconfig`, ESLint 10 + type-checked `typescript-eslint` via
19
+ `@cosyte/eslint-config`, Prettier via `@cosyte/prettier-config`, Vitest 4 + v8 coverage via
20
+ `@cosyte/vitest-config`, dual ESM + CJS build via `tsup` + `@cosyte/tsup-config`, `attw` publish
21
+ gate), thin callers of the reusable `cosyte/.github` CI/release workflows, Changesets on the
22
+ `0.0.x` ladder, and the property-based conformance harness from `@cosyte/test-utils`.
23
+ - **Phase 1 — the generator core (SYNTH-1).** The seeded-PRNG + synthetic-safety + round-trip
24
+ foundation every format plugs into, proven end-to-end on HL7 v2:
25
+ - **Seeded, deterministic PRNG** — a hand-rolled, zero-dep `sfc32` seeded by `splitmix32`
26
+ (`createRng`, `splitmix32`, `sfc32Next`). `Math.random` is **lint-banned** in `src/` (it is not
27
+ seedable). Same seed → byte-identical output, a tested invariant.
28
+ - **The synthetic-safety provider layer** (`safe` + `ssn`/`phone`/`email`/`ipv4`/`ipv6`/`uuid`/
29
+ `identifier`/`address`/`dateYmd`/`name`) — every value drawn from a guaranteed-non-colliding
30
+ source: SSA never-issued (`900–999`) + advertising (`987-65-432x`) SSNs, NANP `555-01xx` phones,
31
+ RFC 2606/6761 `example.*` domains, RFC 5737/3849 TEST-NET IPs, a synthetic assigning authority
32
+ (`COSYTE-SYNTH`) for MRNs, and a shipped clearly-fake name pool. Reserved-range predicates
33
+ (`isSyntheticSsn`/`isSyntheticPhone`/`isSyntheticEmail`/`isSyntheticIp`).
34
+ - **The `Corpus` abstraction** (`makeCorpus`) — a deep-frozen, self-describing seed + manifest.
35
+ - **`defineSynthProfile`** — the profile growth-loop skeleton.
36
+ - **Stable fatal codes** (`SYNTH_FATAL_CODES`: `SYNTH_UNSUPPORTED_FORMAT`, `SYNTH_UNSUPPORTED_QUIRK`)
37
+ - the typed `SynthError`.
38
+ - **HL7 v2 generation** at the `@cosyte/synth/hl7` subpath (`generateAdt`, `roundTrip`, `hl7Corpus`,
39
+ `componentsField`) — builds `ADT^A01/A04/A08` **through `@cosyte/hl7`'s `buildMessage`**, so output
40
+ is spec-clean by construction and round-trips with zero warnings.
41
+ - **Phase 2 — the rest of the HL7 v2 set (SYNTH-2).** Extends HL7 v2 generation from `ADT` alone to
42
+ the full Phase 2 family, all built **through `@cosyte/hl7`'s `buildMessage`** and all round-tripping
43
+ through the parser with **zero warnings**, seed-deterministic, and synthetic-safe:
44
+ - **New generators** at the `@cosyte/synth/hl7` subpath: `generateOru` (`ORU^R01`, an OBR/OBX result
45
+ group), `generateOrm` (`ORM^O01`, an ORC/OBR order), `generateSiu` (`SIU^S12`, an SCH schedule
46
+ activity), and `generateVxu` (`VXU^V04`, a PID + ORC/RXA/RXR immunization). Each emits exactly the
47
+ segments the parser's structure net requires for its trigger, so nothing warns.
48
+ - **`generateHl7(kind, seed)`** — a dispatch over every `Hl7MessageKind`
49
+ (`ADT^A01`/`A04`/`A08`, `ORU^R01`, `ORM^O01`, `SIU^S12`, `VXU^V04`).
50
+ - **`hl7Corpus` now generates a mixed corpus** across every family by default (`mix?` to choose,
51
+ `triggers?` kept for SYNTH-1 ADT-only back-compat).
52
+ - **Shared HL7 building blocks** (`mshScaffold`, `patientIdentity`, `pidSegment`, `seededTimestamp`)
53
+ so every family mints identity from the same synthetic-safety providers in the same draw order;
54
+ `ADT` now uses them (byte-identical output preserved).
55
+ - **A small, license-clean example-code pool** (`EXAMPLE_LAB_OBSERVATIONS`, `EXAMPLE_ORDER_SERVICES`,
56
+ `EXAMPLE_VACCINES` — public LOINC/CVX code facts) to fill coded fields. **No** terminology is
57
+ bundled; codes are illustrative structural fillers only.
58
+ - **Tier-1 fixtures** for the new families and the synthetic-safety + seed-determinism property tests
59
+ extended to sweep every family (PID loci provably synthetic; whole-message real-data sweep zero).
60
+ - **The round-trip harness + synthetic-safety CI gate** — property tests prove seed-determinism,
61
+ zero-warning round-trips, and that no generated value escapes a reserved/synthetic source; the
62
+ repo `phi-scan` gains HL7-PID structured detection (synthetic-SSN-range aware).
63
+ - **Vendored `@cosyte/hl7`** as an optional peer dep via the `mllp` pattern (`file:vendor/*.tgz`
64
+ devDependency) — third-party runtime deps stay at **zero**.
65
+ - **Phase 3 — FHIR R4 / US Core (SYNTH-3).** A new `@cosyte/synth/fhir` subpath generating the US Core
66
+ clinical spine **through `@cosyte/fhir`'s own model constructors and serializer**, so every resource
67
+ is spec-clean by construction — validating under `@cosyte/fhir.validateResource` and, against the
68
+ **real, published US Core 6.1.0 `StructureDefinition`s** (BYO — none bundled), conformant to US Core:
69
+ - **New generators** at the `@cosyte/synth/fhir` subpath: `generatePatient` (base + `profile:"us-core"`
70
+ with the race/ethnicity/birthsex must-support extensions), `generateCondition` (US Core
71
+ problem-list item), `generateObservationLab` (US Core Laboratory Result), `generateVitalSign` (US
72
+ Core Vital Signs), `generateMedicationRequest` (US Core, satisfying the `us-core-21` requester
73
+ invariant), and `generateBundle` (`collection` + `transaction`, wired by `urn:uuid:` references).
74
+ - **`fhirCorpus(seed, count?, mix?)`** — a reproducible mixed corpus across the spine; **`roundTrip`**
75
+ — the FHIR round-trip/validate harness (serialize → parse → validate(strict) → serialize), which
76
+ accepts caller-supplied (BYO) US Core / vendor profiles. For FHIR, **spec-clean means zero
77
+ `error`/`fatal` findings + byte-stable** (the harness exposes both `errors` and `warnings`);
78
+ advisory findings a valid resource may legally carry — `REFERENCE_UNRESOLVED` on a collection
79
+ Bundle's external reference, `MUST_SUPPORT_ABSENT`, `INVARIANT_UNCHECKED`, base `dom-6` — are **not**
80
+ spec-cleanliness violations, unlike the HL7-side "zero warnings" contract (FHIR warnings are not all
81
+ conformance failures).
82
+ - **Model-construction helpers** (`prop`/`str`/`dec`/`bool`/`coding`/`codeableConcept`/`reference`/
83
+ `narrative`/`meta`/`mrnIdentifier`/`fhirPatientIdentity`/`toFhirDate`) that build through
84
+ `@cosyte/fhir`; US Core canonical URLs + code-system identifiers (`US_CORE_PROFILE`, `SYSTEM`, the
85
+ race/ethnicity/birthsex extension URLs) as **facts only**; and a small **license-clean** FHIR
86
+ example-code pool (`EXAMPLE_LAB_OBSERVATIONS`, `EXAMPLE_VITAL_SIGNS`, `EXAMPLE_CONDITIONS`,
87
+ `EXAMPLE_MEDICATIONS`, `EXAMPLE_RACE_CATEGORIES`, `EXAMPLE_ETHNICITY_CATEGORIES` — public LOINC/
88
+ SNOMED/RxNorm/OMB code facts). **No** US Core IG or terminology content is bundled.
89
+ - **US Core conformance is validated firsthand** against the committed real US Core 6.1.0 profiles
90
+ (`test/us-core-profiles/`, BYO reference inputs) — every US-Core generator validates with **zero
91
+ errors** across a 200-seed sweep; plus FHIR seed-determinism, synthetic-safety, and golden-fixture
92
+ property/regression suites, and FHIR-aware structured detection (HumanName + phone `ContactPoint`)
93
+ in the repo `phi-scan`.
94
+ - **Vendored `@cosyte/fhir`** as an optional peer dep via the same `file:vendor/*.tgz` pattern —
95
+ third-party runtime deps stay at **zero**. Deferred to SYNTH-4: `Encounter`, `DiagnosticReport`,
96
+ `Immunization`, `AllergyIntolerance`, `Procedure`, the `document` Bundle shape, and quirk mode.
97
+ - **Phase 4 — the rest of the US Core clinical set (SYNTH-4).** Extends the `@cosyte/synth/fhir` subpath
98
+ from the SYNTH-3 clinical spine to the full US Core clinical set, each built **through `@cosyte/fhir`'s
99
+ own model constructors** and validated firsthand against the **real, published US Core 6.1.0
100
+ `StructureDefinition`s** (BYO — none bundled):
101
+ - **New generators:** `generateEncounter` (US Core Encounter), `generateDiagnosticReport` (US Core
102
+ Laboratory DiagnosticReport — carries the mandated `LAB` category slice and `effectiveDateTime` +
103
+ `issued` for the `us-core-8`/`us-core-9` invariants, with optional `result` wiring),
104
+ `generateImmunization` (US Core Immunization), `generateAllergyIntolerance` (US Core
105
+ AllergyIntolerance — `clinicalStatus` + `verificationStatus` emitted together for `ait-1`/`ait-2`),
106
+ and `generateProcedure` (US Core Procedure). Each takes a `subject`/`patient` reference and claims US
107
+ Core via `meta.profile` by default (`usCore:false` opts out).
108
+ - **The `document` Bundle shape:** `generateBundle({ type: "document" })` leads with the FHIR-mandated
109
+ `Composition` (`bdl-11`) plus a synthetic `Organization` author/custodian, and carries the required
110
+ `identifier` (`bdl-9`) and `timestamp` (`bdl-10`). `buildComposition` is exported. The shared Bundle
111
+ spine now assembles the **full clinical set** wired by `urn:uuid:` `fullUrl`s so **every reference
112
+ resolves in-bundle** (the `DiagnosticReport.result` points at the in-bundle lab `Observation`).
113
+ - **New license-clean example-code pools** (public code facts, no terminology bundled): `EXAMPLE_VACCINES`
114
+ (CVX), `EXAMPLE_ALLERGENS` + `EXAMPLE_ALLERGY_MANIFESTATIONS` (RxNorm/SNOMED), `EXAMPLE_PROCEDURES`
115
+ (SNOMED — never CPT), `EXAMPLE_DIAGNOSTIC_REPORTS` (LOINC panels), `EXAMPLE_ENCOUNTER_TYPES` (SNOMED),
116
+ `EXAMPLE_ENCOUNTER_CLASSES` (v3 ActCode); plus the matching `SYSTEM`/`US_CORE_PROFILE` identifiers.
117
+ - **`fhirCorpus`** now cycles the full clinical set by default; every new generator is covered by the
118
+ round-trip (zero-error, byte-stable), US-Core-conformance (zero-error over a 200-seed sweep),
119
+ seed-determinism, synthetic-safety, and golden-fixture suites. The five new US Core 6.1.0 profiles
120
+ (`us-core-encounter`, `us-core-diagnosticreport-lab`, `us-core-immunization`,
121
+ `us-core-allergyintolerance`, `us-core-procedure`) are committed under `test/us-core-profiles/`.
122
+ Deferred to SYNTH-5: C-CDA generation; quirk mode remains Phase 7.
123
+ - **Phase 4 / C-CDA — spec-clean C-CDA generation (SYNTH-5).** A new `@cosyte/synth/ccda` subpath
124
+ generating Consolidated CDA R2.1 documents **through `@cosyte/ccda`'s `buildCcda`**, so every document
125
+ is spec-clean by construction — it round-trips through `parseCcda` with **zero warnings**, is
126
+ seed-deterministic (byte-identical for a seed), and is synthetic by construction:
127
+ - **New generators:** `generateCcd` (Continuity of Care Document), `generateReferralNote` (the second
128
+ document type `buildCcda` supports, with its Reason-for-Referral + Assessment narrative sections),
129
+ and the generic `generateCcda({ documentType })`. Each populates the CCD SHALL sections (Problems,
130
+ Allergies, Medications, Results, Vital Signs) plus Immunizations, Procedures, and Social History
131
+ (Smoking Status).
132
+ - **`ccdaCorpus`** builds a reproducible mixed corpus (CCD + Referral Note by default); `roundTrip`
133
+ is the C-CDA round-trip-through-the-parser harness (serialize → parse → serialize, judged by the
134
+ parser). `ccdaPatientIdentity` mints the synthetic `recordTarget`.
135
+ - **Reuses the license-clean example-code pools** (the same public LOINC/RxNorm/SNOMED/CVX facts the
136
+ FHIR generators ship, adapted to `@cosyte/ccda`'s OID-coded `BuildCode`) plus a small Social-History
137
+ (SNOMED smoking status) and NCI-route pool. No terminology content is bundled.
138
+ - **Synthetic-by-construction:** the patient name is from the shipped fake-name pool, the MRN lives
139
+ under the synthetic assigning-authority OID (never a real facility namespace), and every date comes
140
+ from the seeded generator — `buildCcda`'s default `effectiveTime: new Date()` is always overridden
141
+ with a synthetic date so the reproducibility contract holds. Seed-determinism and synthetic-safety
142
+ property suites (250-seed sweeps) + golden fixtures added; the repo `phi-scan` gains C-CDA-aware
143
+ structured detection (recordTarget `name` + `telecom`).
144
+ - **`@cosyte/ccda` vendored as an optional peer dep** (`file:vendor/cosyte-ccda-0.0.1.tgz`), lazily
145
+ loaded per format — importing the package root never pulls it; third-party runtime deps stay at
146
+ zero. Deferred to SYNTH-6: X12 generation; quirk mode remains Phase 7.
147
+ - **Phase 5 / X12 — spec-clean HIPAA 005010 generation (SYNTH-6).** A new `@cosyte/synth/x12` subpath
148
+ generating X12 EDI transactions **through `@cosyte/x12`'s domain builders**, so every interchange is
149
+ spec-clean by construction — it round-trips through `@cosyte/x12` with **zero warnings**, is
150
+ byte-stable, seed-deterministic, and synthetic by construction:
151
+ - **New generators:** `generate837P` / `generate837I` / `generate837D` (Professional / Institutional /
152
+ Dental claims via `build837P/I/D`), `generate835` (Health Care Claim Payment/Advice via `build835`,
153
+ **balance-checked by construction** — line, claim, and remit balance identities are satisfied before
154
+ the builder is called), and `generate271` (Health Care Eligibility Benefit Response via `build271`).
155
+ A shared `generate837(variant, …)` selects the claim variant. The builder computes the HL spine and
156
+ the ISA/GS/ST…SE/GE/IEA envelope + control numbers, so `synth` never hand-writes a byte.
157
+ - **`x12Corpus`** builds a reproducible mixed corpus (one of each of 837P/I/D + 835 + 271 by default);
158
+ `roundTrip` is the X12 round-trip-through-the-parser harness (serialize → parse → serialize, judged
159
+ by the parser). `x12Person` / `x12Organization` / `x12ProviderPerson` / `x12Payer` /
160
+ `x12TradingPartners` / `x12EnvelopeTiming` mint the synthetic identity; `dec` / `money` are the
161
+ shared `X12Decimal` money helpers.
162
+ - **Synthetic-safety is the hardest-attacked invariant here** (an 837/271 is identity-dense). New
163
+ provider **`safe.npi`** emits a 10-digit NPI with a **deliberately-invalid Luhn check digit** — a real
164
+ NPI must satisfy the CMS `80840`-prefixed Luhn check, so a `synth` NPI can **never** be a NPPES-issued
165
+ provider (new `isSyntheticNpi` / `npiCheckDigit` / `luhnMod10` + `NPI_LUHN_PREFIX`). Provider tax ids
166
+ are SSA never-issued (900-range) SSNs at `REF*SY`; member ids are synthetic-assigning-authority
167
+ scoped; person names are from the shipped fake-name pool; DOBs/dates come from the seeded generator.
168
+ - The repo **`phi-scan` gains X12-aware structured detection** (NM1 person names + member ids + NPIs,
169
+ PER contact names + phones, `REF*SY` provider SSNs, and a hard refusal of `NM1*34` raw SSNs). A
170
+ Luhn-**valid** XX-qualified NPI is a hard hit — it could denote a real provider. Seed-determinism and
171
+ synthetic-safety property suites (120-seed sweeps) + committed `.edi` golden fixtures added.
172
+ - **`@cosyte/x12` vendored as an optional peer dep** (`file:vendor/cosyte-x12-0.0.1.tgz`), lazily loaded
173
+ per format — importing the package root never pulls it; third-party runtime deps stay at zero.
174
+ - **Deferred:** the **270** eligibility _request_ (`@cosyte/x12` ships `build271` but no `build270`, and
175
+ `synth` never hand-writes bytes around a missing builder — coverage tracks the builder); vendor-quirk
176
+ mode remains Phase 7 / SYNTH-7.
177
+ - **Phase 6 / NCPDP — spec-clean SCRIPT + Telecom generation (SYNTH-7).** A new `@cosyte/synth/ncpdp`
178
+ subpath generating both NCPDP standards **through `@cosyte/ncpdp`'s own emit surface**, so every
179
+ message is spec-clean by construction — it round-trips through the parser with **zero warnings**, is
180
+ byte-stable, seed-deterministic, and synthetic by construction:
181
+ - **New SCRIPT (XML ePrescribing) generators:** `generateNewRx` (via the validated `buildNewRx`
182
+ builder) and `generateRxRenewalRequest` / `generateRxChangeRequest` (built as `@cosyte/ncpdp`'s
183
+ **public typed `ScriptMessage` model** + `serializeScript` — the same typed-model→serializer path
184
+ the X12 arm uses, never a hand-written byte). Each round-trips through `parseScript` cleanly.
185
+ - **New Telecom (vD.0 pharmacy claim) generators:** `generateB1` (billing), `generateB2` (reversal),
186
+ and `generateB3` (rebill) via `buildTelecomRequest` + `serializeTelecom` — the fixed Transaction
187
+ Header, the FS/GS/RS framing, and every field id are the parser's own emit. A shared
188
+ `generateTelecom(code, …)` selects the transaction.
189
+ - **`ncpdpCorpus`** builds a reproducible mixed corpus (one of each of NewRx + RxRenewalRequest +
190
+ RxChangeRequest + B1 + B2 + B3 by default); `scriptRoundTrip` / `telecomRoundTrip` are the NCPDP
191
+ round-trip-through-the-parser harnesses. `ncpdpPatient` / `ncpdpPrescriber` / `ncpdpPharmacy` /
192
+ `ncpdpCardholder` / `ncpdpScriptRouting` mint the synthetic identity; a small license-clean
193
+ example-drug pool (`EXAMPLE_DRUGS`, invented `00000`-labeler NDCs) supplies drug content — **no
194
+ NCPDP-copyrighted text is bundled**.
195
+ - **Synthetic-safety carries a new identity locus X12 did not have — the prescriber DEA.** New
196
+ provider **`safe.dea`** emits a `XX`+7-digit DEA number with a **deliberately-invalid checksum** — a
197
+ real DEA number's 7th digit satisfies the published `(d1+d3+d5)+2·(d2+d4+d6)` checksum, so a `synth`
198
+ DEA can **never** be a validly-issued registration (new `isSyntheticDea` / `deaCheckDigit` +
199
+ `DEA_REGISTRANT_TYPES`). Prescriber NPIs remain invalid-Luhn; patient / cardholder / member ids are
200
+ synthetic-assigning-authority scoped (`MBR`-prefixed); names from the fake-name pool; DOBs and dates
201
+ (including SCRIPT `SentTime`) from the seeded generator (never wall-clock).
202
+ - The repo **`phi-scan` gains an NCPDP arm** — SCRIPT (`<FirstName>`/`<LastName>`/`<MiddleName>`
203
+ names, `<NPI>` Luhn, `<DEANumber>` checksum) and Telecom (field-id-keyed CA/CB/CC/CD names, CQ
204
+ phone, CY/C2 ids, DB prescriber NPI). A Luhn-**valid** NPI or a checksum-**valid** DEA is a hard hit
205
+ — it could denote a real provider. Seed-determinism + synthetic-safety property suites (120-seed
206
+ sweeps) and committed `.xml` (SCRIPT) + `.ncpdp` (Telecom) golden fixtures added.
207
+ - **`@cosyte/ncpdp` vendored as an optional peer dep** (`file:vendor/cosyte-ncpdp-0.0.1.tgz`), lazily
208
+ loaded per format — importing the package root never pulls it; third-party runtime deps stay at zero.
209
+ - **Deferred:** SCRIPT coverage tracks the parser's builder surface — the renewal/change **responses**
210
+ and other lifecycle transactions land as `@cosyte/ncpdp` grows its builders (`synth` never
211
+ hand-writes bytes around a missing builder). Vendor-quirk mode remains Phase 7.
212
+ - **Phase 6 / ASTM — spec-clean E1394 + E1381 generation (SYNTH-8).** A new `@cosyte/synth/astm` subpath
213
+ generating ASTM/CLSI-LIS laboratory messages **through `@cosyte/astm`'s own emit surface**, so every
214
+ message is spec-clean by construction — it round-trips through the parser with **zero warnings**, is
215
+ byte-stable, seed-deterministic, and synthetic by construction. This **completes the spec-clean
216
+ generation core across all six formats** (ASTM was gated on `@cosyte/astm`'s serializer/builder,
217
+ `ASTM-7`, now shipped):
218
+ - **New record generators (E1394):** `generateAstmResult` (a full `H`/`P`/`O`/`R`…/`C`/`L` result
219
+ report) and `generateAstmOrder` (`H`/`P`/`O`/`L`), built through `buildAstmMessage` — the `H|\^&`
220
+ delimiter declaration, record type letters, per-type sequence counters, and `L` terminator are the
221
+ parser's own conservative emit, never a hand-written byte. Each round-trips through
222
+ `parseAstmRecords` cleanly and re-serializes byte-identically.
223
+ - **New framing generator (E1381):** `generateAstmResultFramed` frames the same records via
224
+ `composeAstmFrames` into `<STX> FN text <ETB|ETX> CS <CR><LF>` — the **modulo-256 checksum and the
225
+ `0`–`7` frame number are computed by `@cosyte/astm`, never faked** — round-tripping through
226
+ `parseFramedAstm` with **zero frame and zero record warnings**.
227
+ - **`astmCorpus`** builds a reproducible mixed corpus (a result report + an order by default);
228
+ `astmRoundTrip` / `astmFramedRoundTrip` are the ASTM round-trip-through-the-parser harnesses.
229
+ `astmPatient` / `astmOrder` / `astmHeaderIdentity` mint the synthetic identity; a small license-clean
230
+ `EXAMPLE_ASTM_TESTS` pool (public LOINC codes + invented local analyzer codes) supplies test content
231
+ — **no terminology prose is bundled**.
232
+ - **Synthetic-safety at ASTM's PHI-dense `P` (patient) record:** the patient name comes from the
233
+ shipped fake-name pool, the birthdate is seeded (never wall-clock), and the **practice-assigned**
234
+ and **laboratory-assigned** patient IDs are minted **independently** under the synthetic assigning
235
+ authority (`PRA` / `LAB`-prefixed) so they stay **distinct** — exactly as `@cosyte/astm` keeps them
236
+ distinct on parse. The order accession is `ACC`-prefixed synthetic.
237
+ - The repo **`phi-scan` gains an ASTM arm** — the `P`-record name (field 6, `Last^First^Middle`) and
238
+ the practice/lab patient IDs (fields 3/4), tolerating an E1381 frame prefix so a framed fixture is
239
+ swept identically to a bare record stream. A name not declared synthetic, or a patient ID not
240
+ recognized as synthetic-AA-scoped, is a hard hit. Seed-determinism + synthetic-safety + spec-clean
241
+ round-trip property suites (150-seed sweeps, record and framed) and committed `.astm` (record) +
242
+ `.frame` (framed) golden fixtures added.
243
+ - **`@cosyte/astm` vendored as an optional peer dep** (`file:vendor/cosyte-astm-0.0.0.tgz`), lazily
244
+ loaded per format — importing the package root never pulls it; third-party runtime deps stay at zero.
245
+ - **Deferred:** vendor-quirk mode (lowercase ASTM checksums, framing dropped over TCP, and the other
246
+ tolerances `@cosyte/astm`'s profile system advertises, each round-tripping to exactly the intended
247
+ warning) remains Phase 7.
248
+ - **Phase 7 — vendor-quirk generation, the differentiator (SYNTH-9).** Profile-driven **off-spec**
249
+ fixtures whose vocabulary **is the parsers' own profile systems** — a quirk deviates message
250
+ _structure_ so it round-trips to **exactly one intended, stable parser warning** (the
251
+ **intended-warning contract**), and where a **public** built-in parser profile claims the deviation it
252
+ round-trips cleanly under it (suppressed, or re-badged to `PROFILE_QUIRK_APPLIED`). Shipped for the
253
+ three richest profile systems — **HL7 v2, C-CDA, ASTM**:
254
+ - **Format-agnostic quirk core** (`src/quirk.ts`, root subpath): `QuirkDescriptor` / `QuirkArtifact` /
255
+ `QuirkRoundTripResult` / `QuirkProfiledVerdict` / `QuirkProfileDisposition`, `resolveQuirk` (fail-closed
256
+ `SYNTH_UNSUPPORTED_QUIRK`), `sameCodeSet`, `profileTolerated`, `validateProfileQuirks`,
257
+ `assertIntendedWarnings`, and the `PROFILE_QUIRK_APPLIED` marker. Quirks are applied **post-serialize**
258
+ (roadmap §10 Q4: profile tolerance is parse-side) as a deterministic transform of the parser's own
259
+ emit, and every `generate*Quirk` **self-checks the intended-warning contract at generation time**
260
+ (`assertIntendedWarnings`) — a fixture whose bare parse does not produce exactly the declared code is a
261
+ fatal error, never a silently-mislabeled golden file.
262
+ - **HL7 v2** (`@cosyte/synth/hl7`): `generateHl7Quirk` / `hl7QuirkRoundTrip` / `hl7QuirkCorpus` /
263
+ `hl7QuirkProfile` / `HL7_QUIRKS`. `unknown-zsegment` → `UNKNOWN_SEGMENT` (suppressed by the public
264
+ `visage` PACS profile's `ZDS` claim); `unknown-escape` → `UNKNOWN_ESCAPE_SEQUENCE`.
265
+ - **C-CDA** (`@cosyte/synth/ccda`): `generateCcdaQuirk` / `injectCcdaQuirk` / `ccdaQuirkRoundTrip` /
266
+ `ccdaQuirkCorpus` / `ccdaQuirkProfile` / `CCDA_QUIRKS`. `template-extension-absent` →
267
+ `TEMPLATE_EXTENSION_ABSENT` (re-badged by the public `legacyR11` profile); `deprecated-loinc` →
268
+ `DEPRECATED_LOINC` and `deprecated-code-system` → `DEPRECATED_CODE_SYSTEM` (re-badged by the public
269
+ `smartScorecard` profile). Structural (seed-robust) XML anchors that hold for **every generable
270
+ document type** (CCD **and** Referral Note — the `template-extension-absent` quirk drops the R2.1
271
+ stamp on each document-type template root).
272
+ - **ASTM** (`@cosyte/synth/astm`): `generateAstmQuirk` / `astmQuirkRoundTrip` / `astmQuirkCorpus` /
273
+ `astmQuirkProfile` / `ASTM_QUIRKS`. `unknown-escape` → `ASTM_UNKNOWN_ESCAPE_SEQUENCE` (re-badged by the
274
+ public `referenceCorpus` OSS profile); `unknown-record-type` → `ASTM_RECORD_UNKNOWN_TYPE`.
275
+ - **Synthetic-safety holds in quirk mode** — a quirk changes shape, never provenance, so the `phi-scan`
276
+ gate stays zero over quirk output (proven by scanner tests + committed quirk fixtures under
277
+ `test/fixtures/{hl7,ccda,astm}/quirk/`). Mandatory property suites: intended-warning (every quirk ×
278
+ seed × kind → exactly the intended code), seed-determinism (byte-identical), synthetic-safety.
279
+ - **Grounding:** every quirk is **publicly grounded** (ADR 0018 — a published IG, a vendor interface
280
+ spec, or a redistributable OSS corpus), never a private vendor corpus. **Deferred:** quirk recipes for
281
+ **FHIR / X12 / NCPDP**, and any quirk that would need a private, vendor-attributed corpus
282
+ (`REAL-CORPUS`-gated). New `@cosyte/synth/ccda` export `injectCcdaQuirk`; a new **quirk guide** in
283
+ `docs-content/`.
284
+ - **Phase 8 — the `@cosyte/deid` pairing loop (SYNTH-10).** A new `@cosyte/synth/deid` subpath — a
285
+ deterministic, seeded **closed-loop co-validation harness** for the `synth` ⇄ `deid` pair: **generate**
286
+ a spec-clean synthetic artifact → **enumerate** the distinctive synthetic PHI sentinels `synth` planted
287
+ at its patient loci → **de-identify** through `@cosyte/deid` → **verify** every sentinel is gone from
288
+ the de-identified output (a surviving sentinel is a hard failure) **and** that the clinical payload
289
+ survives (the over-scrub guard). It consumes the shipped generators unchanged — a harness capability,
290
+ not a generator change.
291
+ - **Per-format loops:** `hl7DeidLoop` (ADT/ORU/ORM/SIU/VXU), `fhirDeidLoop` (a US-Core `Bundle`, so
292
+ there is clinical content to prove is not over-scrubbed), `x12DeidLoop` (837P/837I/837D/271/835),
293
+ `ncpdpTelecomDeidLoop` (B1/B2/B3), and `ccdaDeidLoop` (CCD + Referral Note). Each returns an immutable
294
+ `DeidLoopResult` (`planted` / `survivors` / `clinicalProbed` / `clinicalScrubbed` / `pass`).
295
+ - **Removal check, locus-scoped and collision-proof.** Sentinels are enumerated from the patient PHI
296
+ loci — via `@cosyte/deid`'s own extractors for HL7/FHIR/X12/NCPDP, and a `<recordTarget>`-scoped scan
297
+ for C-CDA — then decomposed to **literal, distinctive** synthetic tokens (`identifierSentinels`,
298
+ `recordTargetSentinels`). Removal is verified by sweeping only the **de-identified values that remain
299
+ at those former PHI loci** (`sweepSurvivors`), re-read from the de-identifier's own output — so
300
+ provider/organization identity a de-identifier legitimately retains (drawn from the same synthetic
301
+ pools) never reads as a false survivor.
302
+ - **Removal-oriented policy** (`deidLoopPolicy`, `DEID_LOOP_POLICY_NAME`): HIPAA Safe Harbor with the
303
+ keyed-by-default identifier categories (MRN / beneficiary / account) switched to `redact`, so the loop
304
+ needs **no key context** and is a pure function of the seed.
305
+ - **Over-scrub guard** (`clinicalRetention`): distinctive (≥ 4-char) structured clinical codes present
306
+ before de-identification must survive after — short codes are not probed (they collide inside removed
307
+ PHI). Plus a `summarizeDeidCoverage` per-format coverage report.
308
+ - **Honest scope (roadmap §Phase 8, §7):** this is a **co-validation harness**, not an independent audit
309
+ of `@cosyte/deid` against real-world data — it proves the pair works on `synth`'s own output; a
310
+ sentinel `deid` **blocks** rather than redacts still passes (blocked = gone). Covers the five formats
311
+ both packages support; **NCPDP SCRIPT** and **ASTM** (no `@cosyte/deid` adapter) and **DICOM** (not
312
+ generated by `synth`) are **skipped and named** (`DEID_LOOP_SKIPPED`), never silently.
313
+ - `@cosyte/deid` is an **optional peer dependency**, vendored for dev/test via the `mllp`/`ncpdp`
314
+ pattern (`vendor/cosyte-deid-0.0.0.tgz`); **third-party runtime deps stay at 0**. Property suites:
315
+ seed-determinism (byte-identical artifact + de-identified output + sentinels), removal (0 survivors
316
+ across every format × seed), over-scrub (0 clinical loss), non-vacuity (the loop genuinely fails when
317
+ a sentinel survives — proven by tampering), and synthetic-safety of every planted sentinel.
318
+ **Deferred:** NCPDP SCRIPT / ASTM / DICOM pairing (blocked on `@cosyte/deid` adapters), optional
319
+ Synthea clinical-content ingestion (roadmap §Phase 8 — documented future concern), and SYNTH-11
320
+ release hardening.
321
+ - **Phase 9 — release hardening (SYNTH-11), the final roadmap phase.** No new runtime API; this phase
322
+ is the property/fuzz suite, coverage, publish dry-run, and honesty docs that make the package
323
+ release-shaped. The generator is feature-complete across all six formats.
324
+ - **Consolidated conformance property suite** (`test/property/all-formats.property.test.ts`) — every
325
+ one of the six spec-clean format generators is driven through the **same three mandatory
326
+ properties** (round-trip spec-clean · seed-determinism · synthetic-safety) so no format can silently
327
+ ship without one; plus an **intended-warning** arm proving each quirk corpus (HL7 v2 / C-CDA / ASTM)
328
+ is non-vacuous and stays synthetic-safe. Non-vacuity is asserted directly (the registry is proved to
329
+ cover every `SynthFormat`; every corpus is proved non-empty with non-trivial content).
330
+ - **Seed-sweep generation fuzz** (`test/property/seed-sweep.fuzz.property.test.ts`, the inverted fuzz
331
+ of roadmap §6) — sweeps seed × count × format across the six spec-clean corpora and the three quirk
332
+ corpora, asserting generation is **total**: it never throws outside the sanctioned `SYNTH_FATAL_CODES`
333
+ set, never hangs, and every output still passes the round-trip + synthetic-safety gates. Scales via
334
+ `SYNTH_FUZZ_RUNS`; new `test:fuzz` script + a nightly `Fuzz` workflow (`.github/workflows/fuzz.yml`).
335
+ - **Dual ESM/CJS release-shape smoke** (`scripts/smoke.mjs`, new `smoke` script, run by `verify.sh`) —
336
+ for **every published subpath** (`.`/`hl7`/`fhir`/`ccda`/`x12`/`ncpdp`/`astm`/`deid`) it imports the
337
+ ESM entry and requires the CJS entry from `dist/`, generates synthetic output through each, and
338
+ asserts ESM/CJS agree byte-for-byte for the same seed — catching a broken dual build a source-only
339
+ suite would not.
340
+ - **Publish dry-run proven:** `attw` green (per-condition types across all eight subpaths) and an
341
+ `npm publish --dry-run` clean 58-file tarball carrying every subpath's `.d.ts`/`.mjs`/`.cjs` plus
342
+ `README`/`LICENSE`/`CHANGELOG`. Per-dir **≥90 coverage** continues to gate.
343
+ - **Honesty docs** — `docs-content/limitations.md` (registered in the sidebar and gated by the
344
+ doc/code-agreement runner) leads with the governing sentence (_format/conformance generator, not a
345
+ clinical simulator; synthetic-by-construction; deterministic per seed within a version window; no
346
+ bundled terminology; no DICOM in v1_) and states the full **synthetic-safety posture** (the
347
+ 900-range SSN, invalid-Luhn NPI, invalid-checksum DEA, `555-01xx` phone, `example.*` domain,
348
+ TEST-NET IP, and synthetic-assigning-authority MRN floors), the structural-not-clinical / not-Synthea
349
+ scoping, and the deferred surfaces (FHIR/X12/NCPDP quirks, NCPDP SCRIPT responses, X12 270 request,
350
+ DICOM, Synthea ingestion). **Founder-gated tail (not crossed):** the actual `npm publish` and the
351
+ repo public-flip remain the two standing human stops.
352
+ - `VERSION` export.
353
+ - **Public-surface gate: `scripts/check-no-internal-refs.sh` + `pnpm check:no-internal-refs` + the
354
+ `no-internal-refs` workflow.** Ported from `hl7`'s reference implementation via `ncpdp`'s copy (which
355
+ added the fourth pass), keeping the cross-repo parts verbatim: the prefix list, the paragraph-join
356
+ second pass, the doc-comment third pass, the string-literal fourth pass, the silent-green route
357
+ closures and the NEGATIVE self-tests. Four passes: the public markdown + npm metadata, the same rules
358
+ over paragraph-joined text (so a violation straddling a line wrap cannot hide), `src/` doc comments,
359
+ and `src/` string literals. It self-tests both directions on every run and refuses to print OK from a
360
+ scan that did not read all of its input.
361
+ Two deliberate divergences from the sibling copies, both recorded in the script header:
362
+ **`SYNTH` is in the prefix list here and absent from `ncpdp`'s** (it is this repo's own item prefix,
363
+ and was the whole of the identifier backlog), with the collision it creates handled by an explicit
364
+ `SYNTHETIC_FIXTURE_TOKEN` exclusion for the three provenance markers this package stamps into
365
+ generated output (`SYNTH-FAC`, `SYNTH-LIS`, `SYNTH-ANALYZER`) rather than by dropping the prefix; and
366
+ **rule 5 gained a `roadmap §` arm**, the same widening `ncpdp` made to rule 3 for ADR paths, because
367
+ this repo cites its roadmap by section and 169 such citations were structurally invisible without it.
368
+ Both widenings carry their own standalone self-test so a later "resync with a sibling copy" reds
369
+ instead of silently reopening the hole. `CHANGELOG.md` is excluded on purpose, as in every sibling
370
+ copy: it ships inside the npm tarball, yet the same convention names it as a place identifiers
371
+ belong. That contradiction is ecosystem-wide and is recorded, not decided here.
372
+
373
+ ### Changed
374
+
375
+ - Replaced the parser-archetype scaffold stubs (`parseSynth`, `WARNING_CODES`, `FATAL_CODES`) with the
376
+ generator surface — `@cosyte/synth` is a synthetic-fixture **generator**, not a parser.
377
+ - **Docs:** refreshed the `README.md` status block to describe the **feature-complete** generator surface
378
+ (all six spec-clean formats + quirk mode for HL7 v2/C-CDA/ASTM + the `@cosyte/deid` pairing loop) with
379
+ its honest deferrals, replacing the stale forward-looking Phase 1–7 roadmap narrative. Status remains
380
+ pre-alpha (`0.0.x`), not yet published to npm.
381
+ - **PUBLIC-SURFACE-HYGIENE (founder directive, 2026-07-27): no internal project bookkeeping on a public
382
+ surface.** Swept every surface a consumer reads — `README.md`, `docs-content/`, the npm `description`
383
+ and `keywords`, the `src/` JSDoc that compiles into `dist/*.d.ts` and renders on hover, and the `src/`
384
+ string literals this package emits into what it generates. Measured on the base commit `cdfcdd9` by
385
+ running the gate exactly as it ships, with its refusal suppressed so every pass reports rather than
386
+ stopping at the first: **21 rows over 19 distinct locations on the public markdown and npm metadata**;
387
+ **416 rows over 227 distinct lines in `src/` doc comments** (288 found line by line, 128 more found
388
+ only by the paragraph-reflow pass); **1 in `src/` string literals**, with a second found by hand that
389
+ no rule here can see; and **562 rows over 442 distinct lines in the built declaration files**, 281
390
+ rows per module condition. All
391
+ are now zero. The backlog recorded "11+6"; a count is a function of the rule set, so these were taken
392
+ with the final one and are quoted with the tree they were taken on. Item identifiers (`SYNTH-4`),
393
+ `Phase N` language, ADR numbers, meta-repo paths and `roadmap §N` citations are gone from those
394
+ surfaces; they remain where the convention puts them — the changeset, this file, the commit, the PR
395
+ and the roadmap.
396
+ - **Stale deferral claims deleted rather than reworded.** `src/ccda/index.ts` and `src/astm/index.ts`
397
+ each documented quirk generation as still to come, lines above the export that ships it;
398
+ `src/ncpdp/index.ts` and `docs-content/guides-ncpdp.md` said ASTM generation was not yet shipped when
399
+ the ASTM subpath ships. Stripping the phase number would have left
400
+ a false claim standing in cleaner clothes, so the sentences were cut.
401
+ - **`DEID_LOOP_SKIPPED[].reason` text changed for the `ncpdp-script` and `dicom` entries.** The reasons
402
+ are unchanged in substance; the internal citation trailing each was removed. These strings are part of
403
+ a frozen exported constant, so a consumer asserting them verbatim is affected. The `format` values and
404
+ the shape of the constant are unchanged, and no generated byte changes.
405
+
406
+ ### Deprecated
407
+
408
+ ### Removed
409
+
410
+ ### Fixed
411
+
412
+ ### Security
413
+
414
+ [Unreleased]: https://github.com/cosyte/synth/commits/main
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cosyte
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.