@cosyte/synth 0.1.5 → 0.1.7
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 +30 -0
- package/dist/astm/index.cjs +57 -19
- package/dist/astm/index.cjs.map +1 -1
- package/dist/astm/index.d.cts +7 -0
- package/dist/astm/index.d.ts +7 -0
- package/dist/astm/index.mjs +56 -19
- package/dist/astm/index.mjs.map +1 -1
- package/dist/ccda/index.cjs +76 -26
- package/dist/ccda/index.cjs.map +1 -1
- package/dist/ccda/index.d.cts +6 -0
- package/dist/ccda/index.d.ts +6 -0
- package/dist/ccda/index.mjs +63 -14
- package/dist/ccda/index.mjs.map +1 -1
- package/dist/deid/index.cjs +216 -125
- package/dist/deid/index.cjs.map +1 -1
- package/dist/deid/index.mjs +203 -113
- package/dist/deid/index.mjs.map +1 -1
- package/dist/fhir/index.cjs +146 -74
- package/dist/fhir/index.cjs.map +1 -1
- package/dist/fhir/index.d.cts +6 -0
- package/dist/fhir/index.d.ts +6 -0
- package/dist/fhir/index.mjs +145 -74
- package/dist/fhir/index.mjs.map +1 -1
- package/dist/hl7/index.cjs +53 -9
- package/dist/hl7/index.cjs.map +1 -1
- package/dist/hl7/index.d.cts +6 -0
- package/dist/hl7/index.d.ts +6 -0
- package/dist/hl7/index.mjs +52 -9
- package/dist/hl7/index.mjs.map +1 -1
- package/dist/index.cjs +1 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -3
- package/dist/index.mjs.map +1 -1
- package/dist/ncpdp/index.cjs +76 -23
- package/dist/ncpdp/index.cjs.map +1 -1
- package/dist/ncpdp/index.d.cts +7 -0
- package/dist/ncpdp/index.d.ts +7 -0
- package/dist/ncpdp/index.mjs +75 -23
- package/dist/ncpdp/index.mjs.map +1 -1
- package/dist/x12/index.cjs +72 -23
- package/dist/x12/index.cjs.map +1 -1
- package/dist/x12/index.d.cts +8 -1
- package/dist/x12/index.d.ts +8 -1
- package/dist/x12/index.mjs +71 -23
- package/dist/x12/index.mjs.map +1 -1
- package/package.json +20 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 65399f9: `@cosyte/synth` now installs beside `@cosyte/deid` 0.2. The `@cosyte/deid` peer range is `^0.1.5 || ^0.2.0`, where it was `^0.1.5`, so npm no longer refuses the install with ERESOLVE when a project already has deid 0.2.3. The tests now run against `@cosyte/deid` 0.2.3, and a new workflow installs the lowest and the highest version the range admits and runs the de-identification loop against each.
|
|
8
|
+
|
|
9
|
+
Generated 837P, 837I and 837D claims now write each ICD-10-CM diagnosis code in `HI` without its decimal point (`HI*ABK:J209`, not `HI*ABK:J20.9`), as the 005010 claim guides transmit it. `@cosyte/deid` 0.2 removes a diagnosis code that has a decimal point, which made the X12 pairing loop report clinical codes as scrubbed. The exported `DIAGNOSES` pool (`@cosyte/synth/x12`) holds the same codes in that form: a caller that compared a generated code with `J20.9` must compare with `J209`, or remove the point first.
|
|
10
|
+
|
|
11
|
+
Output change: X12. Every generated 837P, 837I and 837D whose diagnosis code has a decimal point, and every X12 corpus that holds one, carries its diagnosis codes without the decimal point; nothing else in them moves. The X12 de-identification loop results move with it and with deid 0.2's X12 pass. An 837 golden file committed from an earlier release has to be regenerated. HL7 v2, FHIR R4 and US Core, C-CDA, NCPDP and ASTM output is byte for byte what it was for the same seed.
|
|
12
|
+
|
|
13
|
+
- 7bb58a6: Three generated values now follow their standards: the X12 271 benefit percent, the NCPDP Telecom Quantity Dispensed and the ASTM header sender.
|
|
14
|
+
|
|
15
|
+
- X12 271: EB-08 (benefit percent) is a decimal fraction, so it is now written as `.8`, `.9` or `1` instead of `80`, `90` or `100`.
|
|
16
|
+
- NCPDP Telecom: 442-E7 (Quantity Dispensed) carries three implied decimals, so a quantity of 37 is now written as `37000` instead of `37`. It reads back through `@cosyte/ncpdp` as `37.000`.
|
|
17
|
+
- ASTM: the sender name now sits in H.5 (Sender Name or ID). Before, the sender went to H.3 (Message Control ID) and the analyzer string to H.4 (Access Password). H.3 and H.4 are now empty, and the analyzer string is no longer written to the header. `astmHeaderIdentity` still returns both strings.
|
|
18
|
+
|
|
19
|
+
Output change: X12, NCPDP and ASTM. For the same seed, every generated 271, every B1 and B3 Telecom claim, and every ASTM result, order, framed and quirk message changes in the values above, and so do the corpora and de-identification loop results built from them. Every other generated artifact is byte for byte what it was for the same seed. If you commit golden files from an earlier release, regenerate each 271, B1, B3 and ASTM golden file from its seed with this release and commit the new bytes.
|
|
20
|
+
|
|
21
|
+
## 0.1.6
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- d3c649c: Each format subpath now imports without loading its parser, and a corpus checks its artifacts in less time.
|
|
26
|
+
|
|
27
|
+
- Importing `@cosyte/synth/hl7`, `/fhir`, `/ccda`, `/x12`, `/ncpdp`, `/astm` or `/deid` no longer loads the sibling parser it builds through. The parser loads on the first call that needs it, synchronously, so every function keeps its signature. It is the same build an `import` or a `require` of the parser gives you, so a returned message, resource, document or interchange is an instance of the parser classes your own code imports. One thing moves: when an optional parser is not installed, the error that names it now comes from the first call that needs it, not from the import of the subpath. Install the parser for each format you generate, as before.
|
|
28
|
+
- Every corpus generator (`hl7Corpus`, `fhirCorpus`, `ccdaCorpus`, `x12Corpus`, `ncpdpCorpus`, `astmCorpus`) takes a new `verify` option. It defaults to `true`, which reads each artifact back through its parser and records the parser's findings as its `warnings`, as before. A corpus now reads each artifact back once and no longer serializes it a second time, since a corpus never reported byte stability; `roundTrip` and the other round-trip functions still check it. `verify: false` skips the read, a large share of a corpus's time, and leaves every artifact's `warnings` empty without checking it. Pass it when the corpus feeds a load test and the parser's verdict is not needed.
|
|
29
|
+
- The sibling parsers move to their latest releases: `@cosyte/hl7` 0.1.8, `@cosyte/fhir` 0.1.8, `@cosyte/ccda` 0.1.6, `@cosyte/x12` 0.1.10, `@cosyte/ncpdp` 0.1.6, `@cosyte/astm` 0.1.6 and `@cosyte/deid` 0.1.5, and each optional peer range now starts at that release.
|
|
30
|
+
|
|
31
|
+
Output change: X12. Every generated 271 now carries the BHT segment the 005010X279A1 guide requires right after its ST, because `@cosyte/x12` 0.1.10 writes it; its SE segment count is one higher, and the X12 271 de-identification loop result moves with it. A 271 golden file committed from an earlier release has to be regenerated. Every other generated artifact is byte for byte what it was for the same seed.
|
|
32
|
+
|
|
3
33
|
## 0.1.5
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
package/dist/astm/index.cjs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var module$1 = require('module');
|
|
4
|
+
var url = require('url');
|
|
5
|
+
|
|
6
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
4
13
|
|
|
5
14
|
// src/rng/splitmix32.ts
|
|
6
15
|
function splitmix32(seed) {
|
|
@@ -150,6 +159,7 @@ function createRng(seed) {
|
|
|
150
159
|
}
|
|
151
160
|
|
|
152
161
|
// src/corpus.ts
|
|
162
|
+
var UNCHECKED = Object.freeze([]);
|
|
153
163
|
function makeCorpus(seed, artifacts, quirks = []) {
|
|
154
164
|
const counts = {};
|
|
155
165
|
const formats = /* @__PURE__ */ new Set();
|
|
@@ -586,6 +596,28 @@ var ASTM_COMMENT_TEXT = Object.freeze([
|
|
|
586
596
|
"Sample generated by @cosyte/synth.",
|
|
587
597
|
"Placeholder result comment for conformance testing."
|
|
588
598
|
]);
|
|
599
|
+
function defaultHost() {
|
|
600
|
+
if (typeof undefined !== "function") return { require: __require, resolve: void 0 };
|
|
601
|
+
return { require: module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))), resolve: (s) => undefined(s) };
|
|
602
|
+
}
|
|
603
|
+
function isRequireEsmRefusal(error) {
|
|
604
|
+
return error?.code === "ERR_REQUIRE_ESM";
|
|
605
|
+
}
|
|
606
|
+
function loadPeer(specifier, host) {
|
|
607
|
+
if (host.resolve === void 0) return host.require(specifier);
|
|
608
|
+
const entry = url.fileURLToPath(host.resolve(specifier));
|
|
609
|
+
try {
|
|
610
|
+
return host.require(entry);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
if (!isRequireEsmRefusal(error)) throw error;
|
|
613
|
+
return host.require(specifier);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
function lazyPeer(specifier, host) {
|
|
617
|
+
let loaded;
|
|
618
|
+
return () => loaded ??= loadPeer(specifier, defaultHost());
|
|
619
|
+
}
|
|
620
|
+
var astmPeer = lazyPeer("@cosyte/astm");
|
|
589
621
|
|
|
590
622
|
// src/astm/message.ts
|
|
591
623
|
function resultValue(rng, low, high, decimals) {
|
|
@@ -634,13 +666,14 @@ function buildResultInput(options) {
|
|
|
634
666
|
records.push({ type: "C", source: "L", text: rng.pick(ASTM_COMMENT_TEXT), commentType: "G" });
|
|
635
667
|
}
|
|
636
668
|
return {
|
|
637
|
-
|
|
669
|
+
// H.3 (message control id) and H.4 (access password) stay empty; H.5 is the sender name.
|
|
670
|
+
header: { fields: ["", "", head.sender] },
|
|
638
671
|
records,
|
|
639
672
|
terminationCode: "N"
|
|
640
673
|
};
|
|
641
674
|
}
|
|
642
675
|
function generateAstmResult(options) {
|
|
643
|
-
return
|
|
676
|
+
return astmPeer().buildAstmMessage(buildResultInput(options));
|
|
644
677
|
}
|
|
645
678
|
function generateAstmOrder(options) {
|
|
646
679
|
return generateAstmResult({ ...options, resultCount: 0, comment: false });
|
|
@@ -648,21 +681,23 @@ function generateAstmOrder(options) {
|
|
|
648
681
|
function generateAstmResultFramed(options) {
|
|
649
682
|
const raw = generateAstmResult(options);
|
|
650
683
|
const recordLines = raw.split("\r").filter((line) => line.length > 0).map((line) => `${line}\r`);
|
|
651
|
-
return
|
|
684
|
+
return astmPeer().composeAstmFrames(recordLines);
|
|
652
685
|
}
|
|
686
|
+
|
|
687
|
+
// src/astm/round-trip.ts
|
|
653
688
|
function astmRoundTrip(raw) {
|
|
654
|
-
const message =
|
|
689
|
+
const message = astmPeer().parseAstmRecords(raw);
|
|
655
690
|
const warnings = message.warnings.map((w) => String(w.code));
|
|
656
|
-
const byteStable =
|
|
691
|
+
const byteStable = astmPeer().serializeAstmRecords(message) === raw;
|
|
657
692
|
return { content: raw, warnings, byteStable, specClean: warnings.length === 0 && byteStable };
|
|
658
693
|
}
|
|
659
694
|
function astmFramedRoundTrip(bytes) {
|
|
660
|
-
const { message, frameWarnings } =
|
|
695
|
+
const { message, frameWarnings } = astmPeer().parseFramedAstm(bytes);
|
|
661
696
|
const warnings = [
|
|
662
697
|
...frameWarnings.map((w) => String(w.code)),
|
|
663
698
|
...message.warnings.map((w) => String(w.code))
|
|
664
699
|
];
|
|
665
|
-
const reframed =
|
|
700
|
+
const reframed = astmPeer().serializeFramedAstm(message);
|
|
666
701
|
const byteStable = bytesEqual(reframed, bytes);
|
|
667
702
|
const content = latin1(bytes);
|
|
668
703
|
return { content, warnings, byteStable, specClean: warnings.length === 0 && byteStable };
|
|
@@ -677,6 +712,11 @@ function latin1(bytes) {
|
|
|
677
712
|
for (const b of bytes) out += String.fromCharCode(b);
|
|
678
713
|
return out;
|
|
679
714
|
}
|
|
715
|
+
function astmCorpusArtifact(raw, verify) {
|
|
716
|
+
if (!verify) return { content: raw, warnings: UNCHECKED };
|
|
717
|
+
const warnings = astmPeer().parseAstmRecords(raw).warnings.map((w) => String(w.code));
|
|
718
|
+
return { content: raw, warnings };
|
|
719
|
+
}
|
|
680
720
|
|
|
681
721
|
// src/select.ts
|
|
682
722
|
function resolveKind(allowed, requested) {
|
|
@@ -764,7 +804,7 @@ var ASTM_QUIRKS = Object.freeze({
|
|
|
764
804
|
})
|
|
765
805
|
});
|
|
766
806
|
function toleratingProfile(quirk) {
|
|
767
|
-
return quirk === "unknown-escape" ?
|
|
807
|
+
return quirk === "unknown-escape" ? astmPeer().astmProfiles.referenceCorpus : void 0;
|
|
768
808
|
}
|
|
769
809
|
var CR = "\r";
|
|
770
810
|
function applyQuirk(quirk, records) {
|
|
@@ -808,7 +848,7 @@ function generateAstmQuirk(options) {
|
|
|
808
848
|
}
|
|
809
849
|
assertIntendedWarnings(
|
|
810
850
|
descriptor.intendedWarnings,
|
|
811
|
-
|
|
851
|
+
astmPeer().parseAstmRecords(content).warnings.map((w) => String(w.code))
|
|
812
852
|
);
|
|
813
853
|
return Object.freeze({
|
|
814
854
|
format: "astm",
|
|
@@ -821,12 +861,10 @@ function generateAstmQuirk(options) {
|
|
|
821
861
|
function astmQuirkRoundTrip(artifact) {
|
|
822
862
|
const quirk = artifact.quirk;
|
|
823
863
|
const descriptor = resolveQuirk(ASTM_QUIRKS, "astm", quirk);
|
|
824
|
-
const bare =
|
|
864
|
+
const bare = astmPeer().parseAstmRecords(artifact.content).warnings.map((w) => String(w.code));
|
|
825
865
|
const profile = toleratingProfile(quirk);
|
|
826
866
|
const withProfile = profile !== void 0 && descriptor.toleratingProfile !== void 0 ? (() => {
|
|
827
|
-
const warnings =
|
|
828
|
-
(w) => String(w.code)
|
|
829
|
-
);
|
|
867
|
+
const warnings = astmPeer().parseAstmRecords(artifact.content, { profile }).warnings.map((w) => String(w.code));
|
|
830
868
|
return {
|
|
831
869
|
profileName: descriptor.toleratingProfile,
|
|
832
870
|
disposition: descriptor.disposition,
|
|
@@ -876,23 +914,23 @@ var astmQuirkProfile = defineSynthProfile({
|
|
|
876
914
|
// src/astm/index.ts
|
|
877
915
|
var ALL_KINDS = Object.freeze(["Result", "Order"]);
|
|
878
916
|
var DEFAULT_MIX = ALL_KINDS;
|
|
879
|
-
function generateKind(kind, seed) {
|
|
917
|
+
function generateKind(kind, seed, verify) {
|
|
880
918
|
switch (kind) {
|
|
881
919
|
case "Result":
|
|
882
|
-
return
|
|
920
|
+
return astmCorpusArtifact(generateAstmResult({ seed }), verify);
|
|
883
921
|
case "Order":
|
|
884
|
-
return
|
|
922
|
+
return astmCorpusArtifact(generateAstmOrder({ seed }), verify);
|
|
885
923
|
}
|
|
886
924
|
}
|
|
887
925
|
function astmCorpus(options) {
|
|
888
|
-
const { seed } = options;
|
|
926
|
+
const { seed, verify = true } = options;
|
|
889
927
|
const mix = resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
890
928
|
const count = options.count ?? mix.length;
|
|
891
929
|
const seedStream = createRng(seed);
|
|
892
930
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
893
931
|
const kind = mix[i % mix.length] ?? "Result";
|
|
894
932
|
const msgSeed = seedStream.nextUint32();
|
|
895
|
-
const rt = generateKind(kind, msgSeed);
|
|
933
|
+
const rt = generateKind(kind, msgSeed, verify);
|
|
896
934
|
return {
|
|
897
935
|
format: "astm",
|
|
898
936
|
kind,
|