@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/fhir/index.d.cts
CHANGED
|
@@ -395,7 +395,10 @@ interface FhirCorpusOptions {
|
|
|
395
395
|
readonly seed: number;
|
|
396
396
|
/** How many resources to generate. Defaults to `1`. */
|
|
397
397
|
readonly count?: number;
|
|
398
|
-
/**
|
|
398
|
+
/**
|
|
399
|
+
* The resource kinds to cycle through. Defaults to the full US Core clinical spine + a Bundle. An
|
|
400
|
+
* empty list throws `SYNTH_EMPTY_POOL`.
|
|
401
|
+
*/
|
|
399
402
|
readonly mix?: readonly FhirResourceKind[];
|
|
400
403
|
/**
|
|
401
404
|
* Whether each artifact is read back through its parser and the parser's findings recorded as its
|
package/dist/fhir/index.d.ts
CHANGED
|
@@ -395,7 +395,10 @@ interface FhirCorpusOptions {
|
|
|
395
395
|
readonly seed: number;
|
|
396
396
|
/** How many resources to generate. Defaults to `1`. */
|
|
397
397
|
readonly count?: number;
|
|
398
|
-
/**
|
|
398
|
+
/**
|
|
399
|
+
* The resource kinds to cycle through. Defaults to the full US Core clinical spine + a Bundle. An
|
|
400
|
+
* empty list throws `SYNTH_EMPTY_POOL`.
|
|
401
|
+
*/
|
|
399
402
|
readonly mix?: readonly FhirResourceKind[];
|
|
400
403
|
/**
|
|
401
404
|
* Whether each artifact is read back through its parser and the parser's findings recorded as its
|
package/dist/fhir/index.mjs
CHANGED
|
@@ -70,7 +70,11 @@ var SYNTH_FATAL_CODES = Object.freeze({
|
|
|
70
70
|
SYNTH_INVALID_DECIMAL: "SYNTH_INVALID_DECIMAL",
|
|
71
71
|
/** An integer range was requested with its maximum below its minimum. Fatal. */
|
|
72
72
|
SYNTH_INVALID_RANGE: "SYNTH_INVALID_RANGE",
|
|
73
|
-
/**
|
|
73
|
+
/**
|
|
74
|
+
* A value was drawn from an empty pool. Fatal, never a fabricated substitute. A corpus given an
|
|
75
|
+
* empty `mix`, `triggers` or `quirks` list (or a profile whose `quirks` is an empty list) raises it
|
|
76
|
+
* too: there is no kind to cycle through, and an empty list is never read as the default.
|
|
77
|
+
*/
|
|
74
78
|
SYNTH_EMPTY_POOL: "SYNTH_EMPTY_POOL",
|
|
75
79
|
/** A `defineSynthProfile` spec was not usable (a missing or blank `name`). Fatal. */
|
|
76
80
|
SYNTH_INVALID_PROFILE: "SYNTH_INVALID_PROFILE",
|
|
@@ -89,7 +93,21 @@ var SYNTH_FATAL_CODES = Object.freeze({
|
|
|
89
93
|
* apart without matching message text. Fatal, and raised **before** anything is generated: a
|
|
90
94
|
* mislabelled artifact is worse than no artifact.
|
|
91
95
|
*/
|
|
92
|
-
SYNTH_PROFILE_NOT_GENERATED: "SYNTH_PROFILE_NOT_GENERATED"
|
|
96
|
+
SYNTH_PROFILE_NOT_GENERATED: "SYNTH_PROFILE_NOT_GENERATED",
|
|
97
|
+
/**
|
|
98
|
+
* The sibling parser a generator builds through is not installed. Raised on the first call that
|
|
99
|
+
* needs it, by both builds, only when the missing package is the parser itself: a missing
|
|
100
|
+
* dependency of an installed parser is thrown as Node.js raised it. The error carries the parser's
|
|
101
|
+
* package name on `peer`, a message from {@link SYNTH_PEER_MESSAGES} that names its install
|
|
102
|
+
* command, and the error Node.js raised on `cause`.
|
|
103
|
+
*/
|
|
104
|
+
SYNTH_PEER_NOT_INSTALLED: "SYNTH_PEER_NOT_INSTALLED",
|
|
105
|
+
/**
|
|
106
|
+
* An HL7 v2 `ACK` was asked for an inbound message that carries no MSH-10 message control id.
|
|
107
|
+
* MSA-2 must echo that id, and an acknowledgment that cannot be matched to the message it answers
|
|
108
|
+
* is not a fixture worth having, so nothing is generated.
|
|
109
|
+
*/
|
|
110
|
+
SYNTH_ACK_UNCORRELATED: "SYNTH_ACK_UNCORRELATED"
|
|
93
111
|
});
|
|
94
112
|
var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
95
113
|
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.",
|
|
@@ -102,18 +120,38 @@ var SYNTH_FATAL_MESSAGES = Object.freeze({
|
|
|
102
120
|
SYNTH_EMPTY_POOL: "A value was drawn from an empty pool.",
|
|
103
121
|
SYNTH_INVALID_PROFILE: "defineSynthProfile requires a non-empty string name.",
|
|
104
122
|
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.",
|
|
105
|
-
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."
|
|
123
|
+
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.",
|
|
124
|
+
SYNTH_PEER_NOT_INSTALLED: "A sibling @cosyte parser this generator builds through is not installed. Install it beside @cosyte/synth.",
|
|
125
|
+
SYNTH_ACK_UNCORRELATED: (
|
|
126
|
+
// Stryker disable next-line all: a module-level table runs once, at import; the docs test holds it.
|
|
127
|
+
"The inbound message has no MSH-10 control id for an ACK to echo in MSA-2."
|
|
128
|
+
)
|
|
129
|
+
});
|
|
130
|
+
var SYNTH_PEER_MESSAGES = Object.freeze({
|
|
131
|
+
"@cosyte/hl7": "@cosyte/hl7 is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/hl7",
|
|
132
|
+
"@cosyte/fhir": "@cosyte/fhir is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/fhir",
|
|
133
|
+
"@cosyte/ccda": "@cosyte/ccda is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/ccda",
|
|
134
|
+
"@cosyte/x12": "@cosyte/x12 is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/x12",
|
|
135
|
+
"@cosyte/ncpdp": "@cosyte/ncpdp is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/ncpdp",
|
|
136
|
+
"@cosyte/astm": "@cosyte/astm is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/astm",
|
|
137
|
+
"@cosyte/deid": "@cosyte/deid is not installed, and this generator builds through it. Install it beside @cosyte/synth: npm install @cosyte/deid"
|
|
106
138
|
});
|
|
107
139
|
var SynthError = class extends Error {
|
|
108
140
|
/** The stable fatal code. */
|
|
109
141
|
code;
|
|
110
142
|
/**
|
|
111
143
|
* @param code - The stable {@link SynthFatalCode}. The message comes from the frozen registry.
|
|
144
|
+
* @param options - The error that caused this one, and for a missing parser its package.
|
|
112
145
|
*/
|
|
113
|
-
constructor(code) {
|
|
114
|
-
|
|
146
|
+
constructor(code, options = {}) {
|
|
147
|
+
const peer = code === SYNTH_FATAL_CODES.SYNTH_PEER_NOT_INSTALLED ? options.peer : void 0;
|
|
148
|
+
super(
|
|
149
|
+
peer === void 0 ? SYNTH_FATAL_MESSAGES[code] : SYNTH_PEER_MESSAGES[peer],
|
|
150
|
+
"cause" in options ? { cause: options.cause } : void 0
|
|
151
|
+
);
|
|
115
152
|
this.name = "SynthError";
|
|
116
153
|
this.code = code;
|
|
154
|
+
if (peer !== void 0) Object.defineProperty(this, "peer", { value: peer, enumerable: true });
|
|
117
155
|
}
|
|
118
156
|
};
|
|
119
157
|
|
|
@@ -199,7 +237,7 @@ function luhnMod10(digits) {
|
|
|
199
237
|
if (d < 0 || d > 9) continue;
|
|
200
238
|
if (double) {
|
|
201
239
|
d *= 2;
|
|
202
|
-
if (d
|
|
240
|
+
if (d >= 10) d -= 9;
|
|
203
241
|
}
|
|
204
242
|
sum += d;
|
|
205
243
|
double = !double;
|
|
@@ -296,7 +334,7 @@ var SYNTHETIC_CITY_NAMES = Object.freeze([
|
|
|
296
334
|
]);
|
|
297
335
|
|
|
298
336
|
// src/safe/providers.ts
|
|
299
|
-
function ssn(rng, block
|
|
337
|
+
function ssn(rng, block) {
|
|
300
338
|
if (block === "advertising") {
|
|
301
339
|
return `987-00-432${String(rng.int(0, 9))}`;
|
|
302
340
|
}
|
|
@@ -423,6 +461,63 @@ var US_STATES = Object.freeze([
|
|
|
423
461
|
"WY"
|
|
424
462
|
]);
|
|
425
463
|
|
|
464
|
+
// src/safe/animal-code.ts
|
|
465
|
+
var ANIMAL_CODE_SYSTEM = "99COSYTE";
|
|
466
|
+
function animalCode(value, text) {
|
|
467
|
+
return Object.freeze({ code: value, text, system: ANIMAL_CODE_SYSTEM });
|
|
468
|
+
}
|
|
469
|
+
function animalSpecies(value, text, breeds) {
|
|
470
|
+
return Object.freeze({ ...animalCode(value, text), breeds: Object.freeze([...breeds]) });
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// src/safe/animal.ts
|
|
474
|
+
var SYNTHETIC_ANIMAL_SPECIES = Object.freeze([
|
|
475
|
+
animalSpecies("DOG", "Dog", [
|
|
476
|
+
animalCode("DOG-LAB", "Labrador Retriever"),
|
|
477
|
+
animalCode("DOG-BGL", "Beagle"),
|
|
478
|
+
animalCode("DOG-MIX", "Mixed breed dog")
|
|
479
|
+
]),
|
|
480
|
+
animalSpecies("CAT", "Cat", [animalCode("CAT-DSH", "Domestic Shorthair"), animalCode("CAT-SIA", "Siamese")]),
|
|
481
|
+
animalSpecies("HORSE", "Horse", [animalCode("HRS-QTR", "Quarter Horse"), animalCode("HRS-TB", "Thoroughbred")]),
|
|
482
|
+
animalSpecies("CATTLE", "Cattle", [animalCode("CTL-HOL", "Holstein"), animalCode("CTL-ANG", "Angus")])
|
|
483
|
+
]);
|
|
484
|
+
var SYNTHETIC_PET_NAMES = Object.freeze([
|
|
485
|
+
"Fixturepaw",
|
|
486
|
+
"Mockwhisker",
|
|
487
|
+
"Stubtail",
|
|
488
|
+
"Seedpup",
|
|
489
|
+
"Testbiscuit",
|
|
490
|
+
"Placepurr",
|
|
491
|
+
"Synthfur",
|
|
492
|
+
"Dummymane",
|
|
493
|
+
"Examplehoof",
|
|
494
|
+
"Fauxfluff",
|
|
495
|
+
"Samplesnout",
|
|
496
|
+
"Voidwag"
|
|
497
|
+
]);
|
|
498
|
+
var MICROCHIP_TEST_PREFIX = "999";
|
|
499
|
+
function petName(rng) {
|
|
500
|
+
return rng.pick(SYNTHETIC_PET_NAMES);
|
|
501
|
+
}
|
|
502
|
+
function animalBreed(rng) {
|
|
503
|
+
const picked = rng.pick(SYNTHETIC_ANIMAL_SPECIES);
|
|
504
|
+
const { breeds, ...bare } = picked;
|
|
505
|
+
return { species: Object.freeze(bare), breed: rng.pick(breeds) };
|
|
506
|
+
}
|
|
507
|
+
function microchip(rng) {
|
|
508
|
+
return `${MICROCHIP_TEST_PREFIX}${rng.digits(12)}`;
|
|
509
|
+
}
|
|
510
|
+
function animalBirthDate(rng, onDate) {
|
|
511
|
+
const year = Number(onDate.slice(0, 4));
|
|
512
|
+
const month = Number(onDate.slice(4, 6)) - 1;
|
|
513
|
+
const day = Number(onDate.slice(6, 8));
|
|
514
|
+
const latest = Date.UTC(year, month, day);
|
|
515
|
+
const earliest = Date.UTC(year - 20, month, day);
|
|
516
|
+
const span = Math.round((latest - earliest) / 864e5);
|
|
517
|
+
const born = new Date(latest - rng.int(0, span) * 864e5);
|
|
518
|
+
return born.toISOString().slice(0, 10).replace(/-/g, "");
|
|
519
|
+
}
|
|
520
|
+
|
|
426
521
|
// src/safe/index.ts
|
|
427
522
|
var safe = Object.freeze({
|
|
428
523
|
ssn,
|
|
@@ -436,7 +531,11 @@ var safe = Object.freeze({
|
|
|
436
531
|
address,
|
|
437
532
|
dateYmd,
|
|
438
533
|
npi,
|
|
439
|
-
dea
|
|
534
|
+
dea,
|
|
535
|
+
petName,
|
|
536
|
+
animalBreed,
|
|
537
|
+
microchip,
|
|
538
|
+
animalBirthDate
|
|
440
539
|
});
|
|
441
540
|
|
|
442
541
|
// src/fhir/us-core.ts
|
|
@@ -561,19 +660,44 @@ function defaultHost() {
|
|
|
561
660
|
if (typeof import.meta.resolve !== "function") return { require: __require, resolve: void 0 };
|
|
562
661
|
return { require: createRequire(import.meta.url), resolve: (s) => import.meta.resolve(s) };
|
|
563
662
|
}
|
|
663
|
+
function isDenoResolveRefusal(error) {
|
|
664
|
+
return error instanceof TypeError && !("code" in error) && error.message === "Invalid arguments";
|
|
665
|
+
}
|
|
564
666
|
function isRequireEsmRefusal(error) {
|
|
565
667
|
return error?.code === "ERR_REQUIRE_ESM";
|
|
566
668
|
}
|
|
669
|
+
function peerPackage(specifier) {
|
|
670
|
+
const name2 = specifier.split("/").slice(0, 2).join("/");
|
|
671
|
+
return Object.hasOwn(SYNTH_PEER_MESSAGES, name2) ? name2 : void 0;
|
|
672
|
+
}
|
|
673
|
+
function isPeerNotFound(error, specifier, peer) {
|
|
674
|
+
const { code, message } = error ?? {};
|
|
675
|
+
if (typeof message !== "string") return false;
|
|
676
|
+
if (code === "ERR_MODULE_NOT_FOUND") return message.startsWith(`Cannot find package '${peer}'`);
|
|
677
|
+
if (code === "MODULE_NOT_FOUND") return message.startsWith(`Cannot find module '${specifier}'`);
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
567
680
|
function loadPeer(specifier, host) {
|
|
681
|
+
try {
|
|
682
|
+
return loadFrom(specifier, host);
|
|
683
|
+
} catch (error) {
|
|
684
|
+
const peer = peerPackage(specifier);
|
|
685
|
+
if (peer === void 0 || !isPeerNotFound(error, specifier, peer)) throw error;
|
|
686
|
+
throw new SynthError(SYNTH_FATAL_CODES.SYNTH_PEER_NOT_INSTALLED, { peer, cause: error });
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function loadFrom(specifier, host) {
|
|
568
690
|
if (host.resolve === void 0) return host.require(specifier);
|
|
569
|
-
let
|
|
691
|
+
let url;
|
|
570
692
|
try {
|
|
571
|
-
|
|
572
|
-
} catch {
|
|
693
|
+
url = host.resolve(specifier);
|
|
694
|
+
} catch (error) {
|
|
695
|
+
if (!isDenoResolveRefusal(error)) throw error;
|
|
573
696
|
return host.require(specifier);
|
|
574
697
|
}
|
|
698
|
+
if (!url.startsWith("file:")) return host.require(specifier);
|
|
575
699
|
try {
|
|
576
|
-
return host.require(
|
|
700
|
+
return host.require(fileURLToPath(url));
|
|
577
701
|
} catch (error) {
|
|
578
702
|
if (!isRequireEsmRefusal(error)) throw error;
|
|
579
703
|
return host.require(specifier);
|
|
@@ -944,6 +1068,10 @@ function resolveMix(allowed, requested, fallback) {
|
|
|
944
1068
|
if (requested === void 0) return fallback;
|
|
945
1069
|
return requested.map((entry) => resolveKind(allowed, entry));
|
|
946
1070
|
}
|
|
1071
|
+
function requireSelection(selection) {
|
|
1072
|
+
if (selection.length === 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_EMPTY_POOL);
|
|
1073
|
+
return selection;
|
|
1074
|
+
}
|
|
947
1075
|
|
|
948
1076
|
// src/fhir/patient.ts
|
|
949
1077
|
var PATIENT_PROFILES = Object.freeze(["base", "us-core"]);
|
|
@@ -1689,10 +1817,10 @@ function generateKind(kind, seed) {
|
|
|
1689
1817
|
}
|
|
1690
1818
|
function fhirCorpus(options) {
|
|
1691
1819
|
const { seed, count = 1, verify = true } = options;
|
|
1692
|
-
const kinds = resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
1820
|
+
const kinds = requireSelection(resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX));
|
|
1693
1821
|
const seedStream = createRng(seed);
|
|
1694
1822
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
1695
|
-
const kind = kinds[i % kinds.length]
|
|
1823
|
+
const kind = kinds[i % kinds.length];
|
|
1696
1824
|
const rt = fhirCorpusArtifact(generateKind(kind, seedStream.nextUint32()), verify);
|
|
1697
1825
|
return { format: "fhir", kind, content: rt.content, warnings: rt.warnings };
|
|
1698
1826
|
});
|