@cosyte/synth 0.1.4 → 0.1.6
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 +24 -0
- package/README.md +14 -10
- package/dist/astm/index.cjs +164 -102
- 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 +163 -102
- package/dist/astm/index.mjs.map +1 -1
- package/dist/ccda/index.cjs +100 -28
- 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 +87 -16
- package/dist/ccda/index.mjs.map +1 -1
- package/dist/deid/index.cjs +305 -165
- package/dist/deid/index.cjs.map +1 -1
- package/dist/deid/index.mjs +292 -153
- package/dist/deid/index.mjs.map +1 -1
- package/dist/fhir/index.cjs +179 -76
- 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 +178 -76
- package/dist/fhir/index.mjs.map +1 -1
- package/dist/hl7/index.cjs +86 -12
- 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 +85 -12
- package/dist/hl7/index.mjs.map +1 -1
- package/dist/index.cjs +3 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -4
- package/dist/index.d.ts +8 -4
- package/dist/index.mjs +3 -5
- package/dist/index.mjs.map +1 -1
- package/dist/ncpdp/index.cjs +131 -36
- 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 +130 -36
- package/dist/ncpdp/index.mjs.map +1 -1
- package/dist/x12/index.cjs +147 -50
- package/dist/x12/index.cjs.map +1 -1
- package/dist/x12/index.d.cts +6 -0
- package/dist/x12/index.d.ts +6 -0
- package/dist/x12/index.mjs +146 -50
- package/dist/x12/index.mjs.map +1 -1
- package/package.json +20 -15
package/dist/ncpdp/index.cjs
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
|
|
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
|
+
});
|
|
6
13
|
|
|
7
14
|
// src/rng/splitmix32.ts
|
|
8
15
|
function splitmix32(seed) {
|
|
@@ -34,18 +41,59 @@ function sfc32Next(s) {
|
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
// src/codes.ts
|
|
37
|
-
var SYNTH_FATAL_CODES = {
|
|
44
|
+
var SYNTH_FATAL_CODES = Object.freeze({
|
|
45
|
+
/**
|
|
46
|
+
* A format was requested that this build cannot generate through a real parser builder/serializer.
|
|
47
|
+
* Fatal, never a hand-written byte fallback.
|
|
48
|
+
*
|
|
49
|
+
* **No code path in this build raises it.** All six formats generate, so it is reserved for a
|
|
50
|
+
* future format that does not, and is kept because removing a published code is a breaking change.
|
|
51
|
+
* An unsupported *kind* within a format that does generate is `SYNTH_UNSUPPORTED_KIND`.
|
|
52
|
+
*/
|
|
53
|
+
SYNTH_UNSUPPORTED_FORMAT: "SYNTH_UNSUPPORTED_FORMAT",
|
|
54
|
+
/**
|
|
55
|
+
* A vendor quirk was requested that the target format's profile system does not support. Fatal,
|
|
56
|
+
* never a silent no-op and never a fabricated quirk.
|
|
57
|
+
*/
|
|
58
|
+
SYNTH_UNSUPPORTED_QUIRK: "SYNTH_UNSUPPORTED_QUIRK",
|
|
59
|
+
/**
|
|
60
|
+
* A quirk transform found no structural anchor to mutate, so the fixture would not carry the
|
|
61
|
+
* deviation it is labelled with. Fatal: a golden file that lies about its parser verdict is worse
|
|
62
|
+
* than no golden file.
|
|
63
|
+
*/
|
|
64
|
+
SYNTH_QUIRK_ANCHOR_ABSENT: "SYNTH_QUIRK_ANCHOR_ABSENT",
|
|
65
|
+
/**
|
|
66
|
+
* A bare parse of a freshly-generated quirk artifact did not produce exactly the declared intended
|
|
67
|
+
* warning code(s). Fatal, never emit a mislabeled fixture.
|
|
68
|
+
*/
|
|
69
|
+
SYNTH_INTENDED_WARNING_MISMATCH: "SYNTH_INTENDED_WARNING_MISMATCH",
|
|
70
|
+
/** A concept's code-system URI has no OID mapping in the C-CDA example-code table. Fatal. */
|
|
71
|
+
SYNTH_UNMAPPED_CODE_SYSTEM: "SYNTH_UNMAPPED_CODE_SYSTEM",
|
|
72
|
+
/** A money value could not be read as an X12 decimal. Fatal: a generator never rounds to a float. */
|
|
73
|
+
SYNTH_INVALID_DECIMAL: "SYNTH_INVALID_DECIMAL",
|
|
38
74
|
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
39
75
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
40
76
|
/** A value was drawn from an empty pool. Fatal, never a fabricated substitute. */
|
|
41
77
|
SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
|
|
78
|
+
/** A `defineSynthProfile` spec was not usable (a missing or blank `name`). Fatal. */
|
|
79
|
+
SYNTH_INVALID_PROFILE: "SYNTH_INVALID_PROFILE",
|
|
42
80
|
/**
|
|
43
81
|
* A caller-supplied selector (a message kind, a document type, a corpus mix entry, a claim
|
|
44
82
|
* variant, a Bundle type, a resource profile) is not in the closed set that governs it. Fatal:
|
|
45
83
|
* see `resolveKind`: a selector union is erased at run time, and a selector that falls through
|
|
46
84
|
* either mislabels the fixture or hands the value to a peer builder that quotes it back.
|
|
47
85
|
*/
|
|
48
|
-
SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND"
|
|
86
|
+
SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND",
|
|
87
|
+
/**
|
|
88
|
+
* A requested profile IS published by the adopted implementation guide, and this build does not
|
|
89
|
+
* generate it. Deliberately distinct from `SYNTH_UNSUPPORTED_KIND`, which says the name is not in
|
|
90
|
+
* the adopted set at all: "the guide does not publish this" and "we do not generate this yet" are
|
|
91
|
+
* different answers, and a caller building to a regulatory profile set has to be able to tell them
|
|
92
|
+
* apart without matching message text. Fatal, and raised **before** anything is generated: a
|
|
93
|
+
* mislabelled artifact is worse than no artifact.
|
|
94
|
+
*/
|
|
95
|
+
SYNTH_PROFILE_NOT_GENERATED: "SYNTH_PROFILE_NOT_GENERATED"
|
|
96
|
+
});
|
|
49
97
|
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
50
98
|
SYNTH_UNSUPPORTED_FORMAT: "The requested format is not generable by this build. A generator has no byte fallback: it builds through a parser's own serializer or it refuses.",
|
|
51
99
|
SYNTH_UNSUPPORTED_QUIRK: "The requested vendor quirk is not in the target format's quirk registry. Compare the request against that format's exported registry (HL7_QUIRKS, CCDA_QUIRKS, ASTM_QUIRKS).",
|
|
@@ -111,6 +159,7 @@ function createRng(seed) {
|
|
|
111
159
|
}
|
|
112
160
|
|
|
113
161
|
// src/corpus.ts
|
|
162
|
+
var UNCHECKED = Object.freeze([]);
|
|
114
163
|
function makeCorpus(seed, artifacts, quirks = []) {
|
|
115
164
|
const counts = {};
|
|
116
165
|
const formats = /* @__PURE__ */ new Set();
|
|
@@ -467,18 +516,20 @@ function ncpdpScriptRouting(rng) {
|
|
|
467
516
|
}
|
|
468
517
|
|
|
469
518
|
// src/ncpdp/example-codes.ts
|
|
470
|
-
var EXAMPLE_DRUGS = Object.freeze(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
519
|
+
var EXAMPLE_DRUGS = Object.freeze(
|
|
520
|
+
[
|
|
521
|
+
{ description: "Amoxicillin 500 MG Oral Capsule", ndc: "00000010101", form: "EA" },
|
|
522
|
+
{ description: "Lisinopril 10 MG Oral Tablet", ndc: "00000020202", form: "EA" },
|
|
523
|
+
{ description: "Atorvastatin 20 MG Oral Tablet", ndc: "00000030303", form: "EA" },
|
|
524
|
+
{ description: "Metformin 500 MG Oral Tablet", ndc: "00000040404", form: "EA" },
|
|
525
|
+
{ description: "Amlodipine 5 MG Oral Tablet", ndc: "00000050505", form: "EA" },
|
|
526
|
+
{ description: "Omeprazole 20 MG Delayed Release Capsule", ndc: "00000060606", form: "EA" },
|
|
527
|
+
{ description: "Levothyroxine 50 MCG Oral Tablet", ndc: "00000070707", form: "EA" },
|
|
528
|
+
{ description: "Azithromycin 250 MG Oral Tablet", ndc: "00000080808", form: "EA" },
|
|
529
|
+
{ description: "Sertraline 50 MG Oral Tablet", ndc: "00000090909", form: "EA" },
|
|
530
|
+
{ description: "Albuterol 90 MCG Metered Dose Inhaler", ndc: "00000101010", form: "EA" }
|
|
531
|
+
].map((entry) => Object.freeze(entry))
|
|
532
|
+
);
|
|
482
533
|
var EXAMPLE_SIG_TEXT = Object.freeze([
|
|
483
534
|
"Take 1 tablet by mouth once daily",
|
|
484
535
|
"Take 1 capsule by mouth twice daily with food",
|
|
@@ -487,6 +538,30 @@ var EXAMPLE_SIG_TEXT = Object.freeze([
|
|
|
487
538
|
"Inhale 2 puffs by mouth every 4 to 6 hours as needed"
|
|
488
539
|
]);
|
|
489
540
|
var DAW_CODES = Object.freeze(["0", "1", "2"]);
|
|
541
|
+
function defaultHost() {
|
|
542
|
+
if (typeof undefined !== "function") return { require: __require, resolve: void 0 };
|
|
543
|
+
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) };
|
|
544
|
+
}
|
|
545
|
+
function isRequireEsmRefusal(error) {
|
|
546
|
+
return error?.code === "ERR_REQUIRE_ESM";
|
|
547
|
+
}
|
|
548
|
+
function loadPeer(specifier, host) {
|
|
549
|
+
if (host.resolve === void 0) return host.require(specifier);
|
|
550
|
+
const entry = url.fileURLToPath(host.resolve(specifier));
|
|
551
|
+
try {
|
|
552
|
+
return host.require(entry);
|
|
553
|
+
} catch (error) {
|
|
554
|
+
if (!isRequireEsmRefusal(error)) throw error;
|
|
555
|
+
return host.require(specifier);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function lazyPeer(specifier, host) {
|
|
559
|
+
let loaded;
|
|
560
|
+
return () => loaded ??= loadPeer(specifier, defaultHost());
|
|
561
|
+
}
|
|
562
|
+
var ncpdpScriptPeer = lazyPeer("@cosyte/ncpdp/script");
|
|
563
|
+
var ncpdpTelecomPeer = lazyPeer("@cosyte/ncpdp/telecom");
|
|
564
|
+
var ncpdpCommonPeer = lazyPeer("@cosyte/ncpdp/common");
|
|
490
565
|
|
|
491
566
|
// src/ncpdp/script.ts
|
|
492
567
|
var SCRIPT_VERSION = "2017071";
|
|
@@ -517,13 +592,13 @@ function medicationModel(rng, routing) {
|
|
|
517
592
|
const written = `${routing.date.slice(0, 4)}-${routing.date.slice(4, 6)}-${routing.date.slice(6, 8)}`;
|
|
518
593
|
return {
|
|
519
594
|
description: drug.description,
|
|
520
|
-
coded: { productCode:
|
|
595
|
+
coded: { productCode: ncpdpCommonPeer().codedValue(drug.ndc, "ND") },
|
|
521
596
|
quantity: {
|
|
522
|
-
value:
|
|
597
|
+
value: ncpdpCommonPeer().decimalValue(String(rng.int(30, 90))),
|
|
523
598
|
unitOfMeasure: drug.form,
|
|
524
599
|
codeListQualifier: "38"
|
|
525
600
|
},
|
|
526
|
-
daysSupply:
|
|
601
|
+
daysSupply: ncpdpCommonPeer().decimalValue(String(rng.pick([30, 60, 90]))),
|
|
527
602
|
numberOfRefills: String(rng.int(0, 5)),
|
|
528
603
|
substitutions: rng.pick(["0", "1"]),
|
|
529
604
|
writtenDate: written,
|
|
@@ -547,14 +622,14 @@ function generateNewRx(options) {
|
|
|
547
622
|
const pharmacy = pharmacyModel(rng);
|
|
548
623
|
const prescriber = prescriberModel(rng);
|
|
549
624
|
const medication = medicationModel(rng, routing);
|
|
550
|
-
const message =
|
|
625
|
+
const message = ncpdpScriptPeer().buildNewRx({
|
|
551
626
|
header: headerModel(routing),
|
|
552
627
|
patient,
|
|
553
628
|
pharmacy,
|
|
554
629
|
prescriber,
|
|
555
630
|
medication
|
|
556
631
|
});
|
|
557
|
-
return
|
|
632
|
+
return ncpdpScriptPeer().serializeScript(message);
|
|
558
633
|
}
|
|
559
634
|
function lifecycleFields(rng) {
|
|
560
635
|
const routing = ncpdpScriptRouting(rng);
|
|
@@ -571,14 +646,20 @@ function generateRxRenewalRequest(options) {
|
|
|
571
646
|
const rng = createRng(options.seed);
|
|
572
647
|
const { header: header2, ...fields } = lifecycleFields(rng);
|
|
573
648
|
const body = { kind: "RxRenewalRequest", ...fields };
|
|
574
|
-
return
|
|
649
|
+
return ncpdpScriptPeer().serializeScript(
|
|
650
|
+
new (ncpdpScriptPeer()).ScriptMessage({ header: header2, body, warnings: [] })
|
|
651
|
+
);
|
|
575
652
|
}
|
|
576
653
|
function generateRxChangeRequest(options) {
|
|
577
654
|
const rng = createRng(options.seed);
|
|
578
655
|
const { header: header2, ...fields } = lifecycleFields(rng);
|
|
579
656
|
const body = { kind: "RxChangeRequest", ...fields };
|
|
580
|
-
return
|
|
657
|
+
return ncpdpScriptPeer().serializeScript(
|
|
658
|
+
new (ncpdpScriptPeer()).ScriptMessage({ header: header2, body, warnings: [] })
|
|
659
|
+
);
|
|
581
660
|
}
|
|
661
|
+
|
|
662
|
+
// src/ncpdp/telecom.ts
|
|
582
663
|
var SYNTHETIC_BIN = "999999";
|
|
583
664
|
function digitsOnly(value) {
|
|
584
665
|
return value.replace(/\D/g, "");
|
|
@@ -678,7 +759,9 @@ function generateTelecom(code, options) {
|
|
|
678
759
|
prescriberSegment(rng),
|
|
679
760
|
claimSegment(rng, false)
|
|
680
761
|
];
|
|
681
|
-
return
|
|
762
|
+
return ncpdpTelecomPeer().serializeTelecom(
|
|
763
|
+
ncpdpTelecomPeer().buildTelecomRequest({ header: head, segments })
|
|
764
|
+
);
|
|
682
765
|
}
|
|
683
766
|
function generateB1(options) {
|
|
684
767
|
return generateTelecom("B1", options);
|
|
@@ -689,18 +772,30 @@ function generateB2(options) {
|
|
|
689
772
|
function generateB3(options) {
|
|
690
773
|
return generateTelecom("B3", options);
|
|
691
774
|
}
|
|
775
|
+
|
|
776
|
+
// src/ncpdp/round-trip.ts
|
|
692
777
|
function scriptRoundTrip(xml) {
|
|
693
|
-
const message =
|
|
778
|
+
const message = ncpdpScriptPeer().parseScript(xml);
|
|
694
779
|
const warnings = message.warnings.map((w) => String(w.code));
|
|
695
|
-
const byteStable =
|
|
780
|
+
const byteStable = ncpdpScriptPeer().serializeScript(message) === xml;
|
|
696
781
|
return { content: xml, warnings, byteStable, specClean: warnings.length === 0 && byteStable };
|
|
697
782
|
}
|
|
698
783
|
function telecomRoundTrip(wire) {
|
|
699
|
-
const transaction =
|
|
784
|
+
const transaction = ncpdpTelecomPeer().parseTelecom(wire);
|
|
700
785
|
const warnings = transaction.warnings.map((w) => String(w.code));
|
|
701
|
-
const byteStable =
|
|
786
|
+
const byteStable = ncpdpTelecomPeer().serializeTelecom(transaction) === wire;
|
|
702
787
|
return { content: wire, warnings, byteStable, specClean: warnings.length === 0 && byteStable };
|
|
703
788
|
}
|
|
789
|
+
function scriptCorpusArtifact(xml, verify) {
|
|
790
|
+
if (!verify) return { content: xml, warnings: UNCHECKED };
|
|
791
|
+
const warnings = ncpdpScriptPeer().parseScript(xml).warnings.map((w) => String(w.code));
|
|
792
|
+
return { content: xml, warnings };
|
|
793
|
+
}
|
|
794
|
+
function telecomCorpusArtifact(wire, verify) {
|
|
795
|
+
if (!verify) return { content: wire, warnings: UNCHECKED };
|
|
796
|
+
const warnings = ncpdpTelecomPeer().parseTelecom(wire).warnings.map((w) => String(w.code));
|
|
797
|
+
return { content: wire, warnings };
|
|
798
|
+
}
|
|
704
799
|
|
|
705
800
|
// src/select.ts
|
|
706
801
|
function resolveKind(allowed, requested) {
|
|
@@ -723,29 +818,29 @@ var ALL_KINDS = Object.freeze([
|
|
|
723
818
|
"B3"
|
|
724
819
|
]);
|
|
725
820
|
var DEFAULT_MIX = ALL_KINDS;
|
|
726
|
-
function generateKind(kind, seed) {
|
|
821
|
+
function generateKind(kind, seed, verify) {
|
|
727
822
|
switch (kind) {
|
|
728
823
|
case "NewRx":
|
|
729
|
-
return
|
|
824
|
+
return scriptCorpusArtifact(generateNewRx({ seed }), verify);
|
|
730
825
|
case "RxRenewalRequest":
|
|
731
|
-
return
|
|
826
|
+
return scriptCorpusArtifact(generateRxRenewalRequest({ seed }), verify);
|
|
732
827
|
case "RxChangeRequest":
|
|
733
|
-
return
|
|
828
|
+
return scriptCorpusArtifact(generateRxChangeRequest({ seed }), verify);
|
|
734
829
|
case "B1":
|
|
735
830
|
case "B2":
|
|
736
831
|
case "B3":
|
|
737
|
-
return
|
|
832
|
+
return telecomCorpusArtifact(generateTelecom(kind, { seed }), verify);
|
|
738
833
|
}
|
|
739
834
|
}
|
|
740
835
|
function ncpdpCorpus(options) {
|
|
741
|
-
const { seed } = options;
|
|
836
|
+
const { seed, verify = true } = options;
|
|
742
837
|
const mix = resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
743
838
|
const count = options.count ?? mix.length;
|
|
744
839
|
const seedStream = createRng(seed);
|
|
745
840
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
746
841
|
const kind = mix[i % mix.length] ?? "NewRx";
|
|
747
842
|
const txSeed = seedStream.nextUint32();
|
|
748
|
-
const rt = generateKind(kind, txSeed);
|
|
843
|
+
const rt = generateKind(kind, txSeed, verify);
|
|
749
844
|
return {
|
|
750
845
|
format: "ncpdp",
|
|
751
846
|
kind,
|