@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/ccda/index.d.cts
CHANGED
|
@@ -362,6 +362,12 @@ interface CcdaCorpusOptions {
|
|
|
362
362
|
readonly count?: number;
|
|
363
363
|
/** The document types to cycle through. Defaults to one CCD + one Referral Note. */
|
|
364
364
|
readonly mix?: readonly CcdaCorpusKind[];
|
|
365
|
+
/**
|
|
366
|
+
* Whether each artifact is read back through its parser and the parser's findings recorded as its
|
|
367
|
+
* `warnings`. Defaults to `true`. `false` skips the read, a large share of a corpus's time; every
|
|
368
|
+
* artifact's `warnings` is then empty without having been checked.
|
|
369
|
+
*/
|
|
370
|
+
readonly verify?: boolean;
|
|
365
371
|
}
|
|
366
372
|
/**
|
|
367
373
|
* Build a reproducible {@link Corpus} of spec-clean C-CDA documents. Each document is generated from a
|
package/dist/ccda/index.d.ts
CHANGED
|
@@ -362,6 +362,12 @@ interface CcdaCorpusOptions {
|
|
|
362
362
|
readonly count?: number;
|
|
363
363
|
/** The document types to cycle through. Defaults to one CCD + one Referral Note. */
|
|
364
364
|
readonly mix?: readonly CcdaCorpusKind[];
|
|
365
|
+
/**
|
|
366
|
+
* Whether each artifact is read back through its parser and the parser's findings recorded as its
|
|
367
|
+
* `warnings`. Defaults to `true`. `false` skips the read, a large share of a corpus's time; every
|
|
368
|
+
* artifact's `warnings` is then empty without having been checked.
|
|
369
|
+
*/
|
|
370
|
+
readonly verify?: boolean;
|
|
365
371
|
}
|
|
366
372
|
/**
|
|
367
373
|
* Build a reproducible {@link Corpus} of spec-clean C-CDA documents. Each document is generated from a
|
package/dist/ccda/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,7 +38,16 @@ 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",
|
|
34
51
|
/**
|
|
35
52
|
* A vendor quirk was requested that the target format's profile system does not support. Fatal,
|
|
36
53
|
* never a silent no-op and never a fabricated quirk.
|
|
@@ -49,6 +66,8 @@ var SYNTH_FATAL_CODES = {
|
|
|
49
66
|
SYNTH_INTENDED_WARNING_MISMATCH: "SYNTH_INTENDED_WARNING_MISMATCH",
|
|
50
67
|
/** A concept's code-system URI has no OID mapping in the C-CDA example-code table. Fatal. */
|
|
51
68
|
SYNTH_UNMAPPED_CODE_SYSTEM: "SYNTH_UNMAPPED_CODE_SYSTEM",
|
|
69
|
+
/** A money value could not be read as an X12 decimal. Fatal: a generator never rounds to a float. */
|
|
70
|
+
SYNTH_INVALID_DECIMAL: "SYNTH_INVALID_DECIMAL",
|
|
52
71
|
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
53
72
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
54
73
|
/** A value was drawn from an empty pool. Fatal, never a fabricated substitute. */
|
|
@@ -61,7 +80,17 @@ var SYNTH_FATAL_CODES = {
|
|
|
61
80
|
* see `resolveKind`: a selector union is erased at run time, and a selector that falls through
|
|
62
81
|
* either mislabels the fixture or hands the value to a peer builder that quotes it back.
|
|
63
82
|
*/
|
|
64
|
-
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
|
+
});
|
|
65
94
|
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
66
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.",
|
|
67
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).",
|
|
@@ -127,6 +156,7 @@ function createRng(seed) {
|
|
|
127
156
|
}
|
|
128
157
|
|
|
129
158
|
// src/corpus.ts
|
|
159
|
+
var UNCHECKED = Object.freeze([]);
|
|
130
160
|
function makeCorpus(seed, artifacts, quirks = []) {
|
|
131
161
|
const counts = {};
|
|
132
162
|
const formats = /* @__PURE__ */ new Set();
|
|
@@ -737,6 +767,16 @@ Object.freeze([
|
|
|
737
767
|
]);
|
|
738
768
|
|
|
739
769
|
// src/ccda/example-codes.ts
|
|
770
|
+
var LOINC_OID = "2.16.840.1.113883.6.1";
|
|
771
|
+
var SNOMED_CT_OID = "2.16.840.1.113883.6.96";
|
|
772
|
+
var RXNORM_OID = "2.16.840.1.113883.6.88";
|
|
773
|
+
var CVX_OID = "2.16.840.1.113883.12.292";
|
|
774
|
+
var NCI_ROUTE_OID = "2.16.840.1.113883.3.26.1.1";
|
|
775
|
+
var LOINC = LOINC_OID;
|
|
776
|
+
var SNOMED_CT = SNOMED_CT_OID;
|
|
777
|
+
var RXNORM = RXNORM_OID;
|
|
778
|
+
var CVX = CVX_OID;
|
|
779
|
+
var NCI_ROUTE = NCI_ROUTE_OID;
|
|
740
780
|
var URI_TO_OID = Object.freeze({
|
|
741
781
|
"http://loinc.org": LOINC,
|
|
742
782
|
"http://snomed.info/sct": SNOMED_CT,
|
|
@@ -813,6 +853,28 @@ function resolveMix(allowed, requested, fallback) {
|
|
|
813
853
|
if (requested === void 0) return fallback;
|
|
814
854
|
return requested.map((entry) => resolveKind(allowed, entry));
|
|
815
855
|
}
|
|
856
|
+
function defaultHost() {
|
|
857
|
+
if (typeof import.meta.resolve !== "function") return { require: __require, resolve: void 0 };
|
|
858
|
+
return { require: createRequire(import.meta.url), resolve: (s) => import.meta.resolve(s) };
|
|
859
|
+
}
|
|
860
|
+
function isRequireEsmRefusal(error) {
|
|
861
|
+
return error?.code === "ERR_REQUIRE_ESM";
|
|
862
|
+
}
|
|
863
|
+
function loadPeer(specifier, host) {
|
|
864
|
+
if (host.resolve === void 0) return host.require(specifier);
|
|
865
|
+
const entry = fileURLToPath(host.resolve(specifier));
|
|
866
|
+
try {
|
|
867
|
+
return host.require(entry);
|
|
868
|
+
} catch (error) {
|
|
869
|
+
if (!isRequireEsmRefusal(error)) throw error;
|
|
870
|
+
return host.require(specifier);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
function lazyPeer(specifier, host) {
|
|
874
|
+
let loaded;
|
|
875
|
+
return () => loaded ??= loadPeer(specifier, defaultHost());
|
|
876
|
+
}
|
|
877
|
+
var ccdaPeer = lazyPeer("@cosyte/ccda");
|
|
816
878
|
|
|
817
879
|
// src/ccda/ccd.ts
|
|
818
880
|
var CCDA_DOCUMENT_TYPES = Object.freeze(["ccd", "referralNote"]);
|
|
@@ -901,7 +963,7 @@ function generateCcda(options = {}) {
|
|
|
901
963
|
const { seed = 0 } = options;
|
|
902
964
|
const documentType = resolveKind(CCDA_DOCUMENT_TYPES, options.documentType ?? "ccd");
|
|
903
965
|
const rng = createRng(seed);
|
|
904
|
-
return buildCcda(buildInit(rng, documentType));
|
|
966
|
+
return ccdaPeer().buildCcda(buildInit(rng, documentType));
|
|
905
967
|
}
|
|
906
968
|
function generateCcd(options = {}) {
|
|
907
969
|
return generateCcda({ ...options, documentType: "ccd" });
|
|
@@ -909,11 +971,13 @@ function generateCcd(options = {}) {
|
|
|
909
971
|
function generateReferralNote(options = {}) {
|
|
910
972
|
return generateCcda({ ...options, documentType: "referralNote" });
|
|
911
973
|
}
|
|
974
|
+
|
|
975
|
+
// src/ccda/round-trip.ts
|
|
912
976
|
function roundTrip(doc) {
|
|
913
|
-
const content = serializeCcda(doc);
|
|
914
|
-
const reparsed = parseCcda(content);
|
|
977
|
+
const content = ccdaPeer().serializeCcda(doc);
|
|
978
|
+
const reparsed = ccdaPeer().parseCcda(content);
|
|
915
979
|
const warnings = reparsed.warnings.map((w) => String(w.code));
|
|
916
|
-
const byteStable = serializeCcda(reparsed) === content;
|
|
980
|
+
const byteStable = ccdaPeer().serializeCcda(reparsed) === content;
|
|
917
981
|
return {
|
|
918
982
|
content,
|
|
919
983
|
warnings,
|
|
@@ -921,6 +985,14 @@ function roundTrip(doc) {
|
|
|
921
985
|
specClean: warnings.length === 0 && byteStable
|
|
922
986
|
};
|
|
923
987
|
}
|
|
988
|
+
function ccdaCorpusArtifact(doc, verify) {
|
|
989
|
+
const content = ccdaPeer().serializeCcda(doc);
|
|
990
|
+
if (!verify) return { content, warnings: UNCHECKED };
|
|
991
|
+
return {
|
|
992
|
+
content,
|
|
993
|
+
warnings: ccdaPeer().parseCcda(content).warnings.map((w) => String(w.code))
|
|
994
|
+
};
|
|
995
|
+
}
|
|
924
996
|
|
|
925
997
|
// src/profile.ts
|
|
926
998
|
function defineSynthProfile(spec) {
|
|
@@ -1005,7 +1077,7 @@ var CCDA_QUIRKS = Object.freeze({
|
|
|
1005
1077
|
})
|
|
1006
1078
|
});
|
|
1007
1079
|
function toleratingProfile(quirk) {
|
|
1008
|
-
return quirk === "template-extension-absent" ? ccdaProfiles.legacyR11 : ccdaProfiles.smartScorecard;
|
|
1080
|
+
return quirk === "template-extension-absent" ? ccdaPeer().ccdaProfiles.legacyR11 : ccdaPeer().ccdaProfiles.smartScorecard;
|
|
1009
1081
|
}
|
|
1010
1082
|
var DOC_TEMPLATE_ROOTS = [
|
|
1011
1083
|
"2.16.840.1.113883.10.20.22.1.1",
|
|
@@ -1045,11 +1117,11 @@ function generateCcdaQuirk(options) {
|
|
|
1045
1117
|
const seed = options.seed ?? 0;
|
|
1046
1118
|
const documentType = options.documentType ?? "ccd";
|
|
1047
1119
|
const descriptor = resolveQuirk(CCDA_QUIRKS, "ccda", options.quirk);
|
|
1048
|
-
const clean = serializeCcda(generateCcda({ seed, documentType }));
|
|
1120
|
+
const clean = ccdaPeer().serializeCcda(generateCcda({ seed, documentType }));
|
|
1049
1121
|
const content = injectCcdaQuirk(options.quirk, clean);
|
|
1050
1122
|
assertIntendedWarnings(
|
|
1051
1123
|
descriptor.intendedWarnings,
|
|
1052
|
-
parseCcda(content).warnings.map((w) => String(w.code))
|
|
1124
|
+
ccdaPeer().parseCcda(content).warnings.map((w) => String(w.code))
|
|
1053
1125
|
);
|
|
1054
1126
|
return Object.freeze({
|
|
1055
1127
|
format: "ccda",
|
|
@@ -1062,17 +1134,16 @@ function generateCcdaQuirk(options) {
|
|
|
1062
1134
|
function ccdaQuirkRoundTrip(artifact) {
|
|
1063
1135
|
const quirk = artifact.quirk;
|
|
1064
1136
|
const descriptor = resolveQuirk(CCDA_QUIRKS, "ccda", quirk);
|
|
1065
|
-
const bare = parseCcda(artifact.content).warnings.map((w) => String(w.code));
|
|
1137
|
+
const bare = ccdaPeer().parseCcda(artifact.content).warnings.map((w) => String(w.code));
|
|
1066
1138
|
const profile = toleratingProfile(quirk);
|
|
1067
|
-
const profiledWarnings = parseCcda(artifact.content, { profile }).warnings.map(
|
|
1068
|
-
(w) => String(w.code)
|
|
1069
|
-
);
|
|
1139
|
+
const profiledWarnings = ccdaPeer().parseCcda(artifact.content, { profile }).warnings.map((w) => String(w.code));
|
|
1070
1140
|
return {
|
|
1071
1141
|
content: artifact.content,
|
|
1072
1142
|
warnings: bare,
|
|
1073
1143
|
intendedWarnings: artifact.intendedWarnings,
|
|
1074
1144
|
intendedWarningHeld: sameCodeSet(bare, artifact.intendedWarnings),
|
|
1075
1145
|
withProfile: {
|
|
1146
|
+
/* v8 ignore next -- the `?? profile.name` is unreachable: every descriptor in the frozen CCDA_QUIRKS names its toleratingProfile */
|
|
1076
1147
|
profileName: descriptor.toleratingProfile ?? profile.name,
|
|
1077
1148
|
disposition: descriptor.disposition,
|
|
1078
1149
|
warnings: profiledWarnings,
|
|
@@ -1116,13 +1187,13 @@ var ccdaQuirkProfile = defineSynthProfile({
|
|
|
1116
1187
|
var ALL_KINDS = Object.freeze(["ccd", "referralNote"]);
|
|
1117
1188
|
var DEFAULT_MIX = ALL_KINDS;
|
|
1118
1189
|
function ccdaCorpus(options) {
|
|
1119
|
-
const { seed, count = 1 } = options;
|
|
1190
|
+
const { seed, count = 1, verify = true } = options;
|
|
1120
1191
|
const mix = resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
1121
1192
|
const seedStream = createRng(seed);
|
|
1122
1193
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
1123
1194
|
const documentType = mix[i % mix.length] ?? "ccd";
|
|
1124
1195
|
const docSeed = seedStream.nextUint32();
|
|
1125
|
-
const rt =
|
|
1196
|
+
const rt = ccdaCorpusArtifact(generateCcda({ seed: docSeed, documentType }), verify);
|
|
1126
1197
|
return {
|
|
1127
1198
|
format: "ccda",
|
|
1128
1199
|
kind: documentType,
|