@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.d.cts
CHANGED
|
@@ -527,6 +527,12 @@ interface Hl7CorpusOptions {
|
|
|
527
527
|
* supplied it takes precedence over `mix` and restricts the corpus to `ADT` messages.
|
|
528
528
|
*/
|
|
529
529
|
readonly triggers?: readonly AdtTrigger[];
|
|
530
|
+
/**
|
|
531
|
+
* Whether each artifact is read back through its parser and the parser's findings recorded as its
|
|
532
|
+
* `warnings`. Defaults to `true`. `false` skips the read, a large share of a corpus's time; every
|
|
533
|
+
* artifact's `warnings` is then empty without having been checked.
|
|
534
|
+
*/
|
|
535
|
+
readonly verify?: boolean;
|
|
530
536
|
}
|
|
531
537
|
/**
|
|
532
538
|
* Build a reproducible {@link Corpus} of spec-clean HL7 messages across the families. Each
|
package/dist/hl7/index.d.ts
CHANGED
|
@@ -527,6 +527,12 @@ interface Hl7CorpusOptions {
|
|
|
527
527
|
* supplied it takes precedence over `mix` and restricts the corpus to `ADT` messages.
|
|
528
528
|
*/
|
|
529
529
|
readonly triggers?: readonly AdtTrigger[];
|
|
530
|
+
/**
|
|
531
|
+
* Whether each artifact is read back through its parser and the parser's findings recorded as its
|
|
532
|
+
* `warnings`. Defaults to `true`. `false` skips the read, a large share of a corpus's time; every
|
|
533
|
+
* artifact's `warnings` is then empty without having been checked.
|
|
534
|
+
*/
|
|
535
|
+
readonly verify?: boolean;
|
|
530
536
|
}
|
|
531
537
|
/**
|
|
532
538
|
* Build a reproducible {@link Corpus} of spec-clean HL7 messages across the families. Each
|
package/dist/hl7/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,17 +38,36 @@ 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.
|
|
37
54
|
*/
|
|
38
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",
|
|
39
62
|
/**
|
|
40
63
|
* A bare parse of a freshly-generated quirk artifact did not produce exactly the declared intended
|
|
41
64
|
* warning code(s). Fatal, never emit a mislabeled fixture.
|
|
42
65
|
*/
|
|
43
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",
|
|
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",
|
|
44
71
|
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
45
72
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
46
73
|
/** A value was drawn from an empty pool. Fatal, never a fabricated substitute. */
|
|
@@ -53,7 +80,17 @@ var SYNTH_FATAL_CODES = {
|
|
|
53
80
|
* see `resolveKind`: a selector union is erased at run time, and a selector that falls through
|
|
54
81
|
* either mislabels the fixture or hands the value to a peer builder that quotes it back.
|
|
55
82
|
*/
|
|
56
|
-
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
|
+
});
|
|
57
94
|
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
58
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.",
|
|
59
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).",
|
|
@@ -119,6 +156,7 @@ function createRng(seed) {
|
|
|
119
156
|
}
|
|
120
157
|
|
|
121
158
|
// src/corpus.ts
|
|
159
|
+
var UNCHECKED = Object.freeze([]);
|
|
122
160
|
function makeCorpus(seed, artifacts, quirks = []) {
|
|
123
161
|
const counts = {};
|
|
124
162
|
const formats = /* @__PURE__ */ new Set();
|
|
@@ -430,6 +468,30 @@ var safe = Object.freeze({
|
|
|
430
468
|
npi,
|
|
431
469
|
dea
|
|
432
470
|
});
|
|
471
|
+
function defaultHost() {
|
|
472
|
+
if (typeof import.meta.resolve !== "function") return { require: __require, resolve: void 0 };
|
|
473
|
+
return { require: createRequire(import.meta.url), resolve: (s) => import.meta.resolve(s) };
|
|
474
|
+
}
|
|
475
|
+
function isRequireEsmRefusal(error) {
|
|
476
|
+
return error?.code === "ERR_REQUIRE_ESM";
|
|
477
|
+
}
|
|
478
|
+
function loadPeer(specifier, host) {
|
|
479
|
+
if (host.resolve === void 0) return host.require(specifier);
|
|
480
|
+
const entry = fileURLToPath(host.resolve(specifier));
|
|
481
|
+
try {
|
|
482
|
+
return host.require(entry);
|
|
483
|
+
} catch (error) {
|
|
484
|
+
if (!isRequireEsmRefusal(error)) throw error;
|
|
485
|
+
return host.require(specifier);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function lazyPeer(specifier, host) {
|
|
489
|
+
let loaded;
|
|
490
|
+
return () => loaded ??= loadPeer(specifier, defaultHost());
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// src/peers.ts
|
|
494
|
+
var hl7Peer = lazyPeer("@cosyte/hl7");
|
|
433
495
|
|
|
434
496
|
// src/hl7/common.ts
|
|
435
497
|
function seededTimestamp(rng) {
|
|
@@ -442,7 +504,7 @@ function seededTimestamp(rng) {
|
|
|
442
504
|
function mshScaffold(rng, type) {
|
|
443
505
|
const timestamp = seededTimestamp(rng);
|
|
444
506
|
const controlId = `SYNTH${rng.digits(10)}`;
|
|
445
|
-
const message = buildMessage({
|
|
507
|
+
const message = hl7Peer().buildMessage({
|
|
446
508
|
type,
|
|
447
509
|
sendingApp: "COSYTE-SYNTH",
|
|
448
510
|
sendingFacility: "SYNTH-FAC",
|
|
@@ -569,6 +631,7 @@ function obxSegment(rng, setId) {
|
|
|
569
631
|
const obs = rng.pick(EXAMPLE_LAB_OBSERVATIONS);
|
|
570
632
|
const value = `${String(rng.int(1, 300))}.${String(rng.int(0, 9))}`;
|
|
571
633
|
const observedAt = seededTimestamp(rng);
|
|
634
|
+
const units = obs.units ?? "";
|
|
572
635
|
return [
|
|
573
636
|
String(setId),
|
|
574
637
|
// OBX-1 set id
|
|
@@ -580,7 +643,7 @@ function obxSegment(rng, setId) {
|
|
|
580
643
|
// OBX-4 observation sub-id
|
|
581
644
|
value,
|
|
582
645
|
// OBX-5 observation value
|
|
583
|
-
|
|
646
|
+
units,
|
|
584
647
|
// OBX-6 units
|
|
585
648
|
"",
|
|
586
649
|
// OBX-7 reference range
|
|
@@ -794,9 +857,11 @@ function generateVxu(options = {}) {
|
|
|
794
857
|
]);
|
|
795
858
|
return message;
|
|
796
859
|
}
|
|
860
|
+
|
|
861
|
+
// src/hl7/round-trip.ts
|
|
797
862
|
function roundTrip(message) {
|
|
798
863
|
const content = message.toString();
|
|
799
|
-
const reparsed = parseHL7(content);
|
|
864
|
+
const reparsed = hl7Peer().parseHL7(content);
|
|
800
865
|
const warnings = reparsed.warnings.map((w) => String(w.code));
|
|
801
866
|
const byteStable = reparsed.toString() === content;
|
|
802
867
|
return {
|
|
@@ -806,6 +871,14 @@ function roundTrip(message) {
|
|
|
806
871
|
specClean: warnings.length === 0 && byteStable
|
|
807
872
|
};
|
|
808
873
|
}
|
|
874
|
+
function hl7CorpusArtifact(message, verify) {
|
|
875
|
+
const content = message.toString();
|
|
876
|
+
if (!verify) return { content, warnings: UNCHECKED };
|
|
877
|
+
return {
|
|
878
|
+
content,
|
|
879
|
+
warnings: hl7Peer().parseHL7(content).warnings.map((w) => String(w.code))
|
|
880
|
+
};
|
|
881
|
+
}
|
|
809
882
|
|
|
810
883
|
// src/profile.ts
|
|
811
884
|
function defineSynthProfile(spec) {
|
|
@@ -881,7 +954,7 @@ var HL7_QUIRKS = Object.freeze({
|
|
|
881
954
|
})
|
|
882
955
|
});
|
|
883
956
|
function toleratingProfile(quirk) {
|
|
884
|
-
return quirk === "unknown-zsegment" ? profiles.visage : void 0;
|
|
957
|
+
return quirk === "unknown-zsegment" ? hl7Peer().profiles.visage : void 0;
|
|
885
958
|
}
|
|
886
959
|
function segmentsOf(wire) {
|
|
887
960
|
return wire.split(/\r\n|\r|\n/).filter((s) => s.length > 0);
|
|
@@ -929,7 +1002,7 @@ function generateHl7Quirk(options) {
|
|
|
929
1002
|
const content = applyQuirk(options.quirk, baseMessage(kind, seed).toString());
|
|
930
1003
|
assertIntendedWarnings(
|
|
931
1004
|
descriptor.intendedWarnings,
|
|
932
|
-
parseHL7(content).warnings.map((w) => String(w.code))
|
|
1005
|
+
hl7Peer().parseHL7(content).warnings.map((w) => String(w.code))
|
|
933
1006
|
);
|
|
934
1007
|
return Object.freeze({
|
|
935
1008
|
format: "hl7v2",
|
|
@@ -942,12 +1015,12 @@ function generateHl7Quirk(options) {
|
|
|
942
1015
|
function hl7QuirkRoundTrip(artifact) {
|
|
943
1016
|
const quirk = artifact.quirk;
|
|
944
1017
|
const descriptor = resolveQuirk(HL7_QUIRKS, "hl7v2", quirk);
|
|
945
|
-
const bare = parseHL7(artifact.content).warnings.map((w) => String(w.code));
|
|
1018
|
+
const bare = hl7Peer().parseHL7(artifact.content).warnings.map((w) => String(w.code));
|
|
946
1019
|
const profile = toleratingProfile(quirk);
|
|
947
1020
|
const withProfile = profile !== void 0 && descriptor.toleratingProfile !== void 0 ? {
|
|
948
1021
|
profileName: descriptor.toleratingProfile,
|
|
949
1022
|
disposition: descriptor.disposition,
|
|
950
|
-
warnings: parseHL7(artifact.content, profile).warnings.map((w) => String(w.code)),
|
|
1023
|
+
warnings: hl7Peer().parseHL7(artifact.content, profile).warnings.map((w) => String(w.code)),
|
|
951
1024
|
tolerated: false
|
|
952
1025
|
} : void 0;
|
|
953
1026
|
return {
|
|
@@ -1026,7 +1099,7 @@ function generateHl7(kind, seed) {
|
|
|
1026
1099
|
}
|
|
1027
1100
|
}
|
|
1028
1101
|
function hl7Corpus(options) {
|
|
1029
|
-
const { seed, count = 1 } = options;
|
|
1102
|
+
const { seed, count = 1, verify = true } = options;
|
|
1030
1103
|
const kinds = options.triggers !== void 0 ? options.triggers.map(
|
|
1031
1104
|
(t) => `ADT^${resolveKind(ADT_TRIGGERS2, t)}`
|
|
1032
1105
|
) : resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
@@ -1034,7 +1107,7 @@ function hl7Corpus(options) {
|
|
|
1034
1107
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
1035
1108
|
const kind = kinds[i % kinds.length] ?? "ADT^A01";
|
|
1036
1109
|
const messageSeed = seedStream.nextUint32();
|
|
1037
|
-
const rt =
|
|
1110
|
+
const rt = hl7CorpusArtifact(generateHl7(kind, messageSeed), verify);
|
|
1038
1111
|
return {
|
|
1039
1112
|
format: "hl7v2",
|
|
1040
1113
|
kind,
|