@cosyte/synth 0.1.9 → 0.1.10
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 +29 -0
- package/README.md +28 -17
- package/dist/animal-code-Bdl_9Jma.d.cts +30 -0
- package/dist/animal-code-Bdl_9Jma.d.ts +30 -0
- package/dist/astm/index.cjs +147 -17
- package/dist/astm/index.cjs.map +1 -1
- package/dist/astm/index.d.cts +22 -9
- package/dist/astm/index.d.ts +22 -9
- package/dist/astm/index.mjs +147 -17
- package/dist/astm/index.mjs.map +1 -1
- package/dist/ccda/index.cjs +147 -17
- package/dist/ccda/index.cjs.map +1 -1
- package/dist/ccda/index.d.cts +12 -3
- package/dist/ccda/index.d.ts +12 -3
- package/dist/ccda/index.mjs +147 -17
- package/dist/ccda/index.mjs.map +1 -1
- package/dist/deid/index.cjs +537 -30
- package/dist/deid/index.cjs.map +1 -1
- package/dist/deid/index.d.cts +2 -1
- package/dist/deid/index.d.ts +2 -1
- package/dist/deid/index.mjs +537 -30
- package/dist/deid/index.mjs.map +1 -1
- package/dist/fhir/index.cjs +142 -14
- package/dist/fhir/index.cjs.map +1 -1
- package/dist/fhir/index.d.cts +4 -1
- package/dist/fhir/index.d.ts +4 -1
- package/dist/fhir/index.mjs +142 -14
- package/dist/fhir/index.mjs.map +1 -1
- package/dist/hl7/index.cjs +566 -39
- package/dist/hl7/index.cjs.map +1 -1
- package/dist/hl7/index.d.cts +402 -111
- package/dist/hl7/index.d.ts +402 -111
- package/dist/hl7/index.mjs +557 -40
- package/dist/hl7/index.mjs.map +1 -1
- package/dist/index.cjs +124 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +183 -10
- package/dist/index.d.ts +183 -10
- package/dist/index.mjs +115 -14
- package/dist/index.mjs.map +1 -1
- package/dist/ncpdp/index.cjs +142 -14
- package/dist/ncpdp/index.cjs.map +1 -1
- package/dist/ncpdp/index.d.cts +4 -1
- package/dist/ncpdp/index.d.ts +4 -1
- package/dist/ncpdp/index.mjs +142 -14
- package/dist/ncpdp/index.mjs.map +1 -1
- package/dist/{quirk-BfObsiWV.d.ts → quirk-CzknuYUo.d.ts} +16 -5
- package/dist/{quirk-BCSs1jPt.d.cts → quirk-Q5AxzHSC.d.cts} +16 -5
- package/dist/x12/index.cjs +142 -14
- package/dist/x12/index.cjs.map +1 -1
- package/dist/x12/index.d.cts +4 -1
- package/dist/x12/index.d.ts +4 -1
- package/dist/x12/index.mjs +142 -14
- package/dist/x12/index.mjs.map +1 -1
- package/package.json +7 -4
package/dist/ccda/index.cjs
CHANGED
|
@@ -73,7 +73,11 @@ var SYNTH_FATAL_CODES = Object.freeze({
|
|
|
73
73
|
SYNTH_INVALID_DECIMAL: "SYNTH_INVALID_DECIMAL",
|
|
74
74
|
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
75
75
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
76
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* A value was drawn from an empty pool. Fatal, never a fabricated substitute. A corpus given an
|
|
78
|
+
* empty `mix`, `triggers` or `quirks` list (or a profile whose `quirks` is an empty list) raises it
|
|
79
|
+
* too: there is no kind to cycle through, and an empty list is never read as the default.
|
|
80
|
+
*/
|
|
77
81
|
SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
|
|
78
82
|
/** A `defineSynthProfile` spec was not usable (a missing or blank `name`). Fatal. */
|
|
79
83
|
SYNTH_INVALID_PROFILE: "SYNTH_INVALID_PROFILE",
|
|
@@ -92,7 +96,21 @@ var SYNTH_FATAL_CODES = Object.freeze({
|
|
|
92
96
|
* apart without matching message text. Fatal, and raised **before** anything is generated: a
|
|
93
97
|
* mislabelled artifact is worse than no artifact.
|
|
94
98
|
*/
|
|
95
|
-
SYNTH_PROFILE_NOT_GENERATED: "SYNTH_PROFILE_NOT_GENERATED"
|
|
99
|
+
SYNTH_PROFILE_NOT_GENERATED: "SYNTH_PROFILE_NOT_GENERATED",
|
|
100
|
+
/**
|
|
101
|
+
* The sibling parser a generator builds through is not installed. Raised on the first call that
|
|
102
|
+
* needs it, by both builds, only when the missing package is the parser itself: a missing
|
|
103
|
+
* dependency of an installed parser is thrown as Node.js raised it. The error carries the parser's
|
|
104
|
+
* package name on `peer`, a message from {@link SYNTH_PEER_MESSAGES} that names its install
|
|
105
|
+
* command, and the error Node.js raised on `cause`.
|
|
106
|
+
*/
|
|
107
|
+
SYNTH_PEER_NOT_INSTALLED: "SYNTH_PEER_NOT_INSTALLED",
|
|
108
|
+
/**
|
|
109
|
+
* An HL7 v2 `ACK` was asked for an inbound message that carries no MSH-10 message control id.
|
|
110
|
+
* MSA-2 must echo that id, and an acknowledgment that cannot be matched to the message it answers
|
|
111
|
+
* is not a fixture worth having, so nothing is generated.
|
|
112
|
+
*/
|
|
113
|
+
SYNTH_ACK_UNCORRELATED: "SYNTH_ACK_UNCORRELATED"
|
|
96
114
|
});
|
|
97
115
|
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
98
116
|
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.",
|
|
@@ -105,18 +123,38 @@ var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
|
105
123
|
SYNTH_EMPTY_POOL: "A value was drawn from an empty pool.",
|
|
106
124
|
SYNTH_INVALID_PROFILE: "defineSynthProfile requires a non-empty string name.",
|
|
107
125
|
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.",
|
|
108
|
-
SYNTH_PROFILE_NOT_GENERATED: "The requested profile is published by the adopted implementation guide, and this build does not generate it. The coverage surface reports, per adopted profile, whether it is generated."
|
|
126
|
+
SYNTH_PROFILE_NOT_GENERATED: "The requested profile is published by the adopted implementation guide, and this build does not generate it. The coverage surface reports, per adopted profile, whether it is generated.",
|
|
127
|
+
SYNTH_PEER_NOT_INSTALLED: "A sibling @cosyte parser this generator builds through is not installed. Install it beside @cosyte/synth.",
|
|
128
|
+
SYNTH_ACK_UNCORRELATED: (
|
|
129
|
+
// Stryker disable next-line all: a module-level table runs once, at import; the docs test holds it.
|
|
130
|
+
"The inbound message has no MSH-10 control id for an ACK to echo in MSA-2."
|
|
131
|
+
)
|
|
132
|
+
});
|
|
133
|
+
var SYNTH_PEER_MESSAGES = Object.freeze({
|
|
134
|
+
"@cosyte/hl7": "@cosyte/hl7 is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/hl7",
|
|
135
|
+
"@cosyte/fhir": "@cosyte/fhir is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/fhir",
|
|
136
|
+
"@cosyte/ccda": "@cosyte/ccda is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/ccda",
|
|
137
|
+
"@cosyte/x12": "@cosyte/x12 is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/x12",
|
|
138
|
+
"@cosyte/ncpdp": "@cosyte/ncpdp is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/ncpdp",
|
|
139
|
+
"@cosyte/astm": "@cosyte/astm is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/astm",
|
|
140
|
+
"@cosyte/deid": "@cosyte/deid is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/deid"
|
|
109
141
|
});
|
|
110
142
|
var SynthError = class extends Error {
|
|
111
143
|
/** The stable fatal code. */
|
|
112
144
|
code;
|
|
113
145
|
/**
|
|
114
146
|
* @param code - The stable {@link SynthFatalCode}. The message comes from the frozen registry.
|
|
147
|
+
* @param options - The error that caused this one, and for a missing parser its package.
|
|
115
148
|
*/
|
|
116
|
-
constructor(code) {
|
|
117
|
-
|
|
149
|
+
constructor(code, options = {}) {
|
|
150
|
+
const peer = code === SYNTH_FATAL_CODES.SYNTH_PEER_NOT_INSTALLED ? options.peer : void 0;
|
|
151
|
+
super(
|
|
152
|
+
peer === void 0 ? SYNTH_FATAL_MESSAGES[code] : SYNTH_PEER_MESSAGES[peer],
|
|
153
|
+
"cause" in options ? { cause: options.cause } : void 0
|
|
154
|
+
);
|
|
118
155
|
this.name = "SynthError";
|
|
119
156
|
this.code = code;
|
|
157
|
+
if (peer !== void 0) Object.defineProperty(this, "peer", { value: peer, enumerable: true });
|
|
120
158
|
}
|
|
121
159
|
};
|
|
122
160
|
|
|
@@ -224,7 +262,7 @@ function luhnMod10(digits) {
|
|
|
224
262
|
if (d < 0 || d > 9) continue;
|
|
225
263
|
if (double) {
|
|
226
264
|
d *= 2;
|
|
227
|
-
if (d
|
|
265
|
+
if (d >= 10) d -= 9;
|
|
228
266
|
}
|
|
229
267
|
sum += d;
|
|
230
268
|
double = !double;
|
|
@@ -321,7 +359,7 @@ var SYNTHETIC_CITY_NAMES = Object.freeze([
|
|
|
321
359
|
]);
|
|
322
360
|
|
|
323
361
|
// src/safe/providers.ts
|
|
324
|
-
function ssn(rng, block
|
|
362
|
+
function ssn(rng, block) {
|
|
325
363
|
if (block === "advertising") {
|
|
326
364
|
return `987-00-432${String(rng.int(0, 9))}`;
|
|
327
365
|
}
|
|
@@ -448,6 +486,63 @@ var US_STATES = Object.freeze([
|
|
|
448
486
|
"WY"
|
|
449
487
|
]);
|
|
450
488
|
|
|
489
|
+
// src/safe/animal-code.ts
|
|
490
|
+
var ANIMAL_CODE_SYSTEM = "99COSYTE";
|
|
491
|
+
function animalCode(value, text) {
|
|
492
|
+
return Object.freeze({ code: value, text, system: ANIMAL_CODE_SYSTEM });
|
|
493
|
+
}
|
|
494
|
+
function animalSpecies(value, text, breeds) {
|
|
495
|
+
return Object.freeze({ ...animalCode(value, text), breeds: Object.freeze([...breeds]) });
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/safe/animal.ts
|
|
499
|
+
var SYNTHETIC_ANIMAL_SPECIES = Object.freeze([
|
|
500
|
+
animalSpecies("DOG", "Dog", [
|
|
501
|
+
animalCode("DOG-LAB", "Labrador Retriever"),
|
|
502
|
+
animalCode("DOG-BGL", "Beagle"),
|
|
503
|
+
animalCode("DOG-MIX", "Mixed breed dog")
|
|
504
|
+
]),
|
|
505
|
+
animalSpecies("CAT", "Cat", [animalCode("CAT-DSH", "Domestic Shorthair"), animalCode("CAT-SIA", "Siamese")]),
|
|
506
|
+
animalSpecies("HORSE", "Horse", [animalCode("HRS-QTR", "Quarter Horse"), animalCode("HRS-TB", "Thoroughbred")]),
|
|
507
|
+
animalSpecies("CATTLE", "Cattle", [animalCode("CTL-HOL", "Holstein"), animalCode("CTL-ANG", "Angus")])
|
|
508
|
+
]);
|
|
509
|
+
var SYNTHETIC_PET_NAMES = Object.freeze([
|
|
510
|
+
"Fixturepaw",
|
|
511
|
+
"Mockwhisker",
|
|
512
|
+
"Stubtail",
|
|
513
|
+
"Seedpup",
|
|
514
|
+
"Testbiscuit",
|
|
515
|
+
"Placepurr",
|
|
516
|
+
"Synthfur",
|
|
517
|
+
"Dummymane",
|
|
518
|
+
"Examplehoof",
|
|
519
|
+
"Fauxfluff",
|
|
520
|
+
"Samplesnout",
|
|
521
|
+
"Voidwag"
|
|
522
|
+
]);
|
|
523
|
+
var MICROCHIP_TEST_PREFIX = "999";
|
|
524
|
+
function petName(rng) {
|
|
525
|
+
return rng.pick(SYNTHETIC_PET_NAMES);
|
|
526
|
+
}
|
|
527
|
+
function animalBreed(rng) {
|
|
528
|
+
const picked = rng.pick(SYNTHETIC_ANIMAL_SPECIES);
|
|
529
|
+
const { breeds, ...bare } = picked;
|
|
530
|
+
return { species: Object.freeze(bare), breed: rng.pick(breeds) };
|
|
531
|
+
}
|
|
532
|
+
function microchip(rng) {
|
|
533
|
+
return `${MICROCHIP_TEST_PREFIX}${rng.digits(12)}`;
|
|
534
|
+
}
|
|
535
|
+
function animalBirthDate(rng, onDate) {
|
|
536
|
+
const year = Number(onDate.slice(0, 4));
|
|
537
|
+
const month = Number(onDate.slice(4, 6)) - 1;
|
|
538
|
+
const day = Number(onDate.slice(6, 8));
|
|
539
|
+
const latest = Date.UTC(year, month, day);
|
|
540
|
+
const earliest = Date.UTC(year - 20, month, day);
|
|
541
|
+
const span = Math.round((latest - earliest) / 864e5);
|
|
542
|
+
const born = new Date(latest - rng.int(0, span) * 864e5);
|
|
543
|
+
return born.toISOString().slice(0, 10).replace(/-/g, "");
|
|
544
|
+
}
|
|
545
|
+
|
|
451
546
|
// src/safe/index.ts
|
|
452
547
|
var safe = Object.freeze({
|
|
453
548
|
ssn,
|
|
@@ -461,7 +556,11 @@ var safe = Object.freeze({
|
|
|
461
556
|
address,
|
|
462
557
|
dateYmd,
|
|
463
558
|
npi,
|
|
464
|
-
dea
|
|
559
|
+
dea,
|
|
560
|
+
petName,
|
|
561
|
+
animalBreed,
|
|
562
|
+
microchip,
|
|
563
|
+
animalBirthDate
|
|
465
564
|
});
|
|
466
565
|
var SYSTEM = Object.freeze({
|
|
467
566
|
/** FHIR `administrative-gender` (`Patient.gender`). */
|
|
@@ -856,23 +955,52 @@ function resolveMix(allowed, requested, fallback) {
|
|
|
856
955
|
if (requested === void 0) return fallback;
|
|
857
956
|
return requested.map((entry) => resolveKind(allowed, entry));
|
|
858
957
|
}
|
|
958
|
+
function requireSelection(selection) {
|
|
959
|
+
if (selection.length === 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_EMPTY_POOL);
|
|
960
|
+
return selection;
|
|
961
|
+
}
|
|
859
962
|
function defaultHost() {
|
|
860
963
|
if (typeof undefined !== "function") return { require: __require, resolve: void 0 };
|
|
861
964
|
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) };
|
|
862
965
|
}
|
|
966
|
+
function isDenoResolveRefusal(error) {
|
|
967
|
+
return error instanceof TypeError && !("code" in error) && error.message === "Invalid arguments";
|
|
968
|
+
}
|
|
863
969
|
function isRequireEsmRefusal(error) {
|
|
864
970
|
return error?.code === "ERR_REQUIRE_ESM";
|
|
865
971
|
}
|
|
972
|
+
function peerPackage(specifier) {
|
|
973
|
+
const name2 = specifier.split("/").slice(0, 2).join("/");
|
|
974
|
+
return Object.hasOwn(SYNTH_PEER_MESSAGES, name2) ? name2 : void 0;
|
|
975
|
+
}
|
|
976
|
+
function isPeerNotFound(error, specifier, peer) {
|
|
977
|
+
const { code, message } = error ?? {};
|
|
978
|
+
if (typeof message !== "string") return false;
|
|
979
|
+
if (code === "ERR_MODULE_NOT_FOUND") return message.startsWith(`Cannot find package '${peer}'`);
|
|
980
|
+
if (code === "MODULE_NOT_FOUND") return message.startsWith(`Cannot find module '${specifier}'`);
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
866
983
|
function loadPeer(specifier, host) {
|
|
984
|
+
try {
|
|
985
|
+
return loadFrom(specifier, host);
|
|
986
|
+
} catch (error) {
|
|
987
|
+
const peer = peerPackage(specifier);
|
|
988
|
+
if (peer === void 0 || !isPeerNotFound(error, specifier, peer)) throw error;
|
|
989
|
+
throw new SynthError(SYNTH_FATAL_CODES.SYNTH_PEER_NOT_INSTALLED, { peer, cause: error });
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
function loadFrom(specifier, host) {
|
|
867
993
|
if (host.resolve === void 0) return host.require(specifier);
|
|
868
|
-
let
|
|
994
|
+
let url$1;
|
|
869
995
|
try {
|
|
870
|
-
|
|
871
|
-
} catch {
|
|
996
|
+
url$1 = host.resolve(specifier);
|
|
997
|
+
} catch (error) {
|
|
998
|
+
if (!isDenoResolveRefusal(error)) throw error;
|
|
872
999
|
return host.require(specifier);
|
|
873
1000
|
}
|
|
1001
|
+
if (!url$1.startsWith("file:")) return host.require(specifier);
|
|
874
1002
|
try {
|
|
875
|
-
return host.require(
|
|
1003
|
+
return host.require(url.fileURLToPath(url$1));
|
|
876
1004
|
} catch (error) {
|
|
877
1005
|
if (!isRequireEsmRefusal(error)) throw error;
|
|
878
1006
|
return host.require(specifier);
|
|
@@ -1011,7 +1139,7 @@ function defineSynthProfile(spec) {
|
|
|
1011
1139
|
name: spec.name,
|
|
1012
1140
|
...spec.givenNames ? { givenNames: Object.freeze([...spec.givenNames]) } : {},
|
|
1013
1141
|
...spec.familyNames ? { familyNames: Object.freeze([...spec.familyNames]) } : {},
|
|
1014
|
-
quirks: Object.freeze([...spec.quirks
|
|
1142
|
+
...spec.quirks !== void 0 ? { quirks: Object.freeze([...spec.quirks]) } : {}
|
|
1015
1143
|
});
|
|
1016
1144
|
}
|
|
1017
1145
|
|
|
@@ -1053,6 +1181,7 @@ function assertIntendedWarnings(intendedWarnings, bareWarnings) {
|
|
|
1053
1181
|
}
|
|
1054
1182
|
}
|
|
1055
1183
|
function validateProfileQuirks(profile, registry, format) {
|
|
1184
|
+
if (profile.quirks === void 0) return Object.keys(registry);
|
|
1056
1185
|
for (const name2 of profile.quirks) resolveQuirk(registry, format, name2);
|
|
1057
1186
|
return profile.quirks;
|
|
1058
1187
|
}
|
|
@@ -1167,8 +1296,9 @@ var ALL_CCDA_QUIRKS = Object.freeze(
|
|
|
1167
1296
|
Object.keys(CCDA_QUIRKS)
|
|
1168
1297
|
);
|
|
1169
1298
|
function ccdaQuirkCorpus(options) {
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1299
|
+
const names = requireSelection(
|
|
1300
|
+
options.profile?.quirks !== void 0 ? validateProfileQuirks(options.profile, CCDA_QUIRKS, "ccda") : options.quirks ?? ALL_CCDA_QUIRKS
|
|
1301
|
+
);
|
|
1172
1302
|
for (const name2 of names) resolveQuirk(CCDA_QUIRKS, "ccda", name2);
|
|
1173
1303
|
const documentType = options.documentType ?? "ccd";
|
|
1174
1304
|
const count = options.count ?? names.length;
|
|
@@ -1196,10 +1326,10 @@ var ALL_KINDS = Object.freeze(["ccd", "referralNote"]);
|
|
|
1196
1326
|
var DEFAULT_MIX = ALL_KINDS;
|
|
1197
1327
|
function ccdaCorpus(options) {
|
|
1198
1328
|
const { seed, count = 1, verify = true } = options;
|
|
1199
|
-
const mix = resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
1329
|
+
const mix = requireSelection(resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX));
|
|
1200
1330
|
const seedStream = createRng(seed);
|
|
1201
1331
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
1202
|
-
const documentType = mix[i % mix.length]
|
|
1332
|
+
const documentType = mix[i % mix.length];
|
|
1203
1333
|
const docSeed = seedStream.nextUint32();
|
|
1204
1334
|
const rt = ccdaCorpusArtifact(generateCcda({ seed: docSeed, documentType }), verify);
|
|
1205
1335
|
return {
|