@cosyte/synth 0.1.0 → 0.1.2
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 +31 -2
- package/LICENSE +1 -1
- package/README.md +26 -25
- package/dist/astm/index.cjs.map +1 -1
- package/dist/astm/index.d.cts +6 -6
- package/dist/astm/index.d.ts +6 -6
- package/dist/astm/index.mjs.map +1 -1
- package/dist/ccda/index.cjs.map +1 -1
- package/dist/ccda/index.d.cts +2 -2
- package/dist/ccda/index.d.ts +2 -2
- package/dist/ccda/index.mjs.map +1 -1
- package/dist/deid/index.cjs.map +1 -1
- package/dist/deid/index.d.cts +3 -3
- package/dist/deid/index.d.ts +3 -3
- package/dist/deid/index.mjs.map +1 -1
- package/dist/fhir/index.cjs.map +1 -1
- package/dist/fhir/index.d.cts +1 -1
- package/dist/fhir/index.d.ts +1 -1
- package/dist/fhir/index.mjs.map +1 -1
- package/dist/hl7/index.cjs.map +1 -1
- package/dist/hl7/index.d.cts +3 -3
- package/dist/hl7/index.d.ts +3 -3
- package/dist/hl7/index.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/ncpdp/index.cjs.map +1 -1
- package/dist/ncpdp/index.d.cts +2 -2
- package/dist/ncpdp/index.d.ts +2 -2
- package/dist/ncpdp/index.mjs.map +1 -1
- package/dist/{providers-BQtPk3PN.d.cts → providers-DcPrqmp5.d.cts} +2 -2
- package/dist/{providers-BQtPk3PN.d.ts → providers-DcPrqmp5.d.ts} +2 -2
- package/dist/{quirk-C_lZrspq.d.cts → quirk-CilFUhwX.d.cts} +1 -1
- package/dist/{quirk-Bzx9g8KG.d.ts → quirk-DmqYw3Vk.d.ts} +1 -1
- package/dist/x12/index.cjs.map +1 -1
- package/dist/x12/index.d.cts +3 -3
- package/dist/x12/index.d.ts +3 -3
- package/dist/x12/index.mjs.map +1 -1
- package/package.json +41 -51
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
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:
|
|
10
|
+
- the patient gains `raceCode` and `ethnicGroupCode` with `nullFlavor="UNK"`;
|
|
11
|
+
- the author gains a `representedOrganization`;
|
|
12
|
+
- a CCD gains a `documentationOf` service event, and a Referral Note an `informationRecipient`, each with unknown values;
|
|
13
|
+
- problem and allergy concern acts gain the `CONC` code;
|
|
14
|
+
- allergy reaction observations gain an `id`, which shifts the generated ids after them;
|
|
15
|
+
- the Medications section's templateIds move to the 2014-06-09 extension;
|
|
16
|
+
- the vital signs organizer is coded with LOINC 74728-7 instead of SNOMED CT 46680005;
|
|
17
|
+
- immunizations carry `negationInd="false"`;
|
|
18
|
+
- results organizers carry an `effectiveTime` interval with an unknown end;
|
|
19
|
+
- the Social History section moves earlier in the document.
|
|
20
|
+
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.
|
|
21
|
+
|
|
22
|
+
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.
|
|
23
|
+
|
|
24
|
+
## 0.1.1
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- 5dcf91e: Doc comments that ship in the type declarations now write a range with "to" instead of an en dash, for example "the `0` to `7` frame number" and "a seeded 1 to 4".
|
|
29
|
+
|
|
30
|
+
No generated value, export, warning code or seed mapping changes. The README and the documentation pages get the same edit, and the repository's dash check now refuses the en dash, literal or encoded, as it already refused the em dash.
|
|
31
|
+
|
|
3
32
|
## 0.1.0
|
|
4
33
|
|
|
5
34
|
### Minor Changes
|
|
@@ -164,7 +193,7 @@
|
|
|
164
193
|
to write it in the changeset. No runtime code, no public API, no warning code, no generated byte and
|
|
165
194
|
no synthetic-safety behaviour changed.
|
|
166
195
|
|
|
167
|
-
- da7cbdb: The package description, the README, the documentation pages and the exported quirk-registry prose no longer use the em dash, matching the
|
|
196
|
+
- da7cbdb: The package description, the README, the documentation pages and the exported quirk-registry prose no longer use the em dash, matching the Cosyte house voice.
|
|
168
197
|
|
|
169
198
|
**Nothing about generation changed.** No export was added, removed or renamed, no warning or fatal
|
|
170
199
|
code moved, and a given seed still produces byte-identical output for every format. The rewrite is
|
|
@@ -681,7 +710,7 @@ changesets that release consumed. This package is on the pre-alpha `0.0.x` ladde
|
|
|
681
710
|
used to quote were quirk names, format labels, code-system URIs and money strings. What was wrong
|
|
682
711
|
was the _shape_: every fatal took a value parameter, and the only thing keeping PHI out of a
|
|
683
712
|
diagnostic was that callers happened to be passing harmless values. The audit of the thirteen
|
|
684
|
-
|
|
713
|
+
Cosyte repos found that the single property separating the packages that leak from the ones that
|
|
685
714
|
are genuinely prevented is exactly this: whether the message factory takes a value parameter at
|
|
686
715
|
all. It now does not.
|
|
687
716
|
|
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,28 +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
|
-
> spec-clean by construction, and **never real PHI**.
|
|
10
|
+
> Seeded, reproducible **synthetic healthcare fixtures** for Node.js and TypeScript: spec-clean, never real PHI.
|
|
12
11
|
|
|
13
|
-
`@cosyte/synth` generates reproducible synthetic test corpora across the six
|
|
12
|
+
`@cosyte/synth` generates reproducible synthetic test corpora across the six Cosyte formats (HL7 v2,
|
|
14
13
|
FHIR R4 / US Core, C-CDA, X12, NCPDP, and ASTM). It is a **consumer** of the
|
|
15
|
-
|
|
16
|
-
(so
|
|
14
|
+
Cosyte parsers, not a parser: it builds each artifact **through the parser's own builder/serializer**
|
|
15
|
+
(so each artifact re-parses through that parser with zero warnings) and draws every identifier, name,
|
|
17
16
|
date, phone, and address from a **guaranteed-non-colliding synthetic source**. It is a
|
|
18
17
|
**format/conformance generator, not a clinical simulator**: it does not model disease progression
|
|
19
18
|
(that is Synthea).
|
|
20
19
|
|
|
21
|
-
> **Status:**
|
|
22
|
-
> live; this page never repeats it. Below 1.0, a breaking change raises the minor version and the
|
|
20
|
+
> **Status:** the 0.1 line, published to npm. The version shown on the npm package page is the one
|
|
21
|
+
> that is live; this page never repeats it. Below 1.0, a breaking change raises the minor version and the
|
|
23
22
|
> changelog says what broke. The generator is **feature-complete**: the
|
|
24
|
-
> seeded-PRNG core, the synthetic-safety providers, and the round-trip harness; **
|
|
25
|
-
>
|
|
23
|
+
> seeded-PRNG core, the synthetic-safety providers, and the round-trip harness; **generation across all
|
|
24
|
+
> six formats**, HL7 v2 (`ADT`/`ORU`/`ORM`/`SIU`/`VXU`), FHIR R4 / US Core (the full clinical
|
|
26
25
|
> set + `collection`/`transaction`/`document` Bundles), C-CDA R2.1 (CCD + Referral Note), X12 005010
|
|
27
26
|
> (837P/I/D, 835, 271), NCPDP (SCRIPT NewRx / RxRenewal / RxChange + Telecom B1/B2/B3), and ASTM (E1394
|
|
28
27
|
> record reports + E1381 framing), each built **through its parser's own builder/serializer**; **vendor-quirk
|
|
@@ -42,6 +41,8 @@ date, phone, and address from a **guaranteed-non-colliding synthetic source**. I
|
|
|
42
41
|
npm install @cosyte/synth @cosyte/hl7 @cosyte/fhir @cosyte/ccda @cosyte/x12 @cosyte/ncpdp @cosyte/astm
|
|
43
42
|
```
|
|
44
43
|
|
|
44
|
+
Node.js 22 or later, tested on 22 and 24.
|
|
45
|
+
|
|
45
46
|
`@cosyte/hl7`, `@cosyte/fhir`, `@cosyte/ccda`, `@cosyte/x12`, `@cosyte/ncpdp`, `@cosyte/astm`, and
|
|
46
47
|
`@cosyte/deid` are **optional peer dependencies**, each needed only for its subpath (`@cosyte/synth/hl7`,
|
|
47
48
|
`@cosyte/synth/fhir`, `@cosyte/synth/ccda`, `@cosyte/synth/x12`, `@cosyte/synth/ncpdp`,
|
|
@@ -136,7 +137,7 @@ corpus.artifacts.every((a) => a.warnings.length === 0); // true, all spec-clean
|
|
|
136
137
|
|
|
137
138
|
`@cosyte/ccda` is an **optional peer dependency**, needed only for the `@cosyte/synth/ccda` subpath.
|
|
138
139
|
|
|
139
|
-
## Generate
|
|
140
|
+
## Generate an X12 transaction
|
|
140
141
|
|
|
141
142
|
The `@cosyte/synth/x12` subpath builds HIPAA **005010** transactions **through `@cosyte/x12`'s domain
|
|
142
143
|
builders** (`build837P/I/D`, `build835`, `build271`), so the ISA/GS/ST…SE/GE/IEA envelope, the
|
|
@@ -159,15 +160,15 @@ roundTrip(generate271({ seed: 3 })).specClean; // true
|
|
|
159
160
|
|
|
160
161
|
// Or a reproducible mixed corpus (837P/I/D + 835 + 271):
|
|
161
162
|
const corpus = x12Corpus({ seed: 42 });
|
|
162
|
-
corpus.artifacts.every((a) => a.warnings.length === 0); // true
|
|
163
|
+
corpus.artifacts.every((a) => a.warnings.length === 0); // true
|
|
163
164
|
```
|
|
164
165
|
|
|
165
166
|
`@cosyte/x12` is an **optional peer dependency**, needed only for the `@cosyte/synth/x12` subpath.
|
|
166
167
|
|
|
167
|
-
**Deferred:** the **270** eligibility _request_ (`@cosyte/x12` ships
|
|
168
|
-
|
|
168
|
+
**Deferred:** the **270** eligibility _request_ (`@cosyte/x12` ships `build270` on its 0.1 line, but `synth`
|
|
169
|
+
does not generate a 270 yet) and **vendor-quirk mode**.
|
|
169
170
|
|
|
170
|
-
## Generate
|
|
171
|
+
## Generate an NCPDP message
|
|
171
172
|
|
|
172
173
|
The `@cosyte/synth/ncpdp` subpath builds both NCPDP standards **through `@cosyte/ncpdp`'s own emit
|
|
173
174
|
surface**, so each message round-trips through the parser with **zero warnings**. It emits **SCRIPT**
|
|
@@ -193,7 +194,7 @@ telecomRoundTrip(generateB1({ seed: 777 })).specClean; // true
|
|
|
193
194
|
|
|
194
195
|
// Or a reproducible mixed corpus (NewRx + RxRenewal + RxChange + B1 + B2 + B3):
|
|
195
196
|
const corpus = ncpdpCorpus({ seed: 42 });
|
|
196
|
-
corpus.artifacts.every((a) => a.warnings.length === 0); // true
|
|
197
|
+
corpus.artifacts.every((a) => a.warnings.length === 0); // true
|
|
197
198
|
```
|
|
198
199
|
|
|
199
200
|
`@cosyte/ncpdp` is an **optional peer dependency**, needed only for the `@cosyte/synth/ncpdp` subpath.
|
|
@@ -201,7 +202,7 @@ corpus.artifacts.every((a) => a.warnings.length === 0); // true, all spec-clean
|
|
|
201
202
|
**Deferred:** vendor-quirk mode. SCRIPT coverage tracks the parser's builder surface: the
|
|
202
203
|
renewal/change _responses_ land as `@cosyte/ncpdp` grows builders.
|
|
203
204
|
|
|
204
|
-
## Generate
|
|
205
|
+
## Generate an ASTM message
|
|
205
206
|
|
|
206
207
|
The `@cosyte/synth/astm` subpath builds ASTM laboratory messages **through `@cosyte/astm`'s own emit
|
|
207
208
|
surface**: `buildAstmMessage` for the E1394 record layer, `composeAstmFrames` for the E1381 frame
|
|
@@ -224,12 +225,12 @@ import {
|
|
|
224
225
|
// (so they stay DISTINCT, exactly as @cosyte/astm keeps them on parse).
|
|
225
226
|
astmRoundTrip(generateAstmResult({ seed: 12345 })).specClean; // true, zero warnings, byte-stable
|
|
226
227
|
|
|
227
|
-
// The E1381-framed twin: the modulo-256 checksum and 0
|
|
228
|
+
// The E1381-framed twin: the modulo-256 checksum and 0 to 7 frame numbers are computed by @cosyte/astm.
|
|
228
229
|
astmFramedRoundTrip(generateAstmResultFramed({ seed: 12345 })).specClean; // true
|
|
229
230
|
|
|
230
231
|
// Or a reproducible mixed corpus (a result report + an order):
|
|
231
232
|
const corpus = astmCorpus({ seed: 42 });
|
|
232
|
-
corpus.artifacts.every((a) => a.warnings.length === 0); // true
|
|
233
|
+
corpus.artifacts.every((a) => a.warnings.length === 0); // true
|
|
233
234
|
```
|
|
234
235
|
|
|
235
236
|
`@cosyte/astm` is an **optional peer dependency**, needed only for the `@cosyte/synth/astm` subpath.
|
|
@@ -265,7 +266,7 @@ astmQuirkRoundTrip(generateAstmQuirk({ seed: 1, quirk: "unknown-escape" })).inte
|
|
|
265
266
|
A quirk a format's profile system does not support fails closed with a stable `SYNTH_UNSUPPORTED_QUIRK`
|
|
266
267
|
diagnostic, never a silently-wrong fixture, and so do the other selectors: a message kind, a
|
|
267
268
|
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
|
|
269
|
+
before anything is generated, so an unrecognized one is a fatal `SYNTH_UNSUPPORTED_KIND` rather than a
|
|
269
270
|
mislabelled corpus. Every refusal carries a code and a fixed message from a frozen table, and quotes
|
|
270
271
|
nothing you passed. Every quirk is grounded in a **publicly-documented** deviation or a parser's
|
|
271
272
|
**public** profile, never a private vendor corpus.
|
|
@@ -276,7 +277,7 @@ vendor-attributed corpus** to ground.
|
|
|
276
277
|
## Co-validate with `@cosyte/deid` (the pairing loop)
|
|
277
278
|
|
|
278
279
|
The `@cosyte/synth/deid` subpath is a deterministic **closed-loop co-validation harness** for the
|
|
279
|
-
`synth` ⇄ `deid` pair: it **generates**
|
|
280
|
+
`synth` ⇄ `deid` pair: it **generates** an artifact, **plants** distinctive synthetic PHI
|
|
280
281
|
sentinels at the patient loci, **de-identifies** it through `@cosyte/deid`, and **verifies** every
|
|
281
282
|
sentinel is gone from the output (a surviving sentinel is a hard failure) while the clinical payload
|
|
282
283
|
survives (the over-scrub guard).
|
|
@@ -332,14 +333,14 @@ isSyntheticNpi(safe.npi(rng)); // true, always a deliberately-invalid-Luhn NPI (
|
|
|
332
333
|
phones, RFC 2606/6761 `example.*` domains, RFC 5737/3849 TEST-NET IPs, a synthetic assigning
|
|
333
334
|
authority for MRNs). A CI gate proves it.
|
|
334
335
|
**No generated value can be real or plausibly-real PHI.**
|
|
335
|
-
- **
|
|
336
|
-
|
|
336
|
+
- **Checked by the parser's own judgment**, built through the parser's conservative serializer and
|
|
337
|
+
fed straight back in: each artifact re-parses with zero warnings.
|
|
337
338
|
- **Deterministic**, a hand-rolled seeded PRNG (`sfc32`/`splitmix32`); `Math.random` is lint-banned.
|
|
338
339
|
A seed, and only the seed, determines the output, byte-for-byte, anywhere.
|
|
339
340
|
- **Immutable**, generated artifacts and the `Corpus` result are deep-frozen.
|
|
340
|
-
- **Zero third-party runtime dependencies**, the parser peers are first-party
|
|
341
|
+
- **Zero third-party runtime dependencies**, the parser peers are first-party Cosyte packages,
|
|
341
342
|
vendored for dev/test; dual ESM + CJS, validated with `attw`.
|
|
342
343
|
|
|
343
344
|
## License
|
|
344
345
|
|
|
345
|
-
MIT
|
|
346
|
+
MIT, copyright Cosyte LLC. See [LICENSE](https://github.com/cosyte/synth/blob/main/LICENSE).
|