@cosyte/synth 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 4008c31: The npm description lists the six formats with a serial comma.
8
+ - 4153d08: The README follows the house layout, with sections for support, security, and contributing, and the installation page names Node 26, which CI now tests.
9
+
10
+ ## 0.1.2
11
+
12
+ ### Patch Changes
13
+
14
+ - be69f2d: Generated C-CDA documents change: the parser peers are bounded to their 0.1 releases, and `@cosyte/ccda` 0.1 builds each CCD and Referral Note differently for the same seed.
15
+
16
+ Output change: every C-CDA document, for every seed, is now built by `@cosyte/ccda` 0.1, whose builder writes more of what C-CDA R2.1 asks for. Against the same seed on the previous release:
17
+ - the patient gains `raceCode` and `ethnicGroupCode` with `nullFlavor="UNK"`;
18
+ - the author gains a `representedOrganization`;
19
+ - a CCD gains a `documentationOf` service event, and a Referral Note an `informationRecipient`, each with unknown values;
20
+ - problem and allergy concern acts gain the `CONC` code;
21
+ - allergy reaction observations gain an `id`, which shifts the generated ids after them;
22
+ - the Medications section's templateIds move to the 2014-06-09 extension;
23
+ - the vital signs organizer is coded with LOINC 74728-7 instead of SNOMED CT 46680005;
24
+ - immunizations carry `negationInd="false"`;
25
+ - results organizers carry an `effectiveTime` interval with an unknown end;
26
+ - the Social History section moves earlier in the document.
27
+ A C-CDA golden file committed from an earlier release no longer matches and has to be regenerated. Every value stays synthetic. HL7 v2, FHIR R4 and US Core, X12, NCPDP, and ASTM documents are byte-identical for the seeds the determinism baseline records, and their committed golden files still match.
28
+
29
+ Peer ranges: `@cosyte/hl7`, `@cosyte/fhir`, `@cosyte/ccda`, `@cosyte/x12`, `@cosyte/ncpdp`, `@cosyte/astm` and `@cosyte/deid` move from `>=0.0.0` (or `>=0.0.1`) to `^0.1.0`, the releases this package is tested against. A project that pins one of them to a 0.0.x release has to move it to 0.1 to install this version.
30
+
3
31
  ## 0.1.1
4
32
 
5
33
  ### Patch Changes
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Cosyte
3
+ Copyright (c) 2026 Cosyte LLC
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,14 +1,27 @@
1
1
  <a href="https://cosyte.com">
2
2
  <picture>
3
3
  <source media="(prefers-color-scheme: dark)" srcset="https://cosyte.com/tile/cosyte-lockup-tile-on-dark-1200x300.png">
4
- <img alt="Cosyte: a plus mark set in two overlapping rounded squares, one solid and one outlined, beside the Cosyte wordmark" src="https://cosyte.com/tile/cosyte-lockup-tile-on-light-1200x300.png">
4
+ <img alt="The Cosyte logo on its own white ground: the icon beside the word Cosyte." src="https://cosyte.com/tile/cosyte-lockup-tile-on-light-1200x300.png">
5
5
  </picture>
6
6
  </a>
7
7
 
8
8
  # @cosyte/synth
9
9
 
