@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 +28 -0
- package/LICENSE +1 -1
- package/README.md +116 -34
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +29 -24
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
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
|
|
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
|
-
>
|
|
11
|
-
|
|
10
|
+
> Seeded, reproducible **synthetic healthcare fixtures** for Node.js and TypeScript: spec-clean, never real PHI.
|
|
11
|
+
|
|
12
|
+
[](https://www.npmjs.com/package/@cosyte/synth)
|
|
13
|
+
[](https://github.com/cosyte/synth/actions/workflows/ci.yml)
|
|
14
|
+
[](https://github.com/cosyte/synth/blob/main/LICENSE)
|
|
15
|
+
[](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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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