@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/x12/index.d.cts
CHANGED
|
@@ -465,6 +465,12 @@ interface X12CorpusOptions {
|
|
|
465
465
|
readonly count?: number;
|
|
466
466
|
/** The transaction kinds to cycle through. Defaults to one of each. */
|
|
467
467
|
readonly mix?: readonly X12CorpusKind[];
|
|
468
|
+
/**
|
|
469
|
+
* Whether each artifact is read back through its parser and the parser's findings recorded as its
|
|
470
|
+
* `warnings`. Defaults to `true`. `false` skips the read, a large share of a corpus's time; every
|
|
471
|
+
* artifact's `warnings` is then empty without having been checked.
|
|
472
|
+
*/
|
|
473
|
+
readonly verify?: boolean;
|
|
468
474
|
}
|
|
469
475
|
/**
|
|
470
476
|
* Build a reproducible {@link Corpus} of spec-clean X12 transactions. Each transaction is generated from
|
package/dist/x12/index.d.ts
CHANGED
|
@@ -465,6 +465,12 @@ interface X12CorpusOptions {
|
|
|
465
465
|
readonly count?: number;
|
|
466
466
|
/** The transaction kinds to cycle through. Defaults to one of each. */
|
|
467
467
|
readonly mix?: readonly X12CorpusKind[];
|
|
468
|
+
/**
|
|
469
|
+
* Whether each artifact is read back through its parser and the parser's findings recorded as its
|
|
470
|
+
* `warnings`. Defaults to `true`. `false` skips the read, a large share of a corpus's time; every
|
|
471
|
+
* artifact's `warnings` is then empty without having been checked.
|
|
472
|
+
*/
|
|
473
|
+
readonly verify?: boolean;
|
|
468
474
|
}
|
|
469
475
|
/**
|
|
470
476
|
* Build a reproducible {@link Corpus} of spec-clean X12 transactions. Each transaction is generated from
|
package/dist/x12/index.mjs
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
2
10
|
|
|
3
11
|
// src/rng/splitmix32.ts
|
|
4
12
|
function splitmix32(seed) {
|
|
@@ -30,20 +38,59 @@ function sfc32Next(s) {
|
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
// src/codes.ts
|
|
33
|
-
var SYNTH_FATAL_CODES = {
|
|
41
|
+
var SYNTH_FATAL_CODES = Object.freeze({
|
|
42
|
+
/**
|
|
43
|
+
* A format was requested that this build cannot generate through a real parser builder/serializer.
|
|
44
|
+
* Fatal, never a hand-written byte fallback.
|
|
45
|
+
*
|
|
46
|
+
* **No code path in this build raises it.** All six formats generate, so it is reserved for a
|
|
47
|
+
* future format that does not, and is kept because removing a published code is a breaking change.
|
|
48
|
+
* An unsupported *kind* within a format that does generate is `SYNTH_UNSUPPORTED_KIND`.
|
|
49
|
+
*/
|
|
50
|
+
SYNTH_UNSUPPORTED_FORMAT: "SYNTH_UNSUPPORTED_FORMAT",
|
|
51
|
+
/**
|
|
52
|
+
* A vendor quirk was requested that the target format's profile system does not support. Fatal,
|
|
53
|
+
* never a silent no-op and never a fabricated quirk.
|
|
54
|
+
*/
|
|
55
|
+
SYNTH_UNSUPPORTED_QUIRK: "SYNTH_UNSUPPORTED_QUIRK",
|
|
56
|
+
/**
|
|
57
|
+
* A quirk transform found no structural anchor to mutate, so the fixture would not carry the
|
|
58
|
+
* deviation it is labelled with. Fatal: a golden file that lies about its parser verdict is worse
|
|
59
|
+
* than no golden file.
|
|
60
|
+
*/
|
|
61
|
+
SYNTH_QUIRK_ANCHOR_ABSENT: "SYNTH_QUIRK_ANCHOR_ABSENT",
|
|
62
|
+
/**
|
|
63
|
+
* A bare parse of a freshly-generated quirk artifact did not produce exactly the declared intended
|
|
64
|
+
* warning code(s). Fatal, never emit a mislabeled fixture.
|
|
65
|
+
*/
|
|
66
|
+
SYNTH_INTENDED_WARNING_MISMATCH: "SYNTH_INTENDED_WARNING_MISMATCH",
|
|
67
|
+
/** A concept's code-system URI has no OID mapping in the C-CDA example-code table. Fatal. */
|
|
68
|
+
SYNTH_UNMAPPED_CODE_SYSTEM: "SYNTH_UNMAPPED_CODE_SYSTEM",
|
|
34
69
|
/** A money value could not be read as an X12 decimal. Fatal: a generator never rounds to a float. */
|
|
35
70
|
SYNTH_INVALID_DECIMAL: "SYNTH_INVALID_DECIMAL",
|
|
36
71
|
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
37
72
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
38
73
|
/** A value was drawn from an empty pool. Fatal, never a fabricated substitute. */
|
|
39
74
|
SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
|
|
75
|
+
/** A `defineSynthProfile` spec was not usable (a missing or blank `name`). Fatal. */
|
|
76
|
+
SYNTH_INVALID_PROFILE: "SYNTH_INVALID_PROFILE",
|
|
40
77
|
/**
|
|
41
78
|
* A caller-supplied selector (a message kind, a document type, a corpus mix entry, a claim
|
|
42
79
|
* variant, a Bundle type, a resource profile) is not in the closed set that governs it. Fatal:
|
|
43
80
|
* see `resolveKind`: a selector union is erased at run time, and a selector that falls through
|
|
44
81
|
* either mislabels the fixture or hands the value to a peer builder that quotes it back.
|
|
45
82
|
*/
|
|
46
|
-
SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND"
|
|
83
|
+
SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND",
|
|
84
|
+
/**
|
|
85
|
+
* A requested profile IS published by the adopted implementation guide, and this build does not
|
|
86
|
+
* generate it. Deliberately distinct from `SYNTH_UNSUPPORTED_KIND`, which says the name is not in
|
|
87
|
+
* the adopted set at all: "the guide does not publish this" and "we do not generate this yet" are
|
|
88
|
+
* different answers, and a caller building to a regulatory profile set has to be able to tell them
|
|
89
|
+
* apart without matching message text. Fatal, and raised **before** anything is generated: a
|
|
90
|
+
* mislabelled artifact is worse than no artifact.
|
|
91
|
+
*/
|
|
92
|
+
SYNTH_PROFILE_NOT_GENERATED: "SYNTH_PROFILE_NOT_GENERATED"
|
|
93
|
+
});
|
|
47
94
|
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
48
95
|
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.",
|
|
49
96
|
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).",
|
|
@@ -109,6 +156,7 @@ function createRng(seed) {
|
|
|
109
156
|
}
|
|
110
157
|
|
|
111
158
|
// src/corpus.ts
|
|
159
|
+
var UNCHECKED = Object.freeze([]);
|
|
112
160
|
function makeCorpus(seed, artifacts, quirks = []) {
|
|
113
161
|
const counts = {};
|
|
114
162
|
const formats = /* @__PURE__ */ new Set();
|
|
@@ -128,8 +176,32 @@ function makeCorpus(seed, artifacts, quirks = []) {
|
|
|
128
176
|
artifacts: Object.freeze(frozenArtifacts)
|
|
129
177
|
});
|
|
130
178
|
}
|
|
179
|
+
function defaultHost() {
|
|
180
|
+
if (typeof import.meta.resolve !== "function") return { require: __require, resolve: void 0 };
|
|
181
|
+
return { require: createRequire(import.meta.url), resolve: (s) => import.meta.resolve(s) };
|
|
182
|
+
}
|
|
183
|
+
function isRequireEsmRefusal(error) {
|
|
184
|
+
return error?.code === "ERR_REQUIRE_ESM";
|
|
185
|
+
}
|
|
186
|
+
function loadPeer(specifier, host) {
|
|
187
|
+
if (host.resolve === void 0) return host.require(specifier);
|
|
188
|
+
const entry = fileURLToPath(host.resolve(specifier));
|
|
189
|
+
try {
|
|
190
|
+
return host.require(entry);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (!isRequireEsmRefusal(error)) throw error;
|
|
193
|
+
return host.require(specifier);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function lazyPeer(specifier, host) {
|
|
197
|
+
let loaded;
|
|
198
|
+
return () => loaded ??= loadPeer(specifier, defaultHost());
|
|
199
|
+
}
|
|
200
|
+
var x12Peer = lazyPeer("@cosyte/x12");
|
|
201
|
+
|
|
202
|
+
// src/x12/money.ts
|
|
131
203
|
function dec(value) {
|
|
132
|
-
const d = X12Decimal.fromString(value);
|
|
204
|
+
const d = x12Peer().X12Decimal.fromString(value);
|
|
133
205
|
if (d === void 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INVALID_DECIMAL);
|
|
134
206
|
return d;
|
|
135
207
|
}
|
|
@@ -482,39 +554,49 @@ function x12EnvelopeTiming(rng) {
|
|
|
482
554
|
}
|
|
483
555
|
|
|
484
556
|
// src/x12/example-codes.ts
|
|
485
|
-
var PROFESSIONAL_PROCEDURES = Object.freeze(
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
557
|
+
var PROFESSIONAL_PROCEDURES = Object.freeze(
|
|
558
|
+
[
|
|
559
|
+
{ code: "99213", label: "office/outpatient visit, established patient" },
|
|
560
|
+
{ code: "99214", label: "office/outpatient visit, established patient, moderate" },
|
|
561
|
+
{ code: "99203", label: "office/outpatient visit, new patient" },
|
|
562
|
+
{ code: "85025", label: "complete blood count with differential" },
|
|
563
|
+
{ code: "80053", label: "comprehensive metabolic panel" },
|
|
564
|
+
{ code: "93000", label: "electrocardiogram, routine" }
|
|
565
|
+
].map((entry) => Object.freeze(entry))
|
|
566
|
+
);
|
|
493
567
|
var PROCEDURE_MODIFIERS = Object.freeze(["25", "59", "76", "GT"]);
|
|
494
568
|
var PLACES_OF_SERVICE = Object.freeze(["11", "22", "21", "12", "49"]);
|
|
495
|
-
var DIAGNOSES = Object.freeze(
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
var
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
])
|
|
569
|
+
var DIAGNOSES = Object.freeze(
|
|
570
|
+
[
|
|
571
|
+
{ code: "J20.9", label: "acute bronchitis, unspecified" },
|
|
572
|
+
{ code: "E11.9", label: "type 2 diabetes mellitus without complications" },
|
|
573
|
+
{ code: "I10", label: "essential (primary) hypertension" },
|
|
574
|
+
{ code: "M54.5", label: "low back pain" },
|
|
575
|
+
{ code: "R07.9", label: "chest pain, unspecified" },
|
|
576
|
+
{ code: "Z00.00", label: "general adult medical exam without abnormal findings" }
|
|
577
|
+
].map((entry) => Object.freeze(entry))
|
|
578
|
+
);
|
|
579
|
+
var REVENUE_CODES = Object.freeze(
|
|
580
|
+
[
|
|
581
|
+
{ code: "0120", label: "room & board, semi-private" },
|
|
582
|
+
{ code: "0300", label: "laboratory, general" },
|
|
583
|
+
{ code: "0450", label: "emergency room, general" },
|
|
584
|
+
{ code: "0636", label: "drugs requiring detailed coding" }
|
|
585
|
+
].map((entry) => Object.freeze(entry))
|
|
586
|
+
);
|
|
587
|
+
var INSTITUTIONAL_PROCEDURES = Object.freeze(
|
|
588
|
+
[
|
|
589
|
+
{ code: "99221", label: "initial hospital care, per day" },
|
|
590
|
+
{ code: "99231", label: "subsequent hospital care, per day" }
|
|
591
|
+
].map((entry) => Object.freeze(entry))
|
|
592
|
+
);
|
|
593
|
+
var DENTAL_PROCEDURES = Object.freeze(
|
|
594
|
+
[
|
|
595
|
+
{ code: "D2391", label: "resin-based composite, one surface, posterior" },
|
|
596
|
+
{ code: "D1110", label: "prophylaxis, adult" },
|
|
597
|
+
{ code: "D0120", label: "periodic oral evaluation" }
|
|
598
|
+
].map((entry) => Object.freeze(entry))
|
|
599
|
+
);
|
|
518
600
|
var TOOTH_CODES = Object.freeze(["14", "3", "19", "30"]);
|
|
519
601
|
var TOOTH_SURFACES = Object.freeze(["M", "O", "D", "B", "L"]);
|
|
520
602
|
var CARC_CODES = Object.freeze(["1", "2", "3", "45", "96", "197"]);
|
|
@@ -720,11 +802,11 @@ function generate837(variant, options) {
|
|
|
720
802
|
const spec = specOf(rng, resolved);
|
|
721
803
|
switch (resolved) {
|
|
722
804
|
case "P":
|
|
723
|
-
return build837P(spec);
|
|
805
|
+
return x12Peer().build837P(spec);
|
|
724
806
|
case "I":
|
|
725
|
-
return build837I(spec);
|
|
807
|
+
return x12Peer().build837I(spec);
|
|
726
808
|
case "D":
|
|
727
|
-
return build837D(spec);
|
|
809
|
+
return x12Peer().build837D(spec);
|
|
728
810
|
}
|
|
729
811
|
}
|
|
730
812
|
function generate837P(options) {
|
|
@@ -736,6 +818,8 @@ function generate837I(options) {
|
|
|
736
818
|
function generate837D(options) {
|
|
737
819
|
return generate837("D", options);
|
|
738
820
|
}
|
|
821
|
+
|
|
822
|
+
// src/x12/remit-835.ts
|
|
739
823
|
function generate835(options) {
|
|
740
824
|
const rng = createRng(options.seed);
|
|
741
825
|
const partners = x12TradingPartners(rng);
|
|
@@ -836,8 +920,10 @@ function generate835(options) {
|
|
|
836
920
|
},
|
|
837
921
|
claims: [claim]
|
|
838
922
|
};
|
|
839
|
-
return build835(spec);
|
|
923
|
+
return x12Peer().build835(spec);
|
|
840
924
|
}
|
|
925
|
+
|
|
926
|
+
// src/x12/eligibility-271.ts
|
|
841
927
|
function generate271(options) {
|
|
842
928
|
const rng = createRng(options.seed);
|
|
843
929
|
const partners = x12TradingPartners(rng);
|
|
@@ -917,13 +1003,15 @@ function generate271(options) {
|
|
|
917
1003
|
}
|
|
918
1004
|
]
|
|
919
1005
|
};
|
|
920
|
-
return build271(spec);
|
|
1006
|
+
return x12Peer().build271(spec);
|
|
921
1007
|
}
|
|
1008
|
+
|
|
1009
|
+
// src/x12/round-trip.ts
|
|
922
1010
|
function roundTrip(interchange) {
|
|
923
|
-
const content = serializeX12(interchange);
|
|
924
|
-
const reparsed = parseX12(content);
|
|
1011
|
+
const content = x12Peer().serializeX12(interchange);
|
|
1012
|
+
const reparsed = x12Peer().parseX12(content);
|
|
925
1013
|
const warnings = reparsed.warnings.map((w) => String(w.code));
|
|
926
|
-
const byteStable = serializeX12(reparsed) === content;
|
|
1014
|
+
const byteStable = x12Peer().serializeX12(reparsed) === content;
|
|
927
1015
|
return {
|
|
928
1016
|
content,
|
|
929
1017
|
warnings,
|
|
@@ -931,6 +1019,14 @@ function roundTrip(interchange) {
|
|
|
931
1019
|
specClean: warnings.length === 0 && byteStable
|
|
932
1020
|
};
|
|
933
1021
|
}
|
|
1022
|
+
function x12CorpusArtifact(interchange, verify) {
|
|
1023
|
+
const content = x12Peer().serializeX12(interchange);
|
|
1024
|
+
if (!verify) return { content, warnings: UNCHECKED };
|
|
1025
|
+
return {
|
|
1026
|
+
content,
|
|
1027
|
+
warnings: x12Peer().parseX12(content).warnings.map((w) => String(w.code))
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
934
1030
|
|
|
935
1031
|
// src/x12/index.ts
|
|
936
1032
|
var ALL_KINDS = Object.freeze(["837P", "837I", "837D", "835", "271"]);
|
|
@@ -938,26 +1034,26 @@ var DEFAULT_MIX = ALL_KINDS;
|
|
|
938
1034
|
function generateKind(kind, seed) {
|
|
939
1035
|
switch (kind) {
|
|
940
1036
|
case "835":
|
|
941
|
-
return
|
|
1037
|
+
return generate835({ seed });
|
|
942
1038
|
case "271":
|
|
943
|
-
return
|
|
1039
|
+
return generate271({ seed });
|
|
944
1040
|
case "837P":
|
|
945
|
-
return
|
|
1041
|
+
return generate837("P", { seed });
|
|
946
1042
|
case "837I":
|
|
947
|
-
return
|
|
1043
|
+
return generate837("I", { seed });
|
|
948
1044
|
case "837D":
|
|
949
|
-
return
|
|
1045
|
+
return generate837("D", { seed });
|
|
950
1046
|
}
|
|
951
1047
|
}
|
|
952
1048
|
function x12Corpus(options) {
|
|
953
|
-
const { seed } = options;
|
|
1049
|
+
const { seed, verify = true } = options;
|
|
954
1050
|
const mix = resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
955
1051
|
const count = options.count ?? mix.length;
|
|
956
1052
|
const seedStream = createRng(seed);
|
|
957
1053
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
958
1054
|
const kind = mix[i % mix.length] ?? "837P";
|
|
959
1055
|
const txSeed = seedStream.nextUint32();
|
|
960
|
-
const rt = generateKind(kind, txSeed);
|
|
1056
|
+
const rt = x12CorpusArtifact(generateKind(kind, txSeed), verify);
|
|
961
1057
|
return {
|
|
962
1058
|
format: "x12",
|
|
963
1059
|
kind,
|