@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
|
@@ -19,7 +19,9 @@ interface SynthProfileSpec {
|
|
|
19
19
|
/**
|
|
20
20
|
* The vendor quirk recipe names this profile requests. Validated against the target format's quirk
|
|
21
21
|
* registry when the profile drives a quirk corpus (an unsupported quirk is a fatal
|
|
22
|
-
* `SYNTH_UNSUPPORTED_QUIRK`, never a silent no-op).
|
|
22
|
+
* `SYNTH_UNSUPPORTED_QUIRK`, never a silent no-op). Leave it out to leave the quirk selection to
|
|
23
|
+
* the corpus (every quirk by default); an empty list selects nothing, and a quirk corpus refuses it
|
|
24
|
+
* with `SYNTH_EMPTY_POOL`.
|
|
23
25
|
*/
|
|
24
26
|
readonly quirks?: readonly string[];
|
|
25
27
|
}
|
|
@@ -31,8 +33,11 @@ interface SynthProfile {
|
|
|
31
33
|
readonly givenNames?: readonly string[];
|
|
32
34
|
/** The family-name pool this profile draws from. */
|
|
33
35
|
readonly familyNames?: readonly string[];
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
+
/**
|
|
37
|
+
* The requested quirk recipe names, or absent when the spec left `quirks` out. Absent is not an
|
|
38
|
+
* empty list: absent leaves the quirk selection to the corpus, an empty list selects nothing.
|
|
39
|
+
*/
|
|
40
|
+
readonly quirks?: readonly string[];
|
|
36
41
|
}
|
|
37
42
|
/**
|
|
38
43
|
* Define a reusable, frozen synthetic-fixture profile.
|
|
@@ -43,7 +48,12 @@ interface SynthProfile {
|
|
|
43
48
|
* @example
|
|
44
49
|
* ```ts
|
|
45
50
|
* import { defineSynthProfile } from "@cosyte/synth";
|
|
46
|
-
*
|
|
51
|
+
* // Name pools only: `quirks` is left out, so a quirk corpus given this profile applies every quirk.
|
|
52
|
+
* const acme = defineSynthProfile({
|
|
53
|
+
* name: "acme-hospital",
|
|
54
|
+
* givenNames: ["Testina"],
|
|
55
|
+
* familyNames: ["Testerson"],
|
|
56
|
+
* });
|
|
47
57
|
* ```
|
|
48
58
|
*/
|
|
49
59
|
declare function defineSynthProfile(spec: SynthProfileSpec): SynthProfile;
|
|
@@ -235,7 +245,8 @@ declare function assertIntendedWarnings(intendedWarnings: readonly string[], bar
|
|
|
235
245
|
* @param profile - The synth profile whose `quirks` to validate.
|
|
236
246
|
* @param registry - The format's quirk descriptors.
|
|
237
247
|
* @param format - The format being generated.
|
|
238
|
-
* @returns The validated quirk names
|
|
248
|
+
* @returns The validated quirk names: the profile's, in order, or every quirk in `registry` when the
|
|
249
|
+
* profile leaves `quirks` out. An empty `quirks` list comes back empty.
|
|
239
250
|
* @throws SynthError `SYNTH_UNSUPPORTED_QUIRK` for the first unsupported quirk.
|
|
240
251
|
* @example
|
|
241
252
|
* ```ts
|
|
@@ -19,7 +19,9 @@ interface SynthProfileSpec {
|
|
|
19
19
|
/**
|
|
20
20
|
* The vendor quirk recipe names this profile requests. Validated against the target format's quirk
|
|
21
21
|
* registry when the profile drives a quirk corpus (an unsupported quirk is a fatal
|
|
22
|
-
* `SYNTH_UNSUPPORTED_QUIRK`, never a silent no-op).
|
|
22
|
+
* `SYNTH_UNSUPPORTED_QUIRK`, never a silent no-op). Leave it out to leave the quirk selection to
|
|
23
|
+
* the corpus (every quirk by default); an empty list selects nothing, and a quirk corpus refuses it
|
|
24
|
+
* with `SYNTH_EMPTY_POOL`.
|
|
23
25
|
*/
|
|
24
26
|
readonly quirks?: readonly string[];
|
|
25
27
|
}
|
|
@@ -31,8 +33,11 @@ interface SynthProfile {
|
|
|
31
33
|
readonly givenNames?: readonly string[];
|
|
32
34
|
/** The family-name pool this profile draws from. */
|
|
33
35
|
readonly familyNames?: readonly string[];
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
+
/**
|
|
37
|
+
* The requested quirk recipe names, or absent when the spec left `quirks` out. Absent is not an
|
|
38
|
+
* empty list: absent leaves the quirk selection to the corpus, an empty list selects nothing.
|
|
39
|
+
*/
|
|
40
|
+
readonly quirks?: readonly string[];
|
|
36
41
|
}
|
|
37
42
|
/**
|
|
38
43
|
* Define a reusable, frozen synthetic-fixture profile.
|
|
@@ -43,7 +48,12 @@ interface SynthProfile {
|
|
|
43
48
|
* @example
|
|
44
49
|
* ```ts
|
|
45
50
|
* import { defineSynthProfile } from "@cosyte/synth";
|
|
46
|
-
*
|
|
51
|
+
* // Name pools only: `quirks` is left out, so a quirk corpus given this profile applies every quirk.
|
|
52
|
+
* const acme = defineSynthProfile({
|
|
53
|
+
* name: "acme-hospital",
|
|
54
|
+
* givenNames: ["Testina"],
|
|
55
|
+
* familyNames: ["Testerson"],
|
|
56
|
+
* });
|
|
47
57
|
* ```
|
|
48
58
|
*/
|
|
49
59
|
declare function defineSynthProfile(spec: SynthProfileSpec): SynthProfile;
|
|
@@ -235,7 +245,8 @@ declare function assertIntendedWarnings(intendedWarnings: readonly string[], bar
|
|
|
235
245
|
* @param profile - The synth profile whose `quirks` to validate.
|
|
236
246
|
* @param registry - The format's quirk descriptors.
|
|
237
247
|
* @param format - The format being generated.
|
|
238
|
-
* @returns The validated quirk names
|
|
248
|
+
* @returns The validated quirk names: the profile's, in order, or every quirk in `registry` when the
|
|
249
|
+
* profile leaves `quirks` out. An empty `quirks` list comes back empty.
|
|
239
250
|
* @throws SynthError `SYNTH_UNSUPPORTED_QUIRK` for the first unsupported quirk.
|
|
240
251
|
* @example
|
|
241
252
|
* ```ts
|
package/dist/x12/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
|
|
|
@@ -183,19 +221,44 @@ function defaultHost() {
|
|
|
183
221
|
if (typeof undefined !== "function") return { require: __require, resolve: void 0 };
|
|
184
222
|
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) };
|
|
185
223
|
}
|
|
224
|
+
function isDenoResolveRefusal(error) {
|
|
225
|
+
return error instanceof TypeError && !("code" in error) && error.message === "Invalid arguments";
|
|
226
|
+
}
|
|
186
227
|
function isRequireEsmRefusal(error) {
|
|
187
228
|
return error?.code === "ERR_REQUIRE_ESM";
|
|
188
229
|
}
|
|
230
|
+
function peerPackage(specifier) {
|
|
231
|
+
const name2 = specifier.split("/").slice(0, 2).join("/");
|
|
232
|
+
return Object.hasOwn(SYNTH_PEER_MESSAGES, name2) ? name2 : void 0;
|
|
233
|
+
}
|
|
234
|
+
function isPeerNotFound(error, specifier, peer) {
|
|
235
|
+
const { code, message } = error ?? {};
|
|
236
|
+
if (typeof message !== "string") return false;
|
|
237
|
+
if (code === "ERR_MODULE_NOT_FOUND") return message.startsWith(`Cannot find package '${peer}'`);
|
|
238
|
+
if (code === "MODULE_NOT_FOUND") return message.startsWith(`Cannot find module '${specifier}'`);
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
189
241
|
function loadPeer(specifier, host) {
|
|
242
|
+
try {
|
|
243
|
+
return loadFrom(specifier, host);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
const peer = peerPackage(specifier);
|
|
246
|
+
if (peer === void 0 || !isPeerNotFound(error, specifier, peer)) throw error;
|
|
247
|
+
throw new SynthError(SYNTH_FATAL_CODES.SYNTH_PEER_NOT_INSTALLED, { peer, cause: error });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function loadFrom(specifier, host) {
|
|
190
251
|
if (host.resolve === void 0) return host.require(specifier);
|
|
191
|
-
let
|
|
252
|
+
let url$1;
|
|
192
253
|
try {
|
|
193
|
-
|
|
194
|
-
} catch {
|
|
254
|
+
url$1 = host.resolve(specifier);
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (!isDenoResolveRefusal(error)) throw error;
|
|
195
257
|
return host.require(specifier);
|
|
196
258
|
}
|
|
259
|
+
if (!url$1.startsWith("file:")) return host.require(specifier);
|
|
197
260
|
try {
|
|
198
|
-
return host.require(
|
|
261
|
+
return host.require(url.fileURLToPath(url$1));
|
|
199
262
|
} catch (error) {
|
|
200
263
|
if (!isRequireEsmRefusal(error)) throw error;
|
|
201
264
|
return host.require(specifier);
|
|
@@ -261,7 +324,7 @@ function luhnMod10(digits) {
|
|
|
261
324
|
if (d < 0 || d > 9) continue;
|
|
262
325
|
if (double) {
|
|
263
326
|
d *= 2;
|
|
264
|
-
if (d
|
|
327
|
+
if (d >= 10) d -= 9;
|
|
265
328
|
}
|
|
266
329
|
sum += d;
|
|
267
330
|
double = !double;
|
|
@@ -358,7 +421,7 @@ var SYNTHETIC_CITY_NAMES = Object.freeze([
|
|
|
358
421
|
]);
|
|
359
422
|
|
|
360
423
|
// src/safe/providers.ts
|
|
361
|
-
function ssn(rng, block
|
|
424
|
+
function ssn(rng, block) {
|
|
362
425
|
if (block === "advertising") {
|
|
363
426
|
return `987-00-432${String(rng.int(0, 9))}`;
|
|
364
427
|
}
|
|
@@ -485,6 +548,63 @@ var US_STATES = Object.freeze([
|
|
|
485
548
|
"WY"
|
|
486
549
|
]);
|
|
487
550
|
|
|
551
|
+
// src/safe/animal-code.ts
|
|
552
|
+
var ANIMAL_CODE_SYSTEM = "99COSYTE";
|
|
553
|
+
function animalCode(value, text) {
|
|
554
|
+
return Object.freeze({ code: value, text, system: ANIMAL_CODE_SYSTEM });
|
|
555
|
+
}
|
|
556
|
+
function animalSpecies(value, text, breeds) {
|
|
557
|
+
return Object.freeze({ ...animalCode(value, text), breeds: Object.freeze([...breeds]) });
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// src/safe/animal.ts
|
|
561
|
+
var SYNTHETIC_ANIMAL_SPECIES = Object.freeze([
|
|
562
|
+
animalSpecies("DOG", "Dog", [
|
|
563
|
+
animalCode("DOG-LAB", "Labrador Retriever"),
|
|
564
|
+
animalCode("DOG-BGL", "Beagle"),
|
|
565
|
+
animalCode("DOG-MIX", "Mixed breed dog")
|
|
566
|
+
]),
|
|
567
|
+
animalSpecies("CAT", "Cat", [animalCode("CAT-DSH", "Domestic Shorthair"), animalCode("CAT-SIA", "Siamese")]),
|
|
568
|
+
animalSpecies("HORSE", "Horse", [animalCode("HRS-QTR", "Quarter Horse"), animalCode("HRS-TB", "Thoroughbred")]),
|
|
569
|
+
animalSpecies("CATTLE", "Cattle", [animalCode("CTL-HOL", "Holstein"), animalCode("CTL-ANG", "Angus")])
|
|
570
|
+
]);
|
|
571
|
+
var SYNTHETIC_PET_NAMES = Object.freeze([
|
|
572
|
+
"Fixturepaw",
|
|
573
|
+
"Mockwhisker",
|
|
574
|
+
"Stubtail",
|
|
575
|
+
"Seedpup",
|
|
576
|
+
"Testbiscuit",
|
|
577
|
+
"Placepurr",
|
|
578
|
+
"Synthfur",
|
|
579
|
+
"Dummymane",
|
|
580
|
+
"Examplehoof",
|
|
581
|
+
"Fauxfluff",
|
|
582
|
+
"Samplesnout",
|
|
583
|
+
"Voidwag"
|
|
584
|
+
]);
|
|
585
|
+
var MICROCHIP_TEST_PREFIX = "999";
|
|
586
|
+
function petName(rng) {
|
|
587
|
+
return rng.pick(SYNTHETIC_PET_NAMES);
|
|
588
|
+
}
|
|
589
|
+
function animalBreed(rng) {
|
|
590
|
+
const picked = rng.pick(SYNTHETIC_ANIMAL_SPECIES);
|
|
591
|
+
const { breeds, ...bare } = picked;
|
|
592
|
+
return { species: Object.freeze(bare), breed: rng.pick(breeds) };
|
|
593
|
+
}
|
|
594
|
+
function microchip(rng) {
|
|
595
|
+
return `${MICROCHIP_TEST_PREFIX}${rng.digits(12)}`;
|
|
596
|
+
}
|
|
597
|
+
function animalBirthDate(rng, onDate) {
|
|
598
|
+
const year = Number(onDate.slice(0, 4));
|
|
599
|
+
const month = Number(onDate.slice(4, 6)) - 1;
|
|
600
|
+
const day = Number(onDate.slice(6, 8));
|
|
601
|
+
const latest = Date.UTC(year, month, day);
|
|
602
|
+
const earliest = Date.UTC(year - 20, month, day);
|
|
603
|
+
const span = Math.round((latest - earliest) / 864e5);
|
|
604
|
+
const born = new Date(latest - rng.int(0, span) * 864e5);
|
|
605
|
+
return born.toISOString().slice(0, 10).replace(/-/g, "");
|
|
606
|
+
}
|
|
607
|
+
|
|
488
608
|
// src/safe/index.ts
|
|
489
609
|
var safe = Object.freeze({
|
|
490
610
|
ssn,
|
|
@@ -498,7 +618,11 @@ var safe = Object.freeze({
|
|
|
498
618
|
address,
|
|
499
619
|
dateYmd,
|
|
500
620
|
npi,
|
|
501
|
-
dea
|
|
621
|
+
dea,
|
|
622
|
+
petName,
|
|
623
|
+
animalBreed,
|
|
624
|
+
microchip,
|
|
625
|
+
animalBirthDate
|
|
502
626
|
});
|
|
503
627
|
|
|
504
628
|
// src/x12/identity.ts
|
|
@@ -620,6 +744,10 @@ function resolveMix(allowed, requested, fallback) {
|
|
|
620
744
|
if (requested === void 0) return fallback;
|
|
621
745
|
return requested.map((entry) => resolveKind(allowed, entry));
|
|
622
746
|
}
|
|
747
|
+
function requireSelection(selection) {
|
|
748
|
+
if (selection.length === 0) throw new SynthError(SYNTH_FATAL_CODES.SYNTH_EMPTY_POOL);
|
|
749
|
+
return selection;
|
|
750
|
+
}
|
|
623
751
|
|
|
624
752
|
// src/x12/claim-837.ts
|
|
625
753
|
var CLAIM_837_VARIANTS = Object.freeze(["P", "I", "D"]);
|
|
@@ -1056,11 +1184,11 @@ function generateKind(kind, seed) {
|
|
|
1056
1184
|
}
|
|
1057
1185
|
function x12Corpus(options) {
|
|
1058
1186
|
const { seed, verify = true } = options;
|
|
1059
|
-
const mix = resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX);
|
|
1187
|
+
const mix = requireSelection(resolveMix(ALL_KINDS, options.mix, DEFAULT_MIX));
|
|
1060
1188
|
const count = options.count ?? mix.length;
|
|
1061
1189
|
const seedStream = createRng(seed);
|
|
1062
1190
|
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
1063
|
-
const kind = mix[i % mix.length]
|
|
1191
|
+
const kind = mix[i % mix.length];
|
|
1064
1192
|
const txSeed = seedStream.nextUint32();
|
|
1065
1193
|
const rt = x12CorpusArtifact(generateKind(kind, txSeed), verify);
|
|
1066
1194
|
return {
|