10
- > Deterministic, seedable **synthetic healthcare-fixture generator** for Node.js and TypeScript:
11
- > built through each parser's own builder, and **never real PHI**.
10
+ > Seeded, reproducible **synthetic healthcare fixtures** for Node.js and TypeScript: spec-clean, never real PHI.
11
+
12
+ [![npm version](https://img.shields.io/npm/v/@cosyte/synth.svg)](https://www.npmjs.com/package/@cosyte/synth)
13
+ [![CI](https://img.shields.io/github/actions/workflow/status/cosyte/synth/ci.yml?branch=main&label=CI)](https://github.com/cosyte/synth/actions/workflows/ci.yml)
14
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/cosyte/synth/blob/main/LICENSE)
15
+ [![Node](https://img.shields.io/badge/node-%3E%3D22.0.0-brightgreen.svg)](https://nodejs.org)
16
+
17
+ Deterministic, seedable synthetic healthcare test fixtures for Node.js and TypeScript: HL7 v2, FHIR R4 and US Core, C-CDA, X12, NCPDP, and ASTM, spec-clean by construction and never real PHI.
18
+
19
+ ## Why this exists
20
+
21
+ A test suite for a healthcare integration needs messages and documents that look like what a
22
+ sending system produces, and it must never hold a real patient's data. Copying production samples
23
+ and scrubbing them by hand risks the second; writing fixtures by hand rarely gets the first. And a
24
+ fixture that changes on every run cannot be committed as a golden file.
12
25
 
13
26
  `@cosyte/synth` generates reproducible synthetic test corpora across the six Cosyte formats (HL7 v2,
14
27
  FHIR R4 / US Core, C-CDA, X12, NCPDP, and ASTM). It is a **consumer** of the
@@ -18,23 +31,25 @@ date, phone, and address from a **guaranteed-non-colliding synthetic source**. I
18
31
  **format/conformance generator, not a clinical simulator**: it does not model disease progression
19
32
  (that is Synthea).
20
33
 
21
- > **Status:** `0.1`, published to npm. The version shown on the npm package page is the one that is
22
- > live; this page never repeats it. Below 1.0, a breaking change raises the minor version and the
23
- > changelog says what broke. The generator is **feature-complete**: the
24
- > seeded-PRNG core, the synthetic-safety providers, and the round-trip harness; **generation across all
25
- > six formats**, HL7 v2 (`ADT`/`ORU`/`ORM`/`SIU`/`VXU`), FHIR R4 / US Core (the full clinical
26
- > set + `collection`/`transaction`/`document` Bundles), C-CDA R2.1 (CCD + Referral Note), X12 005010
27
- > (837P/I/D, 835, 271), NCPDP (SCRIPT NewRx / RxRenewal / RxChange + Telecom B1/B2/B3), and ASTM (E1394
28
- > record reports + E1381 framing), each built **through its parser's own builder/serializer**; **vendor-quirk
29
- > mode**, profile-driven off-spec fixtures for the three richest profile systems (**HL7 v2, C-CDA, ASTM**),
30
- > each round-tripping to exactly the intended parser warning (the intended-warning contract); and the
31
- > **`@cosyte/deid` pairing loop**, a closed-loop co-validation harness that de-identifies generated output
32
- > and proves every planted synthetic PHI sentinel is removed.
33
- >
34
- > **Deferred (honestly out of scope for now):** quirk recipes for **FHIR / X12 / NCPDP** (and any quirk
35
- > needing a private vendor corpus); the X12 **270** eligibility _request_ and NCPDP SCRIPT lifecycle
36
- > **responses** (both track the parsers' builder surfaces); **NCPDP SCRIPT / ASTM / DICOM** de-identify
37
- > pairing (no `@cosyte/deid` adapter, or not generated); and optional **Synthea** clinical-content ingestion.
34
+ ## Status
35
+
36
+ The 0.1 line, published to npm. The version shown on the npm package page is the one
37
+ that is live; this page never repeats it. Below 1.0, a breaking change raises the minor version and the
38
+ changelog says what broke. The generator is **feature-complete**: the
39
+ seeded-PRNG core, the synthetic-safety providers, and the round-trip harness; **generation across all
40
+ six formats**, HL7 v2 (`ADT`/`ORU`/`ORM`/`SIU`/`VXU`), FHIR R4 / US Core (the full clinical
41
+ set + `collection`/`transaction`/`document` Bundles), C-CDA R2.1 (CCD + Referral Note), X12 005010
42
+ (837P/I/D, 835, 271), NCPDP (SCRIPT NewRx / RxRenewal / RxChange + Telecom B1/B2/B3), and ASTM (E1394
43
+ record reports + E1381 framing), each built **through its parser's own builder/serializer**; **vendor-quirk
44
+ mode**, profile-driven off-spec fixtures for the three richest profile systems (**HL7 v2, C-CDA, ASTM**),
45
+ each round-tripping to exactly the intended parser warning (the intended-warning contract); and the
46
+ **`@cosyte/deid` pairing loop**, a closed-loop co-validation harness that de-identifies generated output
47
+ and proves every planted synthetic PHI sentinel is removed.
48
+
49
+ **Deferred (honestly out of scope for now):** quirk recipes for **FHIR / X12 / NCPDP** (and any quirk
50
+ needing a private vendor corpus); the X12 **270** eligibility _request_ and NCPDP SCRIPT lifecycle
51
+ **responses** (both track the parsers' builder surfaces); **NCPDP SCRIPT / ASTM / DICOM** de-identify
52
+ pairing (no `@cosyte/deid` adapter, or not generated); and optional **Synthea** clinical-content ingestion.
38
53
 
39
54
  ## Install
40
55
 
@@ -42,6 +57,8 @@ date, phone, and address from a **guaranteed-non-colliding synthetic source**. I
42
57
  npm install @cosyte/synth @cosyte/hl7 @cosyte/fhir @cosyte/ccda @cosyte/x12 @cosyte/ncpdp @cosyte/astm
43
58
  ```
44
59
 
60
+ Node.js 22 or later, tested on 22, 24, and 26.
61
+
45
62
  `@cosyte/hl7`, `@cosyte/fhir`, `@cosyte/ccda`, `@cosyte/x12`, `@cosyte/ncpdp`, `@cosyte/astm`, and
46
63
  `@cosyte/deid` are **optional peer dependencies**, each needed only for its subpath (`@cosyte/synth/hl7`,
47
64
  `@cosyte/synth/fhir`, `@cosyte/synth/ccda`, `@cosyte/synth/x12`, `@cosyte/synth/ncpdp`,
@@ -49,7 +66,12 @@ npm install @cosyte/synth @cosyte/hl7 @cosyte/fhir @cosyte/ccda @cosyte/x12 @cos
49
66
  the formats it pairs): install only the packages whose fixtures you generate. The package core has
50
67
  **zero third-party runtime dependencies**.
51
68
 
52
- ## Generate a spec-clean HL7 v2 message
69
+ ## Usage
70
+
71
+ Every value below is synthetic by construction. The full reference, with a guide per format, is
72
+ on [docs.cosyte.com/synth](https://docs.cosyte.com/synth).
73
+
74
+ ### Generate a spec-clean HL7 v2 message
53
75
 
54
76
  The HL7 v2 set covers `ADT` (`A01`/`A04`/`A08`), `ORU^R01`, `ORM^O01`, `SIU^S12`, and `VXU^V04`:
55
77
  each built through `@cosyte/hl7`'s `buildMessage`, so it is spec-clean by construction.
@@ -73,7 +95,7 @@ console.log(corpus.artifacts.every((a) => a.warnings.length === 0)); // true, al
73
95
  console.log(roundTrip(generateHl7("VXU^V04", 12345)).content.split("\r")[0]); // its MSH segment
74
96
  ```
75
97
 
76
- ## Generate a spec-clean FHIR R4 / US Core resource
98
+ ### Generate a spec-clean FHIR R4 / US Core resource
77
99
 
78
100
  The `@cosyte/synth/fhir` subpath builds resources **through `@cosyte/fhir`'s model constructors**, so
79
101
  they are spec-clean by construction: validating under `validateResource` and, against the **real US
@@ -107,7 +129,7 @@ corpus.artifacts.every((a) => a.warnings.length === 0); // true, all spec-clean
107
129
 
108
130
  `@cosyte/fhir` is an **optional peer dependency**, needed only for the `@cosyte/synth/fhir` subpath.
109
131
 
110
- ## Generate a spec-clean C-CDA document
132
+ ### Generate a spec-clean C-CDA document
111
133
 
112
134
  The `@cosyte/synth/ccda` subpath builds Consolidated CDA R2.1 documents **through `@cosyte/ccda`'s
113
135
  `buildCcda`**, so template IDs, LOINC section codes, and structured/narrative agreement are the
@@ -136,7 +158,7 @@ corpus.artifacts.every((a) => a.warnings.length === 0); // true, all spec-clean
136
158
 
137
159
  `@cosyte/ccda` is an **optional peer dependency**, needed only for the `@cosyte/synth/ccda` subpath.
138
160
 
139
- ## Generate an X12 transaction
161
+ ### Generate an X12 transaction
140
162
 
141
163
  The `@cosyte/synth/x12` subpath builds HIPAA **005010** transactions **through `@cosyte/x12`'s domain
142
164
  builders** (`build837P/I/D`, `build835`, `build271`), so the ISA/GS/ST…SE/GE/IEA envelope, the
@@ -164,10 +186,10 @@ corpus.artifacts.every((a) => a.warnings.length === 0); // true
164
186
 
165
187
  `@cosyte/x12` is an **optional peer dependency**, needed only for the `@cosyte/synth/x12` subpath.
166
188
 
167
- **Deferred:** the **270** eligibility _request_ (`@cosyte/x12` ships `build270` from 0.1.0, but `synth`
189
+ **Deferred:** the **270** eligibility _request_ (`@cosyte/x12` ships `build270` on its 0.1 line, but `synth`
168
190
  does not generate a 270 yet) and **vendor-quirk mode**.
169
191
 
170
- ## Generate an NCPDP message
192
+ ### Generate an NCPDP message
171
193
 
172
194
  The `@cosyte/synth/ncpdp` subpath builds both NCPDP standards **through `@cosyte/ncpdp`'s own emit
173
195
  surface**, so each message round-trips through the parser with **zero warnings**. It emits **SCRIPT**
@@ -201,7 +223,7 @@ corpus.artifacts.every((a) => a.warnings.length === 0); // true
201
223
  **Deferred:** vendor-quirk mode. SCRIPT coverage tracks the parser's builder surface: the
202
224
  renewal/change _responses_ land as `@cosyte/ncpdp` grows builders.
203
225
 
204
- ## Generate an ASTM message
226
+ ### Generate an ASTM message
205
227
 
206
228
  The `@cosyte/synth/astm` subpath builds ASTM laboratory messages **through `@cosyte/astm`'s own emit
207
229
  surface**: `buildAstmMessage` for the E1394 record layer, `composeAstmFrames` for the E1381 frame
@@ -234,7 +256,7 @@ corpus.artifacts.every((a) => a.warnings.length === 0); // true
234
256
 
235
257
  `@cosyte/astm` is an **optional peer dependency**, needed only for the `@cosyte/synth/astm` subpath.
236
258
 
237
- ## Generate a vendor-quirk fixture
259
+ ### Generate a vendor-quirk fixture
238
260
 
239
261
  Spec-clean fixtures test that a parser reads a _correct_ message; **quirk mode** tests that it tolerates
240
262
  the realistic vendor deviations real traffic carries, and surfaces exactly the right diagnostic. The
@@ -265,7 +287,7 @@ astmQuirkRoundTrip(generateAstmQuirk({ seed: 1, quirk: "unknown-escape" })).inte
265
287
  A quirk a format's profile system does not support fails closed with a stable `SYNTH_UNSUPPORTED_QUIRK`
266
288
  diagnostic, never a silently-wrong fixture, and so do the other selectors: a message kind, a
267
289
  document type, a corpus mix entry, an `837` variant, a Bundle type. Each is checked against its own set
268
- before anything is generated, so an unrecognised one is a fatal `SYNTH_UNSUPPORTED_KIND` rather than a
290
+ before anything is generated, so an unrecognized one is a fatal `SYNTH_UNSUPPORTED_KIND` rather than a
269
291
  mislabelled corpus. Every refusal carries a code and a fixed message from a frozen table, and quotes
270
292
  nothing you passed. Every quirk is grounded in a **publicly-documented** deviation or a parser's
271
293
  **public** profile, never a private vendor corpus.
@@ -273,7 +295,7 @@ nothing you passed. Every quirk is grounded in a **publicly-documented** deviati
273
295
  **Deferred:** quirk recipes for **FHIR, X12, and NCPDP**, and any quirk that would need a **private,
274
296
  vendor-attributed corpus** to ground.
275
297
 
276
- ## Co-validate with `@cosyte/deid` (the pairing loop)
298
+ ### Co-validate with `@cosyte/deid` (the pairing loop)
277
299
 
278
300
  The `@cosyte/synth/deid` subpath is a deterministic **closed-loop co-validation harness** for the
279
301
  `synth` ⇄ `deid` pair: it **generates** an artifact, **plants** distinctive synthetic PHI
@@ -313,7 +335,7 @@ identity a de-identifier legitimately retains never reads as a false survivor. `
313
335
  **optional peer dependency**, needed only for this subpath. **Skipped and named** (`DEID_LOOP_SKIPPED`):
314
336
  NCPDP **SCRIPT** and **ASTM** (no `@cosyte/deid` adapter) and **DICOM** (not generated by `synth`).
315
337
 
316
- ## Draw a synthetic value
338
+ ### Draw a synthetic value
317
339
 
318
340
  ```ts
319
341
  import { createRng, safe, isSyntheticSsn, isItinFormatted, isSyntheticNpi } from "@cosyte/synth";
@@ -335,11 +357,71 @@ isSyntheticNpi(safe.npi(rng)); // true, always a deliberately-invalid-Luhn NPI (
335
357
  - **Checked by the parser's own judgment**, built through the parser's conservative serializer and
336
358
  fed straight back in: each artifact re-parses with zero warnings.
337
359
  - **Deterministic**, a hand-rolled seeded PRNG (`sfc32`/`splitmix32`); `Math.random` is lint-banned.
338
- A seed, and only the seed, determines the output, byte-for-byte, anywhere.
360
+ A seed determines the output, byte for byte, on the same parser versions, and CI compares those
361
+ bytes across Node 22 and 24.
339
362
  - **Immutable**, generated artifacts and the `Corpus` result are deep-frozen.
340
363
  - **Zero third-party runtime dependencies**, the parser peers are first-party Cosyte packages,
341
- vendored for dev/test; dual ESM + CJS, validated with `attw`.
364
+ installed from npm for the tests; dual ESM + CJS, validated with `attw`.
365
+
366
+ ## Documentation
367
+
368
+ The full reference is on [docs.cosyte.com/synth](https://docs.cosyte.com/synth):
369
+
370
+ - [Installation](https://docs.cosyte.com/synth/installation) and the [quickstart](https://docs.cosyte.com/synth/quickstart).
371
+ - Core concepts: [the archetype](https://docs.cosyte.com/synth/concepts-archetype) and the [limitations](https://docs.cosyte.com/synth/limitations).
372
+ - Guides: [the overview](https://docs.cosyte.com/synth/guides-overview), [FHIR R4 and US Core](https://docs.cosyte.com/synth/guides-fhir),
373
+ [C-CDA](https://docs.cosyte.com/synth/guides-ccda), [X12](https://docs.cosyte.com/synth/guides-x12), [NCPDP](https://docs.cosyte.com/synth/guides-ncpdp),
374
+ [ASTM](https://docs.cosyte.com/synth/guides-astm), [vendor quirks](https://docs.cosyte.com/synth/guides-quirks), and
375
+ [the `@cosyte/deid` pairing loop](https://docs.cosyte.com/synth/guides-deid).
376
+ - [Troubleshooting](https://docs.cosyte.com/synth/troubleshooting).
377
+ - [`CHANGELOG.md`](https://github.com/cosyte/synth/blob/main/CHANGELOG.md): every release.
378
+
379
+ ## Support
380
+
381
+ Questions, bug reports, and requests for a format or a quirk go in
382
+ [GitHub issues](https://github.com/cosyte/synth/issues). Every fixture this package makes is
383
+ synthetic: never attach a real patient's data to an issue. For help with an integration test suite,
384
+ see our [integration services](https://cosyte.com/integration-services).
385
+
386
+ ## Security
387
+
388
+ Report a vulnerability privately, never in a public issue, and with no real patient data in it. Our
389
+ [security policy](https://github.com/cosyte/synth/security/policy) says how. A generated value that
390
+ is real, or realistic enough to pass for real, is a vulnerability: report it that way.
391
+
392
+ ## Contributing
393
+
394
+ **Where to ask:** open an issue at
395
+ [https://github.com/cosyte/synth/issues](https://github.com/cosyte/synth/issues). A report is most
396
+ useful with the seed, the generator call, and the parser warnings you got back.
397
+
398
+ **Unsolicited pull requests are accepted.** For anything that changes the bytes a seed produces, adds
399
+ a generator option, or moves the public API, open an issue first so the behavior can be agreed
400
+ before you write it: a seed that maps to different bytes breaks every golden file built on it.
401
+ Typo, link, and test fixes can go straight to a pull request.
402
+
403
+ **What a contribution has to clear**, every one of them a script this package declares:
404
+
405
+ | check | what it is |
406
+ | ----------------------------- | -------------------------------------------------------- |
407
+ | `pnpm typecheck` | `tsc --noEmit`, strict, over the source and the examples |
408
+ | `pnpm lint` | ESLint, at `--max-warnings=0` |
409
+ | `pnpm format:check` | Prettier |
410
+ | `pnpm phi-scan` | no patient-shaped value anywhere in a tracked file |
411
+ | `pnpm test` | Vitest, including the synthetic-safety suites |
412
+ | `pnpm test:coverage` | the coverage gate |
413
+ | `pnpm build` | the dual ESM and CJS bundle |
414
+ | `pnpm attw` | per-condition type resolution for consumers |
415
+ | `pnpm run determinism:digest` | the bytes each seed produces, against the baseline |
416
+ | `pnpm check:no-emdash` | the house punctuation rule |
417
+ | `pnpm check:no-internal-refs` | no internal bookkeeping on the public surface |
418
+
419
+ New behavior needs a test beside it, and a change a consumer would notice needs a changeset
420
+ (`pnpm changeset`), whose summary becomes the changelog entry. A change to the bytes a seed produces
421
+ says so in its changeset.
342
422
 
343
423
  ## License
344
424
 
345
- MIT © Cosyte
425
+ MIT, copyright Cosyte LLC. See [LICENSE](https://github.com/cosyte/synth/blob/main/LICENSE). The
426
+ standards this package names belong to their owners; see
427
+ [TRADEMARKS.md](https://github.com/cosyte/synth/blob/main/TRADEMARKS.md).
package/dist/index.cjs CHANGED
@@ -560,7 +560,7 @@ function resolveMix(allowed, requested, fallback) {
560
560
  }
561
561
 
562
562
  // src/index.ts
563
- var VERSION = "0.1.1";
563
+ var VERSION = "0.1.3";
564
564
 
565
565
  exports.DEA_REGISTRANT_TYPES = DEA_REGISTRANT_TYPES;
566
566
  exports.DOC_V6_PREFIX = DOC_V6_PREFIX;