@cosyte/synth 0.0.2 → 0.0.4
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 +321 -0
- package/README.md +13 -4
- package/dist/astm/index.cjs +83 -42
- package/dist/astm/index.cjs.map +1 -1
- package/dist/astm/index.d.cts +1 -1
- package/dist/astm/index.d.ts +1 -1
- package/dist/astm/index.mjs +83 -42
- package/dist/astm/index.mjs.map +1 -1
- package/dist/ccda/index.cjs +89 -44
- package/dist/ccda/index.cjs.map +1 -1
- package/dist/ccda/index.d.cts +5 -3
- package/dist/ccda/index.d.ts +5 -3
- package/dist/ccda/index.mjs +89 -44
- package/dist/ccda/index.mjs.map +1 -1
- package/dist/deid/index.cjs +92 -13
- package/dist/deid/index.cjs.map +1 -1
- package/dist/deid/index.d.cts +1 -1
- package/dist/deid/index.d.ts +1 -1
- package/dist/deid/index.mjs +93 -14
- package/dist/deid/index.mjs.map +1 -1
- package/dist/fhir/index.cjs +78 -5
- package/dist/fhir/index.cjs.map +1 -1
- package/dist/fhir/index.mjs +78 -5
- package/dist/fhir/index.mjs.map +1 -1
- package/dist/hl7/index.cjs +89 -41
- package/dist/hl7/index.cjs.map +1 -1
- package/dist/hl7/index.d.cts +1 -1
- package/dist/hl7/index.d.ts +1 -1
- package/dist/hl7/index.mjs +89 -41
- package/dist/hl7/index.mjs.map +1 -1
- package/dist/index.cjs +91 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +126 -8
- package/dist/index.d.ts +126 -8
- package/dist/index.mjs +89 -41
- package/dist/index.mjs.map +1 -1
- package/dist/ncpdp/index.cjs +56 -4
- package/dist/ncpdp/index.cjs.map +1 -1
- package/dist/ncpdp/index.mjs +56 -4
- package/dist/ncpdp/index.mjs.map +1 -1
- package/dist/{quirk-JLyO1Ncj.d.ts → quirk-C9t9CkPS.d.ts} +17 -6
- package/dist/{quirk-DmkgoZdh.d.cts → quirk-DYMDojVw.d.cts} +17 -6
- package/dist/x12/index.cjs +82 -13
- package/dist/x12/index.cjs.map +1 -1
- package/dist/x12/index.d.cts +2 -1
- package/dist/x12/index.d.ts +2 -1
- package/dist/x12/index.mjs +83 -14
- package/dist/x12/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/hl7/index.cjs
CHANGED
|
@@ -31,6 +31,57 @@ function sfc32Next(s) {
|
|
|
31
31
|
return t >>> 0;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// src/codes.ts
|
|
35
|
+
var SYNTH_FATAL_CODES = {
|
|
36
|
+
/**
|
|
37
|
+
* A vendor quirk was requested that the target format's profile system does not support. Fatal —
|
|
38
|
+
* never a silent no-op and never a fabricated quirk.
|
|
39
|
+
*/
|
|
40
|
+
SYNTH_UNSUPPORTED_QUIRK: "SYNTH_UNSUPPORTED_QUIRK",
|
|
41
|
+
/**
|
|
42
|
+
* A bare parse of a freshly-generated quirk artifact did not produce exactly the declared intended
|
|
43
|
+
* warning code(s). Fatal — never emit a mislabeled fixture.
|
|
44
|
+
*/
|
|
45
|
+
SYNTH_INTENDED_WARNING_MISMATCH: "SYNTH_INTENDED_WARNING_MISMATCH",
|
|
46
|
+
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
47
|
+
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
48
|
+
/** A value was drawn from an empty pool. Fatal — never a fabricated substitute. */
|
|
49
|
+
SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
|
|
50
|
+
/** A `defineSynthProfile` spec was not usable (a missing or blank `name`). Fatal. */
|
|
51
|
+
SYNTH_INVALID_PROFILE: "SYNTH_INVALID_PROFILE",
|
|
52
|
+
/**
|
|
53
|
+
* A caller-supplied selector (a message kind, a document type, a corpus mix entry, a claim
|
|
54
|
+
* variant, a Bundle type, a resource profile) is not in the closed set that governs it. Fatal —
|
|
55
|
+
* see `resolveKind`: a selector union is erased at run time, and a selector that falls through
|
|
56
|
+
* either mislabels the fixture or hands the value to a peer builder that quotes it back.
|
|
57
|
+
*/
|
|
58
|
+
SYNTH_UNSUPPORTED_KIND: "SYNTH_UNSUPPORTED_KIND"
|
|
59
|
+
};
|
|
60
|
+
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
61
|
+
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.",
|
|
62
|
+
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).",
|
|
63
|
+
SYNTH_QUIRK_ANCHOR_ABSENT: "The quirk transform found no structural anchor to mutate, so the fixture would not carry the deviation it is labelled with. Refusing to emit a mislabeled fixture.",
|
|
64
|
+
SYNTH_INTENDED_WARNING_MISMATCH: "A bare parse of the generated quirk artifact did not produce exactly the declared intended warning code(s). Refusing to emit a mislabeled fixture.",
|
|
65
|
+
SYNTH_UNMAPPED_CODE_SYSTEM: "The concept's code-system URI has no OID mapping in the C-CDA example-code table.",
|
|
66
|
+
SYNTH_INVALID_DECIMAL: "The value could not be read as an X12 decimal.",
|
|
67
|
+
SYNTH_INVALID_RANGE: "An integer range was requested with its maximum below its minimum.",
|
|
68
|
+
SYNTH_EMPTY_POOL: "A value was drawn from an empty pool.",
|
|
69
|
+
SYNTH_INVALID_PROFILE: "defineSynthProfile requires a non-empty string name.",
|
|
70
|
+
SYNTH_UNSUPPORTED_KIND: "The requested kind, document type, corpus mix entry, variant or profile is not one this generator supports. The supported set is the exported union for that option."
|
|
71
|
+
});
|
|
72
|
+
var SynthError = class extends Error {
|
|
73
|
+
/** The stable fatal code. */
|
|
74
|
+
code;
|
|
75
|
+
/**
|
|
76
|
+
* @param code - The stable {@link SynthFatalCode}. The message comes from the frozen registry.
|
|
77
|
+
*/
|
|
78
|
+
constructor(code) {
|
|
79
|
+
super(SYNTH_FATAL_MESSAGES[code]);
|
|
80
|
+
this.name = "SynthError";
|
|
81
|
+
this.code = code;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
34
85
|
// src/rng/rng.ts
|
|
35
86
|
var Sfc32Rng = class {
|
|
36
87
|
seed;
|
|
@@ -48,7 +99,7 @@ var Sfc32Rng = class {
|
|
|
48
99
|
return this.nextUint32() / 4294967296;
|
|
49
100
|
}
|
|
50
101
|
int(min, max) {
|
|
51
|
-
if (max < min) throw new
|
|
102
|
+
if (max < min) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INVALID_RANGE);
|
|
52
103
|
const span = max - min + 1;
|
|
53
104
|
return min + Math.floor(this.float() * span);
|
|
54
105
|
}
|
|
@@ -56,7 +107,7 @@ var Sfc32Rng = class {
|
|
|
56
107
|
return this.float() < p;
|
|
57
108
|
}
|
|
58
109
|
pick(items) {
|
|
59
|
-
if (items.length === 0) throw new
|
|
110
|
+
if (items.length === 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_EMPTY_POOL);
|
|
60
111
|
return items[this.int(0, items.length - 1)];
|
|
61
112
|
}
|
|
62
113
|
digits(n) {
|
|
@@ -447,10 +498,22 @@ function pidSegment(id) {
|
|
|
447
498
|
];
|
|
448
499
|
}
|
|
449
500
|
|
|
501
|
+
// src/select.ts
|
|
502
|
+
function resolveKind(allowed, requested) {
|
|
503
|
+
const match = allowed.find((value) => value === requested);
|
|
504
|
+
if (match === void 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_KIND);
|
|
505
|
+
return match;
|
|
506
|
+
}
|
|
507
|
+
function resolveMix(allowed, requested, fallback) {
|
|
508
|
+
if (requested === void 0) return fallback;
|
|
509
|
+
return requested.map((entry) => resolveKind(allowed, entry));
|
|
510
|
+
}
|
|
511
|
+
|
|
450
512
|
// src/hl7/adt.ts
|
|
513
|
+
var ADT_TRIGGERS = Object.freeze(["A01", "A04", "A08"]);
|
|
451
514
|
function generateAdt(options = {}) {
|
|
452
515
|
const seed = options.seed ?? 0;
|
|
453
|
-
const trigger = options.trigger ?? "A01";
|
|
516
|
+
const trigger = resolveKind(ADT_TRIGGERS, options.trigger ?? "A01");
|
|
454
517
|
const rng = createRng(seed);
|
|
455
518
|
const { message, timestamp } = mshScaffold(rng, `ADT^${trigger}`);
|
|
456
519
|
message.addSegment("EVN", [trigger, timestamp]);
|
|
@@ -737,7 +800,7 @@ function roundTrip(message) {
|
|
|
737
800
|
// src/profile.ts
|
|
738
801
|
function defineSynthProfile(spec) {
|
|
739
802
|
if (typeof spec.name !== "string" || spec.name.trim().length === 0) {
|
|
740
|
-
throw new
|
|
803
|
+
throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INVALID_PROFILE);
|
|
741
804
|
}
|
|
742
805
|
return Object.freeze({
|
|
743
806
|
name: spec.name,
|
|
@@ -747,28 +810,6 @@ function defineSynthProfile(spec) {
|
|
|
747
810
|
});
|
|
748
811
|
}
|
|
749
812
|
|
|
750
|
-
// src/codes.ts
|
|
751
|
-
var SYNTH_FATAL_CODES = {
|
|
752
|
-
/**
|
|
753
|
-
* A vendor quirk was requested that the target format's profile system does not support. Fatal —
|
|
754
|
-
* never a silent no-op and never a fabricated quirk.
|
|
755
|
-
*/
|
|
756
|
-
SYNTH_UNSUPPORTED_QUIRK: "SYNTH_UNSUPPORTED_QUIRK"
|
|
757
|
-
};
|
|
758
|
-
var SynthError = class extends Error {
|
|
759
|
-
/** The stable fatal code. */
|
|
760
|
-
code;
|
|
761
|
-
/**
|
|
762
|
-
* @param code - The stable {@link SynthFatalCode}.
|
|
763
|
-
* @param message - A human-readable detail (never contains PHI — there is none).
|
|
764
|
-
*/
|
|
765
|
-
constructor(code, message) {
|
|
766
|
-
super(message);
|
|
767
|
-
this.name = "SynthError";
|
|
768
|
-
this.code = code;
|
|
769
|
-
}
|
|
770
|
-
};
|
|
771
|
-
|
|
772
813
|
// src/quirk.ts
|
|
773
814
|
var PROFILE_QUIRK_APPLIED = "PROFILE_QUIRK_APPLIED";
|
|
774
815
|
function sameCodeSet(a, b) {
|
|
@@ -785,12 +826,8 @@ function sameCodeSet(a, b) {
|
|
|
785
826
|
}
|
|
786
827
|
function resolveQuirk(registry, format, name2) {
|
|
787
828
|
const descriptor = registry[name2];
|
|
788
|
-
if (descriptor === void 0) {
|
|
789
|
-
|
|
790
|
-
throw new SynthError(
|
|
791
|
-
SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_QUIRK,
|
|
792
|
-
`${format}: unsupported quirk "${name2}". The ${format} profile system supports: ${supported}.`
|
|
793
|
-
);
|
|
829
|
+
if (descriptor === void 0 || descriptor.format !== format) {
|
|
830
|
+
throw new SynthError(SYNTH_FATAL_CODES.SYNTH_UNSUPPORTED_QUIRK);
|
|
794
831
|
}
|
|
795
832
|
return descriptor;
|
|
796
833
|
}
|
|
@@ -805,11 +842,9 @@ function profileTolerated(disposition, intendedWarnings, warningsUnderProfile) {
|
|
|
805
842
|
return false;
|
|
806
843
|
}
|
|
807
844
|
}
|
|
808
|
-
function assertIntendedWarnings(
|
|
845
|
+
function assertIntendedWarnings(intendedWarnings, bareWarnings) {
|
|
809
846
|
if (!sameCodeSet(bareWarnings, intendedWarnings)) {
|
|
810
|
-
throw new
|
|
811
|
-
`quirk "${quirk}": the intended-warning contract does not hold \u2014 expected exactly [${intendedWarnings.join(", ")}] but a bare parse produced [${bareWarnings.join(", ")}]. Refusing to emit a mislabeled fixture.`
|
|
812
|
-
);
|
|
847
|
+
throw new SynthError(SYNTH_FATAL_CODES.SYNTH_INTENDED_WARNING_MISMATCH);
|
|
813
848
|
}
|
|
814
849
|
}
|
|
815
850
|
function validateProfileQuirks(profile, registry, format) {
|
|
@@ -850,6 +885,15 @@ function applyQuirk(quirk, wire) {
|
|
|
850
885
|
return [...segments, "NTE|1||\\Zff\\"].join("\r");
|
|
851
886
|
}
|
|
852
887
|
}
|
|
888
|
+
var ALL_QUIRK_KINDS = Object.freeze([
|
|
889
|
+
"ADT^A01",
|
|
890
|
+
"ADT^A04",
|
|
891
|
+
"ADT^A08",
|
|
892
|
+
"ORU^R01",
|
|
893
|
+
"ORM^O01",
|
|
894
|
+
"SIU^S12",
|
|
895
|
+
"VXU^V04"
|
|
896
|
+
]);
|
|
853
897
|
function baseMessage(kind, seed) {
|
|
854
898
|
switch (kind) {
|
|
855
899
|
case "ADT^A01":
|
|
@@ -870,11 +914,10 @@ function baseMessage(kind, seed) {
|
|
|
870
914
|
}
|
|
871
915
|
function generateHl7Quirk(options) {
|
|
872
916
|
const seed = options.seed ?? 0;
|
|
873
|
-
const kind = options.kind ?? "ORU^R01";
|
|
917
|
+
const kind = resolveKind(ALL_QUIRK_KINDS, options.kind ?? "ORU^R01");
|
|
874
918
|
const descriptor = resolveQuirk(HL7_QUIRKS, "hl7v2", options.quirk);
|
|
875
919
|
const content = applyQuirk(options.quirk, baseMessage(kind, seed).toString());
|
|
876
920
|
assertIntendedWarnings(
|
|
877
|
-
descriptor.name,
|
|
878
921
|
descriptor.intendedWarnings,
|
|
879
922
|
hl7.parseHL7(content).warnings.map((w) => String(w.code))
|
|
880
923
|
);
|
|
@@ -920,6 +963,7 @@ var ALL_HL7_QUIRKS = Object.freeze(
|
|
|
920
963
|
function hl7QuirkCorpus(options) {
|
|
921
964
|
const quirks = options.profile ? validateProfileQuirks(options.profile, HL7_QUIRKS, "hl7v2") : options.quirks ?? ALL_HL7_QUIRKS;
|
|
922
965
|
const names = quirks.length > 0 ? quirks : ALL_HL7_QUIRKS;
|
|
966
|
+
for (const name2 of names) resolveQuirk(HL7_QUIRKS, "hl7v2", name2);
|
|
923
967
|
const kind = options.kind ?? "ORU^R01";
|
|
924
968
|
const count = options.count ?? names.length;
|
|
925
969
|
const seedStream = createRng(options.seed);
|
|
@@ -942,7 +986,7 @@ var hl7QuirkProfile = defineSynthProfile({
|
|
|
942
986
|
});
|
|
943
987
|
|
|
944
988
|
// src/hl7/index.ts
|
|
945
|
-
var
|
|
989
|
+
var ALL_KINDS = Object.freeze([
|
|
946
990
|
"ADT^A01",
|
|
947
991
|
"ADT^A04",
|
|
948
992
|
"ADT^A08",
|
|
@@ -951,8 +995,10 @@ var DEFAULT_MIX = Object.freeze([
|
|
|
951
995
|
"SIU^S12",
|
|
952
996
|
"VXU^V04"
|
|
953
997
|
]);
|
|
998
|
+
var DEFAULT_MIX = ALL_KINDS;
|
|
999
|
+
var ADT_TRIGGERS2 = Object.freeze(["A01", "A04", "A08"]);
|
|
954
1000
|
function generateHl7(kind, seed) {
|
|
955
|
-
switch (kind) {
|
|
1001
|
+
switch (resolveKind(ALL_KINDS, kind)) {
|
|
956
1002
|
case "ADT^A01":
|
|
957
1003
|
return generateAdt({ seed, trigger: "A01" });
|
|
958
1004
|
case "ADT^A04":
|
|
@@ -971,7 +1017,9 @@ function generateHl7(kind, seed) {
|
|
|
971
1017
|
}
|
|
972
1018
|
function hl7Corpus(options) {
|
|
973
1019
|
const { seed, count = 1 } = options;
|
|
974
|
-
const kinds = options.triggers !== void 0 ? options.triggers.map(
|
|
1020
|
+
const kinds = options.triggers !== void 0 ? options.triggers.map(
|
|
1021
|
+
(t) => `ADT^${resolveKind(ADT_TRIGGERS2, t)}`
|
|
1022
|
+
) : resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
975
1023
|
const seedStream = createRng(seed);
|
|
976
1024
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
977
1025
|
const kind = kinds[i % kinds.length] ?? "ADT^A01";
|