@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/hl7/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) {
|
|
@@ -32,17 +41,36 @@ function sfc32Next(s) {
|
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
// src/codes.ts
|
|
35
|
-
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",
|
|
36
54
|
/**
|
|
37
55
|
* A vendor quirk was requested that the target format's profile system does not support. Fatal,
|
|
38
56
|
* never a silent no-op and never a fabricated quirk.
|
|
39
57
|
*/
|
|
40
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",
|
|
41
65
|
/**
|
|
42
66
|
* A bare parse of a freshly-generated quirk artifact did not produce exactly the declared intended
|
|
43
67
|
* warning code(s). Fatal, never emit a mislabeled fixture.
|
|
44
68
|
*/
|
|
45
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",
|
|
46
74
|
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
47
75
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
48
76
|
/** A value was drawn from an empty pool. Fatal, never a fabricated substitute. */
|
|
@@ -55,7 +83,17 @@ var SYNTH_FATAL_CODES = {
|
|
|
55
83
|
* see `resolveKind`: a selector union is erased at run time, and a selector that falls through
|
|
56
84
|
* either mislabels the fixture or hands the value to a peer builder that quotes it back.
|
|
57
85
|
*/
|
|
58
|
-
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
|
+
});
|
|
59
97
|
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
60
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.",
|
|
61
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).",
|
|
@@ -121,6 +159,7 @@ function createRng(seed) {
|
|
|
121
159
|
}
|
|
122
160
|
|
|
123
161
|
// src/corpus.ts
|
|
162
|
+
var UNCHECKED = Object.freeze([]);
|
|
124
163
|
function makeCorpus(seed, artifacts, quirks = []) {
|
|
125
164
|
const counts = {};
|
|
126
165
|
const formats = /* @__PURE__ */ new Set();
|
|
@@ -432,6 +471,30 @@ var safe = Object.freeze({
|
|
|
432
471
|
npi,
|
|
433
472
|
dea
|
|
434
473
|
});
|
|
474
|
+
function defaultHost() {
|
|
475
|
+
if (typeof undefined !== "function") return { require: __require, resolve: void 0 };
|
|
476
|
+
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) };
|
|
477
|
+
}
|
|
478
|
+
function isRequireEsmRefusal(error) {
|
|
479
|
+
return error?.code === "ERR_REQUIRE_ESM";
|
|
480
|
+
}
|
|
481
|
+
function loadPeer(specifier, host) {
|
|
482
|
+
if (host.resolve === void 0) return host.require(specifier);
|
|
483
|
+
const entry = url.fileURLToPath(host.resolve(specifier));
|
|
484
|
+
try {
|
|
485
|
+
return host.require(entry);
|
|
486
|
+
} catch (error) {
|
|
487
|
+
if (!isRequireEsmRefusal(error)) throw error;
|
|
488
|
+
return host.require(specifier);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function lazyPeer(specifier, host) {
|
|
492
|
+
let loaded;
|
|
493
|
+
return () => loaded ??= loadPeer(specifier, defaultHost());
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/peers.ts
|
|
497
|
+
var hl7Peer = lazyPeer("@cosyte/hl7");
|
|
435
498
|
|
|
436
499
|
// src/hl7/common.ts
|
|
437
500
|
function seededTimestamp(rng) {
|
|
@@ -444,7 +507,7 @@ function seededTimestamp(rng) {
|
|
|
444
507
|
function mshScaffold(rng, type) {
|
|
445
508
|
const timestamp = seededTimestamp(rng);
|
|
446
509
|
const controlId = `SYNTH${rng.digits(10)}`;
|
|
447
|
-
const message =
|
|
510
|
+
const message = hl7Peer().buildMessage({
|
|
448
511
|
type,
|
|
449
512
|
sendingApp: "COSYTE-SYNTH",
|
|
450
513
|
sendingFacility: "SYNTH-FAC",
|
|
@@ -571,6 +634,7 @@ function obxSegment(rng, setId) {
|
|
|
571
634
|
const obs = rng.pick(EXAMPLE_LAB_OBSERVATIONS);
|
|
572
635
|
const value = `${String(rng.int(1, 300))}.${String(rng.int(0, 9))}`;
|
|
573
636
|
const observedAt = seededTimestamp(rng);
|
|
637
|
+
const units = obs.units ?? "";
|
|
574
638
|
return [
|
|
575
639
|
String(setId),
|
|
576
640
|
// OBX-1 set id
|
|
@@ -582,7 +646,7 @@ function obxSegment(rng, setId) {
|
|
|
582
646
|
// OBX-4 observation sub-id
|
|
583
647
|
value,
|
|
584
648
|
// OBX-5 observation value
|
|
585
|
-
|
|
649
|
+
units,
|
|
586
650
|
// OBX-6 units
|
|
587
651
|
"",
|
|
588
652
|
// OBX-7 reference range
|
|
@@ -796,9 +860,11 @@ function generateVxu(options = {}) {
|
|
|
796
860
|
]);
|
|
797
861
|
return message;
|
|
798
862
|
}
|
|
863
|
+
|
|
864
|
+
// src/hl7/round-trip.ts
|
|
799
865
|
function roundTrip(message) {
|
|
800
866
|
const content = message.toString();
|
|
801
|
-
const reparsed =
|
|
867
|
+
const reparsed = hl7Peer().parseHL7(content);
|
|
802
868
|
const warnings = reparsed.warnings.map((w) => String(w.code));
|
|
803
869
|
const byteStable = reparsed.toString() === content;
|
|
804
870
|
return {
|
|
@@ -808,6 +874,14 @@ function roundTrip(message) {
|
|
|
808
874
|
specClean: warnings.length === 0 && byteStable
|
|
809
875
|
};
|
|
810
876
|
}
|
|
877
|
+
function hl7CorpusArtifact(message, verify) {
|
|
878
|
+
const content = message.toString();
|
|
879
|
+
if (!verify) return { content, warnings: UNCHECKED };
|
|
880
|
+
return {
|
|
881
|
+
content,
|
|
882
|
+
warnings: hl7Peer().parseHL7(content).warnings.map((w) => String(w.code))
|
|
883
|
+
};
|
|
884
|
+
}
|
|
811
885
|
|
|
812
886
|
// src/profile.ts
|
|
813
887
|
function defineSynthProfile(spec) {
|
|
@@ -883,7 +957,7 @@ var HL7_QUIRKS = Object.freeze({
|
|
|
883
957
|
})
|
|
884
958
|
});
|
|
885
959
|
function toleratingProfile(quirk) {
|
|
886
|
-
return quirk === "unknown-zsegment" ?
|
|
960
|
+
return quirk === "unknown-zsegment" ? hl7Peer().profiles.visage : void 0;
|
|
887
961
|
}
|
|
888
962
|
function segmentsOf(wire) {
|
|
889
963
|
return wire.split(/\r\n|\r|\n/).filter((s) => s.length > 0);
|
|
@@ -931,7 +1005,7 @@ function generateHl7Quirk(options) {
|
|
|
931
1005
|
const content = applyQuirk(options.quirk, baseMessage(kind, seed).toString());
|
|
932
1006
|
assertIntendedWarnings(
|
|
933
1007
|
descriptor.intendedWarnings,
|
|
934
|
-
|
|
1008
|
+
hl7Peer().parseHL7(content).warnings.map((w) => String(w.code))
|
|
935
1009
|
);
|
|
936
1010
|
return Object.freeze({
|
|
937
1011
|
format: "hl7v2",
|
|
@@ -944,12 +1018,12 @@ function generateHl7Quirk(options) {
|
|
|
944
1018
|
function hl7QuirkRoundTrip(artifact) {
|
|
945
1019
|
const quirk = artifact.quirk;
|
|
946
1020
|
const descriptor = resolveQuirk(HL7_QUIRKS, "hl7v2", quirk);
|
|
947
|
-
const bare =
|
|
1021
|
+
const bare = hl7Peer().parseHL7(artifact.content).warnings.map((w) => String(w.code));
|
|
948
1022
|
const profile = toleratingProfile(quirk);
|
|
949
1023
|
const withProfile = profile !== void 0 && descriptor.toleratingProfile !== void 0 ? {
|
|
950
1024
|
profileName: descriptor.toleratingProfile,
|
|
951
1025
|
disposition: descriptor.disposition,
|
|
952
|
-
warnings:
|
|
1026
|
+
warnings: hl7Peer().parseHL7(artifact.content, profile).warnings.map((w) => String(w.code)),
|
|
953
1027
|
tolerated: false
|
|
954
1028
|
} : void 0;
|
|
955
1029
|
return {
|
|
@@ -1028,7 +1102,7 @@ function generateHl7(kind, seed) {
|
|
|
1028
1102
|
}
|
|
1029
1103
|
}
|
|
1030
1104
|
function hl7Corpus(options) {
|
|
1031
|
-
const { seed, count = 1 } = options;
|
|
1105
|
+
const { seed, count = 1, verify = true } = options;
|
|
1032
1106
|
const kinds = options.triggers !== void 0 ? options.triggers.map(
|
|
1033
1107
|
(t) => `ADT^${resolveKind(ADT_TRIGGERS2, t)}`
|
|
1034
1108
|
) : resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
@@ -1036,7 +1110,7 @@ function hl7Corpus(options) {
|
|
|
1036
1110
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
1037
1111
|
const kind = kinds[i % kinds.length] ?? "ADT^A01";
|
|
1038
1112
|
const messageSeed = seedStream.nextUint32();
|
|
1039
|
-
const rt =
|
|
1113
|
+
const rt = hl7CorpusArtifact(generateHl7(kind, messageSeed), verify);
|
|
1040
1114
|
return {
|
|
1041
1115
|
format: "hl7v2",
|
|
1042
1116
|
kind,
|