@cosyte/synth 0.0.1
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 +414 -0
- package/LICENSE +21 -0
- package/README.md +325 -0
- package/dist/astm/index.cjs +847 -0
- package/dist/astm/index.cjs.map +1 -0
- package/dist/astm/index.d.cts +418 -0
- package/dist/astm/index.d.ts +418 -0
- package/dist/astm/index.mjs +828 -0
- package/dist/astm/index.mjs.map +1 -0
- package/dist/ccda/index.cjs +1103 -0
- package/dist/ccda/index.cjs.map +1 -0
- package/dist/ccda/index.d.cts +380 -0
- package/dist/ccda/index.d.ts +380 -0
- package/dist/ccda/index.mjs +1077 -0
- package/dist/ccda/index.mjs.map +1 -0
- package/dist/deid/index.cjs +2809 -0
- package/dist/deid/index.cjs.map +1 -0
- package/dist/deid/index.d.cts +464 -0
- package/dist/deid/index.d.ts +464 -0
- package/dist/deid/index.mjs +2793 -0
- package/dist/deid/index.mjs.map +1 -0
- package/dist/example-codes-DeXcnCSK.d.cts +105 -0
- package/dist/example-codes-DeXcnCSK.d.ts +105 -0
- package/dist/fhir/index.cjs +1429 -0
- package/dist/fhir/index.cjs.map +1 -0
- package/dist/fhir/index.d.cts +772 -0
- package/dist/fhir/index.d.ts +772 -0
- package/dist/fhir/index.mjs +1384 -0
- package/dist/fhir/index.mjs.map +1 -0
- package/dist/hl7/index.cjs +1012 -0
- package/dist/hl7/index.cjs.map +1 -0
- package/dist/hl7/index.d.cts +548 -0
- package/dist/hl7/index.d.ts +548 -0
- package/dist/hl7/index.mjs +990 -0
- package/dist/hl7/index.mjs.map +1 -0
- package/dist/index.cjs +535 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +407 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.mjs +488 -0
- package/dist/index.mjs.map +1 -0
- package/dist/ncpdp/index.cjs +714 -0
- package/dist/ncpdp/index.cjs.map +1 -0
- package/dist/ncpdp/index.d.cts +432 -0
- package/dist/ncpdp/index.d.ts +432 -0
- package/dist/ncpdp/index.mjs +695 -0
- package/dist/ncpdp/index.mjs.map +1 -0
- package/dist/providers-OLz3zAc-.d.cts +343 -0
- package/dist/providers-OLz3zAc-.d.ts +343 -0
- package/dist/quirk-DmkgoZdh.d.cts +239 -0
- package/dist/quirk-JLyO1Ncj.d.ts +239 -0
- package/dist/x12/index.cjs +920 -0
- package/dist/x12/index.cjs.map +1 -0
- package/dist/x12/index.d.cts +484 -0
- package/dist/x12/index.d.ts +484 -0
- package/dist/x12/index.mjs +892 -0
- package/dist/x12/index.mjs.map +1 -0
- package/package.json +210 -0
|
@@ -0,0 +1,695 @@
|
|
|
1
|
+
import { buildNewRx, serializeScript, ScriptMessage, parseScript } from '@cosyte/ncpdp/script';
|
|
2
|
+
import { decimalValue, codedValue } from '@cosyte/ncpdp/common';
|
|
3
|
+
import { serializeTelecom, buildTelecomRequest, parseTelecom } from '@cosyte/ncpdp/telecom';
|
|
4
|
+
|
|
5
|
+
// src/rng/splitmix32.ts
|
|
6
|
+
function splitmix32(seed) {
|
|
7
|
+
let a = seed | 0;
|
|
8
|
+
return function next() {
|
|
9
|
+
a = a + 2654435769 | 0;
|
|
10
|
+
let t = a ^ a >>> 16;
|
|
11
|
+
t = Math.imul(t, 569420461);
|
|
12
|
+
t = t ^ t >>> 15;
|
|
13
|
+
t = Math.imul(t, 1935289751);
|
|
14
|
+
t = t ^ t >>> 15;
|
|
15
|
+
return t >>> 0;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/rng/sfc32.ts
|
|
20
|
+
function sfc32Next(s) {
|
|
21
|
+
s.a |= 0;
|
|
22
|
+
s.b |= 0;
|
|
23
|
+
s.c |= 0;
|
|
24
|
+
s.d |= 0;
|
|
25
|
+
const t = (s.a + s.b | 0) + s.d | 0;
|
|
26
|
+
s.d = s.d + 1 | 0;
|
|
27
|
+
s.a = s.b ^ s.b >>> 9;
|
|
28
|
+
s.b = s.c + (s.c << 3) | 0;
|
|
29
|
+
s.c = s.c << 21 | s.c >>> 11;
|
|
30
|
+
s.c = s.c + t | 0;
|
|
31
|
+
return t >>> 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/rng/rng.ts
|
|
35
|
+
var Sfc32Rng = class {
|
|
36
|
+
seed;
|
|
37
|
+
#state;
|
|
38
|
+
constructor(seed) {
|
|
39
|
+
this.seed = seed | 0;
|
|
40
|
+
const mix = splitmix32(this.seed);
|
|
41
|
+
this.#state = { a: mix(), b: mix(), c: mix(), d: mix() };
|
|
42
|
+
for (let i = 0; i < 8; i += 1) sfc32Next(this.#state);
|
|
43
|
+
}
|
|
44
|
+
nextUint32() {
|
|
45
|
+
return sfc32Next(this.#state);
|
|
46
|
+
}
|
|
47
|
+
float() {
|
|
48
|
+
return this.nextUint32() / 4294967296;
|
|
49
|
+
}
|
|
50
|
+
int(min, max) {
|
|
51
|
+
if (max < min) throw new RangeError(`Rng.int: max (${String(max)}) < min (${String(min)})`);
|
|
52
|
+
const span = max - min + 1;
|
|
53
|
+
return min + Math.floor(this.float() * span);
|
|
54
|
+
}
|
|
55
|
+
bool(p = 0.5) {
|
|
56
|
+
return this.float() < p;
|
|
57
|
+
}
|
|
58
|
+
pick(items) {
|
|
59
|
+
if (items.length === 0) throw new RangeError("Rng.pick: empty array");
|
|
60
|
+
return items[this.int(0, items.length - 1)];
|
|
61
|
+
}
|
|
62
|
+
digits(n) {
|
|
63
|
+
let out = "";
|
|
64
|
+
for (let i = 0; i < n; i += 1) out += String(this.int(0, 9));
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
function createRng(seed) {
|
|
69
|
+
return new Sfc32Rng(seed);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/corpus.ts
|
|
73
|
+
function makeCorpus(seed, artifacts, quirks = []) {
|
|
74
|
+
const counts = {};
|
|
75
|
+
const formats = /* @__PURE__ */ new Set();
|
|
76
|
+
const frozenArtifacts = artifacts.map((a) => {
|
|
77
|
+
counts[a.kind] = (counts[a.kind] ?? 0) + 1;
|
|
78
|
+
formats.add(a.format);
|
|
79
|
+
return Object.freeze({ ...a, warnings: Object.freeze([...a.warnings]) });
|
|
80
|
+
});
|
|
81
|
+
const manifest = Object.freeze({
|
|
82
|
+
formats: Object.freeze([...formats]),
|
|
83
|
+
counts: Object.freeze(counts),
|
|
84
|
+
quirks: Object.freeze([...quirks])
|
|
85
|
+
});
|
|
86
|
+
return Object.freeze({
|
|
87
|
+
seed,
|
|
88
|
+
manifest,
|
|
89
|
+
artifacts: Object.freeze(frozenArtifacts)
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/safe/reserved.ts
|
|
94
|
+
var SYNTHETIC_ASSIGNING_AUTHORITY = Object.freeze({
|
|
95
|
+
/** The human-readable assigning-authority namespace id (HL7 HD.1). */
|
|
96
|
+
namespaceId: "COSYTE-SYNTH",
|
|
97
|
+
/** The universal id — an OID under HL7's example arc `2.16.840.1.113883.19` (HD.2). */
|
|
98
|
+
universalId: "2.16.840.1.113883.19.999",
|
|
99
|
+
/** The universal id type (HD.3). */
|
|
100
|
+
universalIdType: "ISO"
|
|
101
|
+
});
|
|
102
|
+
var RESERVED_EMAIL_DOMAINS = Object.freeze([
|
|
103
|
+
"example.com",
|
|
104
|
+
"example.org",
|
|
105
|
+
"example.net"
|
|
106
|
+
]);
|
|
107
|
+
var TEST_NET_V4_PREFIXES = Object.freeze([
|
|
108
|
+
"192.0.2",
|
|
109
|
+
// TEST-NET-1
|
|
110
|
+
"198.51.100",
|
|
111
|
+
// TEST-NET-2
|
|
112
|
+
"203.0.113"
|
|
113
|
+
// TEST-NET-3
|
|
114
|
+
]);
|
|
115
|
+
var DOC_V6_PREFIX = "2001:db8";
|
|
116
|
+
var NPI_LUHN_PREFIX = "80840";
|
|
117
|
+
function luhnMod10(digits) {
|
|
118
|
+
let sum = 0;
|
|
119
|
+
let double = false;
|
|
120
|
+
for (let i = digits.length - 1; i >= 0; i -= 1) {
|
|
121
|
+
let d = digits.charCodeAt(i) - 48;
|
|
122
|
+
if (d < 0 || d > 9) continue;
|
|
123
|
+
if (double) {
|
|
124
|
+
d *= 2;
|
|
125
|
+
if (d > 9) d -= 9;
|
|
126
|
+
}
|
|
127
|
+
sum += d;
|
|
128
|
+
double = !double;
|
|
129
|
+
}
|
|
130
|
+
return sum % 10;
|
|
131
|
+
}
|
|
132
|
+
function npiCheckDigit(base9) {
|
|
133
|
+
const partial = luhnMod10(`${NPI_LUHN_PREFIX}${base9}0`);
|
|
134
|
+
return (10 - partial) % 10;
|
|
135
|
+
}
|
|
136
|
+
var DEA_REGISTRANT_TYPES = Object.freeze([
|
|
137
|
+
"A",
|
|
138
|
+
"B",
|
|
139
|
+
"F",
|
|
140
|
+
"G",
|
|
141
|
+
"M",
|
|
142
|
+
"P",
|
|
143
|
+
"R",
|
|
144
|
+
"X"
|
|
145
|
+
]);
|
|
146
|
+
function deaCheckDigit(base6) {
|
|
147
|
+
let odd = 0;
|
|
148
|
+
let even = 0;
|
|
149
|
+
for (let i = 0; i < 6; i += 1) {
|
|
150
|
+
const digit = base6.charCodeAt(i) - 48;
|
|
151
|
+
if (i % 2 === 0) odd += digit;
|
|
152
|
+
else even += digit;
|
|
153
|
+
}
|
|
154
|
+
return (odd + 2 * even) % 10;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/safe/names-pool.ts
|
|
158
|
+
var SYNTHETIC_GIVEN_NAMES = Object.freeze([
|
|
159
|
+
"Testina",
|
|
160
|
+
"Fixtura",
|
|
161
|
+
"Synthos",
|
|
162
|
+
"Placeholda",
|
|
163
|
+
"Sampleton",
|
|
164
|
+
"Prototius",
|
|
165
|
+
"Stubbina",
|
|
166
|
+
"Exampla",
|
|
167
|
+
"Quilliam",
|
|
168
|
+
"Fabrica",
|
|
169
|
+
"Simula",
|
|
170
|
+
"Testry",
|
|
171
|
+
"Seedwin",
|
|
172
|
+
"Corpora",
|
|
173
|
+
"Reprodo",
|
|
174
|
+
"Mocktavia",
|
|
175
|
+
"Dummett",
|
|
176
|
+
"Voidwin",
|
|
177
|
+
"Deteria",
|
|
178
|
+
"Randomir"
|
|
179
|
+
]);
|
|
180
|
+
var SYNTHETIC_FAMILY_NAMES = Object.freeze([
|
|
181
|
+
"Testerson",
|
|
182
|
+
"Fauxman",
|
|
183
|
+
"Placeholt",
|
|
184
|
+
"Mockridge",
|
|
185
|
+
"Fixtingham",
|
|
186
|
+
"Synthwell",
|
|
187
|
+
"Dummerton",
|
|
188
|
+
"Examplewood",
|
|
189
|
+
"Fabricant",
|
|
190
|
+
"Simulacre",
|
|
191
|
+
"Nonesuch",
|
|
192
|
+
"Seedman",
|
|
193
|
+
"Corpusworth",
|
|
194
|
+
"Reprodus",
|
|
195
|
+
"Voidmark",
|
|
196
|
+
"Deterwood",
|
|
197
|
+
"Randomson",
|
|
198
|
+
"Quillfeather",
|
|
199
|
+
"Notreal",
|
|
200
|
+
"Genfield"
|
|
201
|
+
]);
|
|
202
|
+
var SYNTHETIC_STREET_NAMES = Object.freeze([
|
|
203
|
+
"Fixture Lane",
|
|
204
|
+
"Sample Street",
|
|
205
|
+
"Placeholder Avenue",
|
|
206
|
+
"Synthetic Way",
|
|
207
|
+
"Example Boulevard",
|
|
208
|
+
"Testing Terrace",
|
|
209
|
+
"Mock Road",
|
|
210
|
+
"Prototype Court"
|
|
211
|
+
]);
|
|
212
|
+
var SYNTHETIC_CITY_NAMES = Object.freeze([
|
|
213
|
+
"Faketon",
|
|
214
|
+
"Synthville",
|
|
215
|
+
"Exampleburg",
|
|
216
|
+
"Testford",
|
|
217
|
+
"Mockhaven",
|
|
218
|
+
"Fixtureton"
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
// src/safe/providers.ts
|
|
222
|
+
function ssn(rng, block = "never-issued") {
|
|
223
|
+
if (block === "advertising") {
|
|
224
|
+
return `987-65-432${String(rng.int(0, 9))}`;
|
|
225
|
+
}
|
|
226
|
+
const area = rng.int(900, 999);
|
|
227
|
+
const group = rng.digits(2);
|
|
228
|
+
const serial = rng.digits(4);
|
|
229
|
+
return `${String(area)}-${group}-${serial}`;
|
|
230
|
+
}
|
|
231
|
+
function phone(rng) {
|
|
232
|
+
const area = `${String(rng.int(2, 9))}${rng.digits(2)}`;
|
|
233
|
+
const line = `01${rng.digits(2)}`;
|
|
234
|
+
return `(${area}) 555-${line}`;
|
|
235
|
+
}
|
|
236
|
+
function name(rng) {
|
|
237
|
+
return { given: rng.pick(SYNTHETIC_GIVEN_NAMES), family: rng.pick(SYNTHETIC_FAMILY_NAMES) };
|
|
238
|
+
}
|
|
239
|
+
function email(rng, person) {
|
|
240
|
+
const domain = rng.pick(RESERVED_EMAIL_DOMAINS);
|
|
241
|
+
const slug = person ? `${person.given}.${person.family}`.toLowerCase() : `synth${rng.digits(6)}`;
|
|
242
|
+
return `${slug}@${domain}`;
|
|
243
|
+
}
|
|
244
|
+
function ipv4(rng) {
|
|
245
|
+
return `${rng.pick(TEST_NET_V4_PREFIXES)}.${String(rng.int(1, 254))}`;
|
|
246
|
+
}
|
|
247
|
+
function ipv6(rng) {
|
|
248
|
+
const tail = rng.nextUint32().toString(16).padStart(4, "0").slice(-4);
|
|
249
|
+
return `${DOC_V6_PREFIX}::${tail}`;
|
|
250
|
+
}
|
|
251
|
+
function uuid(rng) {
|
|
252
|
+
const bytes = new Uint8Array(16);
|
|
253
|
+
for (let i = 0; i < 16; i += 1) bytes[i] = rng.int(0, 255);
|
|
254
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
255
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
256
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0"));
|
|
257
|
+
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`;
|
|
258
|
+
}
|
|
259
|
+
function npi(rng) {
|
|
260
|
+
const base9 = rng.digits(9);
|
|
261
|
+
const wrongCheck = (npiCheckDigit(base9) + 1) % 10;
|
|
262
|
+
return `${base9}${String(wrongCheck)}`;
|
|
263
|
+
}
|
|
264
|
+
function dea(rng, person) {
|
|
265
|
+
const type = rng.pick(DEA_REGISTRANT_TYPES);
|
|
266
|
+
const initialSource = person?.family ?? rng.pick(SYNTHETIC_FAMILY_NAMES);
|
|
267
|
+
const initial = initialSource.slice(0, 1).toUpperCase();
|
|
268
|
+
const base6 = rng.digits(6);
|
|
269
|
+
const wrongCheck = (deaCheckDigit(base6) + 1) % 10;
|
|
270
|
+
return `${type}${initial}${base6}${String(wrongCheck)}`;
|
|
271
|
+
}
|
|
272
|
+
function identifier(rng, typeCode = "MR") {
|
|
273
|
+
return {
|
|
274
|
+
value: rng.digits(8),
|
|
275
|
+
typeCode,
|
|
276
|
+
assigningAuthority: SYNTHETIC_ASSIGNING_AUTHORITY.namespaceId,
|
|
277
|
+
assigningAuthorityOid: SYNTHETIC_ASSIGNING_AUTHORITY.universalId
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
function address(rng) {
|
|
281
|
+
const number = rng.int(1, 9999);
|
|
282
|
+
return {
|
|
283
|
+
street: `${String(number)} ${rng.pick(SYNTHETIC_STREET_NAMES)}`,
|
|
284
|
+
city: rng.pick(SYNTHETIC_CITY_NAMES),
|
|
285
|
+
state: rng.pick(US_STATES),
|
|
286
|
+
zip: "00000"
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function dateYmd(rng, minYear = 1930, maxYear = 2010) {
|
|
290
|
+
const year = rng.int(minYear, maxYear);
|
|
291
|
+
const month = rng.int(1, 12);
|
|
292
|
+
const daysInMonth = new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
293
|
+
const day = rng.int(1, daysInMonth);
|
|
294
|
+
return `${String(year).padStart(4, "0")}${String(month).padStart(2, "0")}${String(day).padStart(2, "0")}`;
|
|
295
|
+
}
|
|
296
|
+
var US_STATES = Object.freeze([
|
|
297
|
+
"AL",
|
|
298
|
+
"AK",
|
|
299
|
+
"AZ",
|
|
300
|
+
"AR",
|
|
301
|
+
"CA",
|
|
302
|
+
"CO",
|
|
303
|
+
"CT",
|
|
304
|
+
"DE",
|
|
305
|
+
"FL",
|
|
306
|
+
"GA",
|
|
307
|
+
"HI",
|
|
308
|
+
"ID",
|
|
309
|
+
"IL",
|
|
310
|
+
"IN",
|
|
311
|
+
"IA",
|
|
312
|
+
"KS",
|
|
313
|
+
"KY",
|
|
314
|
+
"LA",
|
|
315
|
+
"ME",
|
|
316
|
+
"MD",
|
|
317
|
+
"MA",
|
|
318
|
+
"MI",
|
|
319
|
+
"MN",
|
|
320
|
+
"MS",
|
|
321
|
+
"MO",
|
|
322
|
+
"MT",
|
|
323
|
+
"NE",
|
|
324
|
+
"NV",
|
|
325
|
+
"NH",
|
|
326
|
+
"NJ",
|
|
327
|
+
"NM",
|
|
328
|
+
"NY",
|
|
329
|
+
"NC",
|
|
330
|
+
"ND",
|
|
331
|
+
"OH",
|
|
332
|
+
"OK",
|
|
333
|
+
"OR",
|
|
334
|
+
"PA",
|
|
335
|
+
"RI",
|
|
336
|
+
"SC",
|
|
337
|
+
"SD",
|
|
338
|
+
"TN",
|
|
339
|
+
"TX",
|
|
340
|
+
"UT",
|
|
341
|
+
"VT",
|
|
342
|
+
"VA",
|
|
343
|
+
"WA",
|
|
344
|
+
"WV",
|
|
345
|
+
"WI",
|
|
346
|
+
"WY"
|
|
347
|
+
]);
|
|
348
|
+
|
|
349
|
+
// src/safe/index.ts
|
|
350
|
+
var safe = Object.freeze({
|
|
351
|
+
ssn,
|
|
352
|
+
phone,
|
|
353
|
+
name,
|
|
354
|
+
email,
|
|
355
|
+
ipv4,
|
|
356
|
+
ipv6,
|
|
357
|
+
uuid,
|
|
358
|
+
identifier,
|
|
359
|
+
address,
|
|
360
|
+
dateYmd,
|
|
361
|
+
npi,
|
|
362
|
+
dea
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// src/ncpdp/identity.ts
|
|
366
|
+
var SYNTHETIC_PHARMACY_NAMES = Object.freeze([
|
|
367
|
+
"SYNTH COMMUNITY PHARMACY",
|
|
368
|
+
"FIXTURE DRUG MART",
|
|
369
|
+
"PLACEHOLDER RX SERVICES",
|
|
370
|
+
"SAMPLE APOTHECARY LLC",
|
|
371
|
+
"MOCK PHARMACY GROUP"
|
|
372
|
+
]);
|
|
373
|
+
function ncpdpPatient(rng) {
|
|
374
|
+
const person = safe.name(rng);
|
|
375
|
+
const dob = safe.dateYmd(rng, 1935, 2010);
|
|
376
|
+
const gender = rng.pick(["1", "2"]);
|
|
377
|
+
const patientId = `MBR${safe.identifier(rng, "MR").value}`;
|
|
378
|
+
const phone2 = safe.phone(rng);
|
|
379
|
+
const address2 = safe.address(rng);
|
|
380
|
+
return { person, dob, gender, patientId, phone: phone2, address: address2 };
|
|
381
|
+
}
|
|
382
|
+
function ncpdpPrescriber(rng) {
|
|
383
|
+
const person = safe.name(rng);
|
|
384
|
+
const npi2 = safe.npi(rng);
|
|
385
|
+
const dea2 = safe.dea(rng, person);
|
|
386
|
+
return { person, npi: npi2, dea: dea2 };
|
|
387
|
+
}
|
|
388
|
+
function ncpdpPharmacy(rng) {
|
|
389
|
+
const businessName = rng.pick(SYNTHETIC_PHARMACY_NAMES);
|
|
390
|
+
const npi2 = safe.npi(rng);
|
|
391
|
+
const ncpdpId = rng.digits(7);
|
|
392
|
+
return { businessName, npi: npi2, ncpdpId };
|
|
393
|
+
}
|
|
394
|
+
function ncpdpCardholder(rng) {
|
|
395
|
+
const person = safe.name(rng);
|
|
396
|
+
const cardholderId = `MBR${safe.identifier(rng, "MB").value}`;
|
|
397
|
+
const groupId = `GRP${rng.digits(5)}`;
|
|
398
|
+
const personCode = rng.pick(["01", "02", "03"]);
|
|
399
|
+
return { person, cardholderId, groupId, personCode };
|
|
400
|
+
}
|
|
401
|
+
function ncpdpScriptRouting(rng) {
|
|
402
|
+
const date = safe.dateYmd(rng, 2024, 2026);
|
|
403
|
+
const yyyy = date.slice(0, 4);
|
|
404
|
+
const mm = date.slice(4, 6);
|
|
405
|
+
const dd = date.slice(6, 8);
|
|
406
|
+
const hh = String(rng.int(0, 23)).padStart(2, "0");
|
|
407
|
+
const mi = String(rng.int(0, 59)).padStart(2, "0");
|
|
408
|
+
return {
|
|
409
|
+
messageId: `SYNTH-${rng.digits(10)}`,
|
|
410
|
+
sentTime: `${yyyy}-${mm}-${dd}T${hh}:${mi}:00Z`,
|
|
411
|
+
prescriberOrderNumber: `PON${rng.digits(8)}`,
|
|
412
|
+
date
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// src/ncpdp/example-codes.ts
|
|
417
|
+
var EXAMPLE_DRUGS = Object.freeze([
|
|
418
|
+
{ description: "Amoxicillin 500 MG Oral Capsule", ndc: "00000010101", form: "EA" },
|
|
419
|
+
{ description: "Lisinopril 10 MG Oral Tablet", ndc: "00000020202", form: "EA" },
|
|
420
|
+
{ description: "Atorvastatin 20 MG Oral Tablet", ndc: "00000030303", form: "EA" },
|
|
421
|
+
{ description: "Metformin 500 MG Oral Tablet", ndc: "00000040404", form: "EA" },
|
|
422
|
+
{ description: "Amlodipine 5 MG Oral Tablet", ndc: "00000050505", form: "EA" },
|
|
423
|
+
{ description: "Omeprazole 20 MG Delayed Release Capsule", ndc: "00000060606", form: "EA" },
|
|
424
|
+
{ description: "Levothyroxine 50 MCG Oral Tablet", ndc: "00000070707", form: "EA" },
|
|
425
|
+
{ description: "Azithromycin 250 MG Oral Tablet", ndc: "00000080808", form: "EA" },
|
|
426
|
+
{ description: "Sertraline 50 MG Oral Tablet", ndc: "00000090909", form: "EA" },
|
|
427
|
+
{ description: "Albuterol 90 MCG Metered Dose Inhaler", ndc: "00000101010", form: "EA" }
|
|
428
|
+
]);
|
|
429
|
+
var EXAMPLE_SIG_TEXT = Object.freeze([
|
|
430
|
+
"Take 1 tablet by mouth once daily",
|
|
431
|
+
"Take 1 capsule by mouth twice daily with food",
|
|
432
|
+
"Take 2 tablets by mouth every 12 hours as needed",
|
|
433
|
+
"Take 1 tablet by mouth at bedtime",
|
|
434
|
+
"Inhale 2 puffs by mouth every 4 to 6 hours as needed"
|
|
435
|
+
]);
|
|
436
|
+
var DAW_CODES = Object.freeze(["0", "1", "2"]);
|
|
437
|
+
|
|
438
|
+
// src/ncpdp/script.ts
|
|
439
|
+
var SCRIPT_VERSION = "2017071";
|
|
440
|
+
function patientModel(rng) {
|
|
441
|
+
const p = ncpdpPatient(rng);
|
|
442
|
+
return {
|
|
443
|
+
name: { lastName: p.person.family, firstName: p.person.given },
|
|
444
|
+
gender: p.gender,
|
|
445
|
+
dateOfBirth: p.dob
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function pharmacyModel(rng) {
|
|
449
|
+
const rx = ncpdpPharmacy(rng);
|
|
450
|
+
return {
|
|
451
|
+
businessName: rx.businessName,
|
|
452
|
+
identification: { npi: rx.npi, ncpdpId: rx.ncpdpId }
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function prescriberModel(rng) {
|
|
456
|
+
const dr = ncpdpPrescriber(rng);
|
|
457
|
+
return {
|
|
458
|
+
name: { lastName: dr.person.family, firstName: dr.person.given },
|
|
459
|
+
identification: { npi: dr.npi, deaNumber: dr.dea }
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function medicationModel(rng, routing) {
|
|
463
|
+
const drug = rng.pick(EXAMPLE_DRUGS);
|
|
464
|
+
const written = `${routing.date.slice(0, 4)}-${routing.date.slice(4, 6)}-${routing.date.slice(6, 8)}`;
|
|
465
|
+
return {
|
|
466
|
+
description: drug.description,
|
|
467
|
+
coded: { productCode: codedValue(drug.ndc, "ND") },
|
|
468
|
+
quantity: {
|
|
469
|
+
value: decimalValue(String(rng.int(30, 90))),
|
|
470
|
+
unitOfMeasure: drug.form,
|
|
471
|
+
codeListQualifier: "38"
|
|
472
|
+
},
|
|
473
|
+
daysSupply: decimalValue(String(rng.pick([30, 60, 90]))),
|
|
474
|
+
numberOfRefills: String(rng.int(0, 5)),
|
|
475
|
+
substitutions: rng.pick(["0", "1"]),
|
|
476
|
+
writtenDate: written,
|
|
477
|
+
sigText: rng.pick(EXAMPLE_SIG_TEXT)
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function headerModel(routing) {
|
|
481
|
+
return {
|
|
482
|
+
version: SCRIPT_VERSION,
|
|
483
|
+
from: "SYNTHPRESCRIBER",
|
|
484
|
+
to: "SYNTHPHARMACY",
|
|
485
|
+
messageId: routing.messageId,
|
|
486
|
+
sentTime: routing.sentTime,
|
|
487
|
+
prescriberOrderNumber: routing.prescriberOrderNumber
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function generateNewRx(options) {
|
|
491
|
+
const rng = createRng(options.seed);
|
|
492
|
+
const routing = ncpdpScriptRouting(rng);
|
|
493
|
+
const patient = patientModel(rng);
|
|
494
|
+
const pharmacy = pharmacyModel(rng);
|
|
495
|
+
const prescriber = prescriberModel(rng);
|
|
496
|
+
const medication = medicationModel(rng, routing);
|
|
497
|
+
const message = buildNewRx({
|
|
498
|
+
header: headerModel(routing),
|
|
499
|
+
patient,
|
|
500
|
+
pharmacy,
|
|
501
|
+
prescriber,
|
|
502
|
+
medication
|
|
503
|
+
});
|
|
504
|
+
return serializeScript(message);
|
|
505
|
+
}
|
|
506
|
+
function lifecycleFields(rng) {
|
|
507
|
+
const routing = ncpdpScriptRouting(rng);
|
|
508
|
+
return {
|
|
509
|
+
requestReferenceNumber: `REQ${rng.digits(9)}`,
|
|
510
|
+
patient: patientModel(rng),
|
|
511
|
+
pharmacy: pharmacyModel(rng),
|
|
512
|
+
prescriber: prescriberModel(rng),
|
|
513
|
+
medicationPrescribed: medicationModel(rng, routing),
|
|
514
|
+
header: headerModel(routing)
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
function generateRxRenewalRequest(options) {
|
|
518
|
+
const rng = createRng(options.seed);
|
|
519
|
+
const { header: header2, ...fields } = lifecycleFields(rng);
|
|
520
|
+
const body = { kind: "RxRenewalRequest", ...fields };
|
|
521
|
+
return serializeScript(new ScriptMessage({ header: header2, body, warnings: [] }));
|
|
522
|
+
}
|
|
523
|
+
function generateRxChangeRequest(options) {
|
|
524
|
+
const rng = createRng(options.seed);
|
|
525
|
+
const { header: header2, ...fields } = lifecycleFields(rng);
|
|
526
|
+
const body = { kind: "RxChangeRequest", ...fields };
|
|
527
|
+
return serializeScript(new ScriptMessage({ header: header2, body, warnings: [] }));
|
|
528
|
+
}
|
|
529
|
+
var SYNTHETIC_BIN = "999999";
|
|
530
|
+
function digitsOnly(value) {
|
|
531
|
+
return value.replace(/\D/g, "");
|
|
532
|
+
}
|
|
533
|
+
function header(rng, transactionCode, pharmacyNpi) {
|
|
534
|
+
const date = serviceDate(rng);
|
|
535
|
+
return {
|
|
536
|
+
binNumber: SYNTHETIC_BIN,
|
|
537
|
+
versionRelease: "D0",
|
|
538
|
+
transactionCode,
|
|
539
|
+
processorControlNumber: `SYN${rng.digits(4)}`,
|
|
540
|
+
transactionCount: "1",
|
|
541
|
+
serviceProviderIdQualifier: "01",
|
|
542
|
+
// 01 = NPI
|
|
543
|
+
serviceProviderId: pharmacyNpi,
|
|
544
|
+
dateOfService: date,
|
|
545
|
+
softwareCertificationId: `SW${rng.digits(6)}`
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function serviceDate(rng) {
|
|
549
|
+
const year = rng.int(2024, 2026);
|
|
550
|
+
const month = rng.int(1, 12);
|
|
551
|
+
const day = rng.int(1, 28);
|
|
552
|
+
return `${String(year)}${String(month).padStart(2, "0")}${String(day).padStart(2, "0")}`;
|
|
553
|
+
}
|
|
554
|
+
function field(id, value) {
|
|
555
|
+
return { id, value };
|
|
556
|
+
}
|
|
557
|
+
function patientSegment(rng) {
|
|
558
|
+
const p = ncpdpPatient(rng);
|
|
559
|
+
return {
|
|
560
|
+
segmentId: "01",
|
|
561
|
+
fields: [
|
|
562
|
+
field("C4", p.dob),
|
|
563
|
+
field("C5", p.gender),
|
|
564
|
+
field("CA", p.person.given),
|
|
565
|
+
field("CB", p.person.family),
|
|
566
|
+
field("CM", p.address.street),
|
|
567
|
+
field("CQ", digitsOnly(p.phone)),
|
|
568
|
+
field("CY", p.patientId)
|
|
569
|
+
]
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function insuranceSegment(rng) {
|
|
573
|
+
const c = ncpdpCardholder(rng);
|
|
574
|
+
return {
|
|
575
|
+
segmentId: "04",
|
|
576
|
+
fields: [
|
|
577
|
+
field("C2", c.cardholderId),
|
|
578
|
+
field("C1", c.groupId),
|
|
579
|
+
field("C3", c.personCode),
|
|
580
|
+
field("CC", c.person.given),
|
|
581
|
+
field("CD", c.person.family)
|
|
582
|
+
]
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function prescriberSegment(rng) {
|
|
586
|
+
const dr = ncpdpPrescriber(rng);
|
|
587
|
+
return {
|
|
588
|
+
segmentId: "03",
|
|
589
|
+
fields: [field("EZ", "01"), field("DB", dr.npi)]
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
function claimSegment(rng, minimal) {
|
|
593
|
+
const drug = rng.pick(EXAMPLE_DRUGS);
|
|
594
|
+
const rxRef = `RX${rng.digits(7)}`;
|
|
595
|
+
const fill = String(rng.int(0, 5));
|
|
596
|
+
if (minimal) {
|
|
597
|
+
return {
|
|
598
|
+
segmentId: "07",
|
|
599
|
+
fields: [field("EM", "1"), field("D2", rxRef), field("D3", fill)]
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
segmentId: "07",
|
|
604
|
+
fields: [
|
|
605
|
+
field("EM", "1"),
|
|
606
|
+
// 1 = Rx Billing
|
|
607
|
+
field("D2", rxRef),
|
|
608
|
+
field("D3", fill),
|
|
609
|
+
field("E1", "03"),
|
|
610
|
+
// 03 = NDC
|
|
611
|
+
field("D7", drug.ndc),
|
|
612
|
+
field("E7", String(rng.int(30, 90))),
|
|
613
|
+
field("D5", String(rng.pick([30, 60, 90]))),
|
|
614
|
+
field("D8", rng.pick(DAW_CODES))
|
|
615
|
+
]
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function generateTelecom(code, options) {
|
|
619
|
+
const rng = createRng(options.seed);
|
|
620
|
+
const pharmacy = ncpdpPharmacy(rng);
|
|
621
|
+
const head = header(rng, code, pharmacy.npi);
|
|
622
|
+
const segments = code === "B2" ? [insuranceSegment(rng), claimSegment(rng, true)] : [
|
|
623
|
+
patientSegment(rng),
|
|
624
|
+
insuranceSegment(rng),
|
|
625
|
+
prescriberSegment(rng),
|
|
626
|
+
claimSegment(rng, false)
|
|
627
|
+
];
|
|
628
|
+
return serializeTelecom(buildTelecomRequest({ header: head, segments }));
|
|
629
|
+
}
|
|
630
|
+
function generateB1(options) {
|
|
631
|
+
return generateTelecom("B1", options);
|
|
632
|
+
}
|
|
633
|
+
function generateB2(options) {
|
|
634
|
+
return generateTelecom("B2", options);
|
|
635
|
+
}
|
|
636
|
+
function generateB3(options) {
|
|
637
|
+
return generateTelecom("B3", options);
|
|
638
|
+
}
|
|
639
|
+
function scriptRoundTrip(xml) {
|
|
640
|
+
const message = parseScript(xml);
|
|
641
|
+
const warnings = message.warnings.map((w) => String(w.code));
|
|
642
|
+
const byteStable = serializeScript(message) === xml;
|
|
643
|
+
return { content: xml, warnings, byteStable, specClean: warnings.length === 0 && byteStable };
|
|
644
|
+
}
|
|
645
|
+
function telecomRoundTrip(wire) {
|
|
646
|
+
const transaction = parseTelecom(wire);
|
|
647
|
+
const warnings = transaction.warnings.map((w) => String(w.code));
|
|
648
|
+
const byteStable = serializeTelecom(transaction) === wire;
|
|
649
|
+
return { content: wire, warnings, byteStable, specClean: warnings.length === 0 && byteStable };
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/ncpdp/index.ts
|
|
653
|
+
var DEFAULT_MIX = Object.freeze([
|
|
654
|
+
"NewRx",
|
|
655
|
+
"RxRenewalRequest",
|
|
656
|
+
"RxChangeRequest",
|
|
657
|
+
"B1",
|
|
658
|
+
"B2",
|
|
659
|
+
"B3"
|
|
660
|
+
]);
|
|
661
|
+
function generateKind(kind, seed) {
|
|
662
|
+
switch (kind) {
|
|
663
|
+
case "NewRx":
|
|
664
|
+
return scriptRoundTrip(generateNewRx({ seed }));
|
|
665
|
+
case "RxRenewalRequest":
|
|
666
|
+
return scriptRoundTrip(generateRxRenewalRequest({ seed }));
|
|
667
|
+
case "RxChangeRequest":
|
|
668
|
+
return scriptRoundTrip(generateRxChangeRequest({ seed }));
|
|
669
|
+
case "B1":
|
|
670
|
+
case "B2":
|
|
671
|
+
case "B3":
|
|
672
|
+
return telecomRoundTrip(generateTelecom(kind, { seed }));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
function ncpdpCorpus(options) {
|
|
676
|
+
const { seed, mix = DEFAULT_MIX } = options;
|
|
677
|
+
const count = options.count ?? mix.length;
|
|
678
|
+
const seedStream = createRng(seed);
|
|
679
|
+
const artifacts = Array.from({ length: count }, (_unused, i) => {
|
|
680
|
+
const kind = mix[i % mix.length] ?? "NewRx";
|
|
681
|
+
const txSeed = seedStream.nextUint32();
|
|
682
|
+
const rt = generateKind(kind, txSeed);
|
|
683
|
+
return {
|
|
684
|
+
format: "ncpdp",
|
|
685
|
+
kind,
|
|
686
|
+
content: rt.content,
|
|
687
|
+
warnings: rt.warnings
|
|
688
|
+
};
|
|
689
|
+
});
|
|
690
|
+
return makeCorpus(seed, artifacts);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
export { DAW_CODES, EXAMPLE_DRUGS, EXAMPLE_SIG_TEXT, generateB1, generateB2, generateB3, generateNewRx, generateRxChangeRequest, generateRxRenewalRequest, generateTelecom, ncpdpCardholder, ncpdpCorpus, ncpdpPatient, ncpdpPharmacy, ncpdpPrescriber, ncpdpScriptRouting, scriptRoundTrip, telecomRoundTrip };
|
|
694
|
+
//# sourceMappingURL=index.mjs.map
|
|
695
|
+
//# sourceMappingURL=index.mjs.map
|